Les singletons sont comme le communisme: ils sonnent tous les deux très bien sur le papier, mais explosent de problèmes dans la pratique.
Le modèle singleton met un accent disproportionné sur la facilité d'accès aux objets. Il évite complètement le contexte en exigeant que chaque consommateur utilise un objet de portée AppDomain, ne laissant aucune option pour différentes implémentations. Il intègre les connaissances de l'infrastructure dans vos classes (l'appel à GetInstance()
) tout en ajoutant une puissance d'expression exactement nulle . Cela réduit en fait votre pouvoir expressif, car vous ne pouvez pas changer l'implémentation utilisée par une classe sans la changer pour toutes . Vous ne pouvez tout simplement pas ajouter des fonctionnalités uniques.
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 le comprendre pleinement Foo
ou l'utiliser avec confiance à moins de lire sa source et de découvrir le fait dont il dépend 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'implémenté avec des propriétés / méthodes statiques, n'est guère plus qu'un hack autour de l'implémentation d'une infrastructure. Il vous limite de multiples façons tout en n'offrant aucun avantage perceptible par rapport aux alternatives. Vous pouvez l'utiliser comme vous le souhaitez, mais comme il existe des alternatives viables qui favorisent une meilleure conception, cela ne devrait jamais être une pratique recommandée.
D'autres ont très bien expliqué le problème des singletons en général. Je voudrais juste ajouter une note sur le cas spécifique de Logger. Je suis d'accord avec vous que ce n'est généralement pas un problème d'accéder à un enregistreur (ou au root logger, pour être précis) en tant que singleton, via une méthode statique
getInstance()
ougetRootLogger()
. (sauf si vous voulez voir ce qui est consigné par la classe que vous testez - mais d'après mon expérience, je peux difficilement me souvenir de tels cas où cela était nécessaire. Là encore, pour d'autres, cela pourrait être une préoccupation plus urgente).IMO généralement un enregistreur singleton n'est pas un souci, car il ne contient aucun état pertinent pour la classe que vous testez. Autrement dit, l'état de l'enregistreur (et ses modifications possibles) n'ont aucun effet sur l'état de la classe testée. Cela 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 la cohérence des interfaces, il doit être injecté même si la classe en question ne consigne rien pour le moment - l'alternative serait que lorsque vous découvrez à un moment donné que vous devez maintenant enregistrer quelque chose de cette classe, vous avez besoin d'un enregistreur, donc vous devez ajouter un paramètre de constructeur pour DI, cassant tout le code client. Je n'aime pas ces deux options et je pense que l'utilisation de 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 la suivante: 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 .
la source
Il s'agit principalement, mais pas entièrement, de tests. Les singltons étaient populaires car il était facile de les consommer, mais les singletons présentent un certain nombre d'inconvénients.
DI vous permet de consommer facilement vos classes dépendantes - il suffit de le mettre dans les arguments du constructeur, et le système le fournit pour vous - tout en vous offrant la flexibilité de test et de construction.
la source
À peu près le seul moment où vous devriez utiliser un Singleton au lieu de Dependency Injection est si le Singleton représente une valeur immuable, telle que List.Empty ou similaire (en supposant des listes immuables).
La vérification intestinale d'un Singleton devrait être "Serais-je d'accord si c'était une variable globale au lieu d'un Singleton?" Sinon, vous utilisez le modèle Singleton pour couvrir une variable globale et vous devriez envisager une approche différente.
la source
Je viens de consulter l'article de Monostate - c'est une alternative intéressante à Singleton, mais il a des propriétés étranges:
N'est-ce pas effrayant - parce que le mappeur dépend vraiment de la connexion à la base de données pour effectuer save () - mais si un autre mappeur a été créé auparavant - il peut ignorer cette étape dans l'acquisition de ses dépendances. Bien que soigné, c'est aussi un peu désordonné, n'est-ce pas?
la source
Il existe d'autres alternatives à Singleton: les modèles Proxy et MonoState.
http://www.objectmentor.com/resources/articles/SingletonAndMonostate.pdf
Comment le modèle de proxy peut-il être utilisé pour remplacer un singleton?
la source