49 votes

Problèmes avec Singleton Pattern

J'ai lu sur le pattern Singleton pour les derniers jours. La perception générale est que les scénarios où il est nécessaire sont assez rares (si ce n'est rare), probablement parce qu'il a son propre ensemble de problèmes tels que

  • Dans un garbage collection environnement, il peut être un problème en ce qui concerne la gestion de la mémoire.
  • Dans un environnement multithread, il peut provoquer des goulots d'étranglement et d'introduire des problèmes de synchronisation.
  • Des maux de tête à partir d'essais prespective.

Je commence à avoir des idées derrière ces questions, mais pas totalement sûr au sujet de ces préoccupations. Comme dans le cas de la collecte des ordures problème, l'utilisation de static singleton dans la mise en œuvre (qui est inhérent au modèle), c'est que le souci? Comme cela signifierait que l'instance statique durera jusqu'à l'application. Est-ce quelque chose qui se dégrade de gestion de la mémoire (il signifie simplement que la mémoire allouée à un singleton ne sera pas libéré)?

Bien sûr, dans un multithread d'installation, d'avoir tous les threads dans le conflit pour l'instance du singleton serait un goulot d'étranglement. Mais comment l'utilisation de ce modèle provoque des problèmes de synchronisation (certes, nous pouvons utiliser un mutex ou quelque chose comme ça pour synchroniser l'accès).

À partir d'une (unité?)les tests de point de vue, depuis les singletons utiliser des méthodes statiques (qui sont difficiles à se moque de lui ou écrasé), ils peuvent causer des problèmes. Pas sûr à ce sujet. Quelqu'un peut-il donner des précisions sur ces tests concernent?

Merci.

41voto

carl Points 25879

Dans un garbage collection environnement, il peut être un problème en ce qui concerne la gestion de la mémoire

Dans typique singleton implémentations, une fois que vous créez le singleton, vous ne pourrez jamais détruire. Cette non-destructive de la nature est parfois acceptable avec le singleton est petit. Toutefois, si le singleton est énorme, alors vous inutilement utilise plus de mémoire que vous devez.

C'est un gros problème dans les langues où vous avez un garbage collector (comme Java, Python, etc) parce que le garbage collector sera toujours croire que le singleton est nécessaire. En C++, vous pouvez tricher en delete-ing le pointeur. Toutefois, cela permet d'ouvrir sa propre boîte de pandore parce que c'est censé être un singleton, mais en le supprimant, vous permettant de créer un second.

Dans la plupart des cas, cette sur-utilisation de la mémoire ne se dégrade pas les performances de la mémoire, mais il peut être considéré comme une fuite de mémoire. Avec un grand singleton, vous perdez la mémoire sur votre ordinateur de l'utilisateur ou de l'appareil. (Vous pouvez exécuter dans une fragmentation de la mémoire si vous allouer un énorme singleton, mais c'est généralement un non-problème).

Dans un environnement multithread, il peut provoquer des goulots d'étranglement et d'introduire des problèmes de synchronisation.

Si chaque thread est d'accéder au même objet et que vous utilisez un mutex, chaque thread doit attendre jusqu'à ce que l'autre a déverrouillé le singleton. Et si le fils dépendent fortement de la singleton, alors vous allez dégrader les performances d'un seul fil de l'environnement, car un thread passe le plus clair de sa vie à attendre.

Toutefois, si votre domaine d'application le permet, vous pouvez créer un objet pour chaque thread -- de cette façon, le fil ne passe pas de temps d'attente et au lieu de cela fait le travail.

Des maux de tête à partir d'essais prespective.

Notamment, un singleton constructeur ne peut être testé une fois. Vous devez créer une toute nouvelle suite de test afin de tester le constructeur. C'est très bien si votre constructeur ne prend aucun paramètre, mais une fois que vous acceptez un paremeter vous ne pouvez plus efficace de l'unité de teest.

En outre, vous ne pouvez pas écraser le singleton aussi efficacement et à votre utilisation des objets fantaisie devient difficile à utiliser (il y a des façons de contourner cela, mais c'est plus d'ennuis que cela vaut la peine). Continuez à lire pour en savoir plus sur cette...

(Et il conduit à une mauvaise conception, aussi!)

Les Singletons sont aussi un signe d'une mauvaise conception. Certains programmeurs veulent faire de leur base de données de la classe singleton. "Notre application sera jamais utiliser deux bases de données," ils pensent généralement. Mais, il viendra un moment où il peut faire sens pour l'utilisation de deux bases de données, ou de tests unitaires, vous souhaitez utiliser deux bases de données SQLite. Si vous avez utilisé un singleton, vous aurez à faire des changements importants à votre demande. Mais si vous avez utilisé des objets réguliers depuis le début, vous pouvez profiter de la programmation orientée objet pour obtenir votre tâche effectuée avec efficacité et dans le temps.

La plupart des cas de singleton sont le résultat de la programmeur d'être paresseux. Ils ne souhaitent pas transmettre autour d'un objet (par exemple, l'objet de base de données) à un ensemble de méthodes, de sorte qu'ils créer un singleton que chaque méthode utilise comme un paramètre implicite. Mais, cette approche piqûres pour les raisons ci-dessus.

Essayez de ne jamais utiliser un singleton, si vous le pouvez. Même si elles peuvent sembler une bonne approche dès le début, cela a toujours conduit à une mauvaise conception et difficiles à maintenir le code en bas de la ligne.

30voto

Greg Hewgill Points 356191

Si vous n'avez pas vu l'article, les singletons sont des menteurs pathologiques , vous devriez également le lire. Il explique comment les interconnexions entre singletons sont masquées de l'interface, de sorte que la façon dont vous devez construire un logiciel est également masquée de l'interface.

Il existe des liens vers quelques autres articles sur des singletons du même auteur.

22voto

Andrew Shepherd Points 16670

Lors de l'évaluation d'un Singleton, vous devez vous demander: "Quelle est l'alternative? Aurait les mêmes problèmes se passerait si je n'ai pas utilisé le pattern Singleton?"

La plupart des systèmes ont un certain besoin de Gros Objets Globaux. Ce sont des éléments qui sont volumineux et coûteux (par exemple, Base de données des Gestionnaires de Connexion), ou de détenir des omniprésente de l'état de l'information (par exemple, le verrouillage de l'information).

L'alternative à un Singleton est d'avoir ce Gros Objet Global créé au démarrage, et transmis en tant que paramètre pour toutes les classes ou méthodes qui ont besoin d'accéder à cet objet.

Aurait les mêmes problèmes se produisent dans le non-singleton cas? Nous allons les examiner un par un:

  • Gestion de la mémoire: Le Grand Objet Global pourrait exister lorsque l'application a été commencé, et l'objet existera jusqu'à l'arrêt. Comme il y a un seul objet, il prendra exactement la même quantité de mémoire que le singleton cas. L'utilisation de la mémoire n'est pas un problème. (@MadKeithV: à l'Ordre de la destruction lors de l'arrêt est un autre problème).

  • Le Multithreading et les goulets d'étranglement: Tous les fils aurait besoin d'accéder au même objet, qu'ils ont été transmis cet objet en tant que paramètre ou si ils ont appelé MyBigGlobalObject.GetInstance(). Donc, Singleton ou non, vous toujours la même synchronisation des questions (qui, heureusement, ont des solutions standard). Ce n'est pas un problème non plus.

  • Tests unitaires: Si vous n'utilisez pas un Singleton, alors vous pouvez créer le Grand Objet Global au début de chaque test, et le garbage collector, c'est à emporter lorsque le test est terminé. Chaque test démarre avec une nouvelle, propre environnement unnaffected par le test précédent. Sinon, dans le Singleton cas, le seul objet de vies à travers TOUTES les épreuves, et peut facilement devenir "contaminés". Alors oui, le pattern Singleton vraiment mord quand il s'agit de tests unitaires.

Ma préférence: en raison de l'unité de test, seul, j'ai tendance à éviter le pattern Singleton. Si c'est l'un des rares milieux où je n'ai pas de tests unitaires (par exemple, la couche d'interface utilisateur), alors je pourrait utiliser les Singletons, sinon je les éviter.

8voto

jalf Points 142628

Mon principal argument contre les singletons est en gros qu'ils combinent deux mauvaises propriétés.

Les choses que vous mentionnez peut être un problème, bien sûr, mais ils ne pas avoir à l'être. La synchronisation chose peut être fixe, il ne devient un goulot d'étranglement si le nombre de threads accèdent fréquemment à l'singleton, et ainsi de suite. Ces questions sont ennuyeux, mais pas deal breakers.

Le problème beaucoup plus fondamental avec les singletons, c'est que ce qu'ils essaient de faire est fondamentalement mauvais.

Un singleton, tel que défini par le GoF, a deux propriétés:

  • Il est accessible dans le monde entier, et
  • Il empêche la classe de jamais être instanciée qu'une seule fois.

Le premier devrait être simple. Globales sont, généralement parlant, mauvais. Si vous ne voulez pas globale, alors vous ne voulez pas un singleton.

La deuxième question est moins évidente, mais fondamentalement, il tente de résoudre un problème inexistant.

Quand était la dernière fois que vous avez accidentellement instancié de la classe, où vous plutôt destinées à la réutilisation d'une instance existante?

Quand était la dernière fois que vous avez accidentellement tapé "std::ostream() << "hello world << std::endl", lorsque vous signifiait "std::cout << "hello world << std::endl"?

Il n'a tout simplement pas se produire. Nous n'avons donc pas besoin de prévenir ce problème en premier lieu.

Mais plus important encore, le pressentiment que "seule une instance doit exister" est presque toujours tort. Ce que nous avons l'habitude de dire est "je ne peut actuellement voir une utilisation pour un exemple".

mais "je ne peux voir que l'utilisation d'une instance" n'est pas la même chose que "l'application s'écrouler si quelqu'un ose la création de deux instances".

Dans ce dernier cas, un singleton peut être justifiée. mais dans le premier cas, c'est vraiment un prématuré est un choix de conception.

Habituellement, nous ne nous envie plus d'une instance.

Vous avez souvent besoin de plus d'un enregistreur. Il y a le journal que vous écrivez propre, des messages structurés, pour le client, de surveiller, et il y a celui de vous faire un dump de débogage de données pour votre propre usage.

Il est également facile d'imaginer que vous pourriez finir vers le haut à l'aide de plus d'une base de données.

Ou de paramètres de programme. Bien sûr, un seul ensemble de paramètres peut être active à la fois. Mais, alors qu'ils sont actifs, l'utilisateur peut entrer dans la "boîte de dialogue" options et configurer un deuxième ensemble de paramètres. Il n'a pas appliqué encore, mais une fois qu'il touche "ok", ils doivent être échangés à l'intérieur et remplacer le jeu actif en cours. Et cela signifie que jusqu'à ce qu'il le hit "ok", deux séries d'options existent réellement.

Et plus généralement, les tests unitaires:

L'une des règles fondamentales de tests unitaires, c'est qu'ils doivent être exécutés dans l'isolement. Chaque essai doit configurer l'environnement à partir de zéro, exécutez le test, et à la déchirure tout en bas. Ce qui signifie que chaque test sera à vouloir créer un nouvel objet singleton, exécutez le test contre elle, et le fermer.

Ce qui n'est évidemment pas possible, en raison d'un singleton est créé une fois et une seule fois. Il ne peut pas être supprimé. De nouvelles instances ne peut pas être créé.

Donc, finalement, le problème avec les singletons n'est pas les subtilités, comme "il est difficile d'obtenir le thread de sécurité correct", mais beaucoup plus fondamentale "ils n'ont pas réellement contribuer à quelque chose de positif à votre code. Ils ajoutent deux traits, chacun d'eux négatif, à votre base de code. Qui voudrait jamais que c'est?"

3voto

Rafał Dowgird Points 16600

À propos de cette unité de test de préoccupation. Les principaux problèmes semble être pas à tester les singletons eux-mêmes, mais à tester les objets que l'utilisation .

De tels objets ne peuvent pas être isolés pour les tests, car ils ont des dépendances sur les singletons qui sont à la fois caché et difficile à enlever. C'est encore pire si le singleton représente une interface à un système externe (DB connexion, de paiement, tirs de l'unité). Test un tel objet peut écrire de façon inattendue en DB, envoyer de l'argent, qui sait où, ou même tirer quelques missiles intercontinentaux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X