93 votes

Injection de dépendances et modèle de conception Singleton

Comment identifier quand utiliser l'injection de dépendance ou le modèle singleton. J'ai lu sur beaucoup de sites web qu'ils disaient "Utiliser l'injection de dépendance plutôt que le modèle singleton". Mais je ne suis pas sûr d'être totalement d'accord avec eux. Pour mes projets de petite ou moyenne envergure, je vois définitivement l'utilisation du modèle singleton de manière directe.

Par exemple Logger. Je pourrais utiliser Logger.GetInstance().Log(...) Mais, au lieu de cela, pourquoi ai-je besoin d'injecter chaque classe que je crée, avec l'instance du logger ?

91voto

Bryan Watts Points 22810

Les singletons, c'est comme le communisme : ils ont l'air bien sur le papier, mais explosent de problèmes dans la pratique.

Le modèle singleton accorde une importance disproportionnée à la facilité d'accès aux objets. Il ignore complètement le contexte en exigeant que chaque consommateur utilise un objet adapté à l'AppDomain, ne laissant aucune option pour des implémentations différentes. Il intègre la connaissance de l'infrastructure dans vos classes (l'appel à la fonction GetInstance() ) tout en ajoutant exactement zéro la puissance expressive. En fait, cela diminue votre pouvoir d'expression, car vous ne pouvez pas changer l'implémentation utilisée par une classe sans la changer pour les autres classes. tous d'entre eux. Vous ne pouvez tout simplement pas ajouter des éléments de fonctionnalité ponctuels.

De plus, lorsque la classe Foo dépend de Logger.GetInstance() , Foo cache effectivement ses dépendances aux consommateurs. Cela signifie que vous ne pouvez pas comprendre pleinement Foo ou l'utiliser en toute confiance à moins de lire sa source et de découvrir le fait qu'il dépend de Logger . Si vous n'avez pas la source, cela limite votre capacité à comprendre et à utiliser efficacement le code dont vous dépendez.

Le modèle singleton, tel qu'il est mis en œuvre avec des propriétés/méthodes statiques, n'est guère plus qu'un bricolage pour mettre en œuvre une infrastructure. Il vous limite de multiples façons tout en n'offrant aucun avantage perceptible par rapport aux autres solutions. Vous pouvez l'utiliser comme bon vous semble, mais comme il existe des alternatives viables qui favorisent une meilleure conception, il ne devrait jamais être une pratique recommandée.

10 votes

@BryanWatts tout cela dit, les singletons sont toujours beaucoup plus rapides et moins sujets aux erreurs dans une application normale de taille moyenne. Habituellement, je n'ai pas plus d'une implémentation possible pour un logger (personnalisé), alors pourquoi le ** aurais-je besoin de : 1. Créer une interface pour cela et la modifier chaque fois que je dois ajouter/changer des membres publics 2. Maintenir une configuration DI 3. Cacher le fait qu'il n'y a qu'un seul objet de ce type dans tout le système 4. Me lier à une fonctionnalité stricte causée par une séparation prématurée des préoccupations.

0 votes

UriAbramson : Il semble que vous ayez déjà pris la décision sur les compromis que vous préférez, donc je ne vais pas essayer de vous convaincre.

0 votes

@BryanWatts Ce n'est pas le cas, mon commentaire était peut-être un peu trop dur, mais j'ai vraiment envie d'entendre ce que vous avez à dire sur le point que j'ai soulevé...

68voto

Bozho Points 273663

Si vous voulez vérifier ce qui est enregistré dans un test, vous avez besoin de l'injection de dépendances. De plus, un logger est rarement un singleton - généralement, vous avez un logger pour chacune de vos classes.

Regardez cette présentation sur Object-oriented design for testability et vous verrez pourquoi les singletons sont mauvais.

Le problème avec les singletons est qu'ils représentent un état global qui est difficile à prévoir, surtout dans les tests.

Gardez à l'esprit qu'un objet peut être de facto singleton mais être obtenu par injection de dépendance, plutôt que par Singleton.getInstance() .

Je ne fais qu'énumérer certains points importants soulevés par Misko Hevery dans sa présentation. Après l'avoir regardée, vous comprendrez mieux pourquoi il est préférable d'avoir un objet défini. ce que ses dépendances sont, mais pas de définir une manière comment créer les.

19voto

Péter Török Points 72981

D'autres ont très bien expliqué le problème des singletons en général. Je voudrais juste ajouter une remarque sur le cas spécifique du Logger. Je suis d'accord avec vous pour dire que ce n'est généralement pas un problème d'accéder à un Logger (ou au Root logger, pour être précis) en tant que singleton, via un static getInstance() ou getRootLogger() méthode. (sauf si vous voulez voir ce qui est enregistré par la classe que vous testez - mais dans mon expérience, je ne me souviens guère de cas où cela a été nécessaire. Cependant, pour d'autres personnes, cela pourrait être une préoccupation plus pressante).

En général, un logger singleton n'est pas un problème, puisqu'il ne contient pas d'état pertinent pour la classe que vous testez. En d'autres termes, l'état du logger (et ses éventuelles modifications) n'a aucun effet sur l'état de la classe testée. Il ne rend donc pas vos tests unitaires plus difficiles.

L'alternative serait d'injecter le logger via le constructeur, dans (presque) toutes les classes de votre application. Pour des raisons de cohérence des interfaces, le logger doit être injecté même si la classe en question n'enregistre rien pour l'instant. maintenant si vous avez besoin de journaliser quelque chose à partir de cette classe, vous avez besoin d'un logger, donc vous devez ajouter un paramètre de constructeur pour DI, ce qui casse tout le code client. Je n'aime pas ces deux options, et j'ai le sentiment qu'utiliser DI pour la journalisation ne ferait que me compliquer la vie afin de me conformer à une règle théorique, sans aucun avantage concret.

Donc ma conclusion est : une classe qui est utilisée (presque) universellement, mais qui ne contient pas d'état pertinent pour votre application, peut être implémentée en toute sécurité en tant que Singleton. .

1 votes

Même dans ce cas, un singleton peut s'avérer pénible. Si vous réalisez que votre logger a besoin d'un paramètre supplémentaire dans certains cas, vous devez soit créer une nouvelle méthode (et laisser le logger devenir plus laid), soit casser tous les consommateurs du logger, même s'ils ne s'en soucient pas.

4 votes

@kyoryu, je parle du cas "habituel", qui, à mon avis, implique l'utilisation d'un cadre de journalisation standard (de facto). (qui sont généralement configurables via des propriétés / fichiers XML). Bien sûr, il y a des exceptions - comme toujours. Si je sais que mon application est exceptionnelle à cet égard, je n'utiliserai pas de Singleton. Mais la suringénierie en disant "cela pourrait être utile un jour" est presque toujours un effort gaspillé.

0 votes

Si vous utilisez déjà le DI, il n'y a pas beaucoup d'ingénierie supplémentaire. (BTW, je ne suis pas en désaccord avec vous, je suis l'upvote). De nombreux enregistreurs nécessitent des informations de type "catégorie" ou autre, et l'ajout de paramètres supplémentaires peut s'avérer pénible. Le cacher derrière une interface peut aider à garder le code de consommation propre, et peut faciliter le passage à un autre cadre de journalisation.

10voto

Scott Weinstein Points 11404

Il s'agit surtout, mais pas uniquement, de tests. Les singletons ont été populaires parce qu'il était facile de les consommer, mais il y a un certain nombre d'inconvénients aux singletons.

  • Difficile à tester. Comment m'assurer que le logger fait la bonne chose ?
  • Difficile à tester avec. Cela signifie que si je teste un code qui utilise le logger, mais que ce n'est pas le point central de mon test, je dois quand même m'assurer que mon environnement de test supporte le logger.
  • Parfois, vous ne voulez pas un singlton, mais plus de flexibilité.

DI vous permet de consommer facilement vos classes dépendantes - il suffit de les mettre dans les arguments du constructeur, et le système les fournit pour vous - tout en vous donnant la flexibilité de test et de construction.

0 votes

Pas vraiment. Il s'agit de l'état mutable partagé et des dépendances statiques qui rendent les choses pénibles à long terme. Les tests ne sont que l'exemple le plus évident, et souvent le plus douloureux.

2voto

kyoryu Points 8281

La seule fois où vous devriez utiliser un singleton au lieu de l'injection de dépendances est si le singleton représente une valeur immuable, comme List.Empty ou autre (en supposant des listes immuables).

La vérification instinctive d'un Singleton devrait être "est-ce que ça irait si c'était une variable globale au lieu d'un Singleton ?" Si ce n'est pas le cas, vous utilisez le modèle Singleton pour masquer une variable globale et vous devriez envisager une autre approche.

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