Quelle est l'alternative à Singleton

114

Nous avons une classe qui contient les informations de configuration de l'application. C'était un singleton. Après un examen architectural, on nous a dit de supprimer le singleton. Nous avons vu certains avantages de ne pas utiliser singleton dans les tests unitaires car nous pouvons tester différentes configurations en même temps.

Sans singleton, nous devons passer l'instance partout dans notre code. Cela devient tellement compliqué que nous avons écrit un wrapper singleton. Maintenant que nous portons le même code vers PHP et .NET, je me demande s'il existe un meilleur modèle que nous pouvons utiliser pour l'objet de configuration.

John Mill
la source

Réponses:

131

Le blog Google Testing contient une série d'entrées sur la manière d'éviter Singleton (afin de créer du code testable). Peut-être que cela peut vous aider:

Le dernier article explique en détail comment déplacer la création de nouveaux objets dans une usine, afin d'éviter d'utiliser des singletons. Cela vaut la peine d'être lu à coup sûr.

En bref, nous transférons tous les nouveaux opérateurs dans une usine. Nous regroupons tous les objets de durée de vie similaire dans une seule usine.

FrankS
la source
3
*** Utiliser l'injection de dépendance pour éviter les singletons
Justin
Ces articles sont aussi bons que les normes de programmation Google C ++!
2
Eh bien pas vraiment. Le conseil «Ne pas utiliser de méthodes statiques» va directement à l'encontre du principe d'interface minimale de Scott Meyers / Herb Sutters par exemple. Il y a des conseils utiles, mais ils manquent de la contribution de plusieurs esprits.
Matthieu M.
@FrankS pourquoi avez-vous changé l'ordre des liens? C'était en bon chronologique au début.
cregox
@Cawas en fait aucune idée, c'était il y a plus de quatre ans, donc je suppose que j'avais quelques raisons à cela à l'époque :-)
FrankS
15

La meilleure façon est d'utiliser un modèle Factory à la place. Lorsque vous construisez une nouvelle instance de votre classe (en usine), vous pouvez insérer les données `` globales '' dans l'objet nouvellement construit, soit en tant que référence à une seule instance (que vous stockez dans la classe de fabrique), soit en copiant les données dans le nouvel objet.

Tous vos objets contiendront alors les données qui vivaient dans le singleton. Je ne pense pas qu'il y ait beaucoup de différence dans l'ensemble, mais cela peut rendre votre code plus facile à lire.

gbjbaanb
la source
1
Je ne suis pas d'accord avec l'énoncé de la «meilleure façon», mais +1 pour une bonne alternative.
tylermac
Le problème avec cette approche est que chaque nouvel objet contient (ou fait référence) ce qui peut potentiellement être une énorme quantité de données. var_dump () sur n'importe lequel de ces objets contenant gob aboutit très rapidement à d'énormes listes parsemées librement d' avertissements de récursivité . C'est vraiment moche, ne peut pas être terriblement efficace et donne l'impression que les choses se détraquent. Cependant, je n'ai pas personnellement trouvé de meilleur moyen. J'ai plié la méthode "usine" en utilisant __construct () pour référencer un global. On a l'impression que tout est plié en quatre, cependant, pour éviter le redoutable Singleton ...
FYA
2
@EastGhostCom: autant utiliser le singleton et arrêter d'essayer de se compliquer les choses :)
gbjbaanb
5

Je pourrais dire l'évidence ici, mais y a-t-il une raison pour laquelle vous ne pouvez pas utiliser un framework d'injection de dépendances tel que Spring ou Guice ? (Je crois que Spring est également disponible pour .NET maintenant).

De cette façon, le framework peut contenir une seule copie des objets de configuration, et vos beans (services, DAO, etc.) n'ont pas à se soucier de la recherche.

C'est l'approche que j'adopte habituellement!


la source
4

Si vous utilisez Spring Framework , vous pouvez simplement créer un bean normal. Par défaut (ou si vous définissez explicitement scope="singleton") une seule instance du bean est créée et cette instance est renvoyée chaque fois que le bean est utilisé dans une dépendance ou récupéré via getBean().

Vous bénéficiez de l'avantage de l'instance unique, sans le couplage du modèle Singleton.

Gene Gotimer
la source
3
Oh the irony - utilisez des haricots de printemps (singleton) pour remplacer votre singleton ...
Zack Macomber
4

L'alternative est de transmettre ce dont vous avez besoin au lieu de demander des choses à un objet.

Koen
la source
4

n'accumulez pas de responsabilités sur un seul objet de configuration car il se terminera par un très gros objet à la fois difficile à comprendre et fragile.

Par exemple, si vous avez besoin d'un autre paramètre pour une classe particulière, vous modifiez l' Configurationobjet, puis recompilez toutes les classes qui l'utilisent. C'est quelque peu problématique.

Essayez de refactoriser votre code pour éviter un objet commun, global et volumineux Configuration. Transmettez uniquement les paramètres requis aux classes clientes:

class Server {

    int port;

    Server(Configuration config) {
        this.port = config.getServerPort();
    } 

}

devrait être remanié pour:

 class Server {

    public Server(int port) {
       this.port = port;
    }
 }

un framework d'injection de dépendances aidera beaucoup ici, mais ce n'est pas strictement nécessaire.

dfa
la source
Oui c'est vraiment un bon point. Je l'ai fait avant. Mon gros objet de configuration implémentait des interfaces comme MailServiceConf, ServerConf .. que le framework d'injection de dépendance passe la configuration aux classes afin que mes classes ne soient pas dépendantes du gros objet de configuration.
caltuntas
1

Vous pouvez accomplir le même comportement de singleton en utilisant des méthodes statiques. Steve Yegge l'explique très bien dans ce post.

Youssef
la source
En fait, l'article est assez bon et il ne dit pas que vous devriez utiliser des méthodes statiques à la place. Au lieu de cela, il déclare que les méthodes statiques ne sont également que des singletons et à la fin, il recommande d'utiliser le modèle de méthode d'usine: «Je terminerai en disant que si vous ressentez toujours le besoin d'utiliser des objets Singleton, envisagez d'utiliser le modèle de méthode d'usine à la place. ... "
FrankS
0

Une classe contenant uniquement des méthodes et des champs statiques est-elle possible? Je ne sais pas exactement quelle est votre situation, mais cela vaut peut-être la peine de l'examiner.

Thomas Owens
la source
1
Si la classe est sans état, elle doit être une classe statique.
AlbertoPL
1
C'est en C ++ - le modèle est connu sous le nom de Monostate.
0

Dépend de quels outils / frameworks etc. sont utilisés. Avec les outils d'injection de dépendances / ioc, on peut souvent encore obtenir des performances / optimisations de singleton en demandant au conteneur di / ioc d'utiliser le comportement de singleton pour la classe requise - (comme une interface IConfigSettings) en ne créant qu'une seule instance de la classe. Cela pourrait encore être remplacé par des tests

Vous pouvez également utiliser une fabrique pour créer la classe et renvoyer la même instance à chaque fois que vous la demandez - mais pour le tester, elle peut renvoyer une version stubbed / simulée

saret
la source
0

Examinez la possibilité de faire la configuration comme interface de rappel. Ainsi, votre code sensible à la configuration ressemblera à:

MyReuseCode.Configure(IConfiguration)

Le code d'initialisation du système ressemblera à:

Library.init(MyIConfigurationImpl)
Dewfy
la source
0

Vous pouvez utiliser un framework d'injection de dépendances pour soulager la douleur de transmettre l'objet de configuration. Une solution décente est ninject qui a l'avantage d'utiliser du code plutôt que xml.

Colin Gravill
la source
0

Peut-être pas très propre non plus, mais vous pourriez peut-être passer les bits d'information que vous souhaitez modifier à la méthode qui crée le singleton - au lieu d'utiliser

public static Singleton getInstance() {
    if(singleton != null)
        createSingleton();
        return singleton;
    }
}

vous pouvez appeler createSingleton(Information info)directement au démarrage de l'application (et dans les méthodes setUp des tests unitaires).

Roland Ewald
la source
-1

Les singletons ne sont pas mauvais mais le modèle de conception est imparfait. J'ai une classe dont je souhaite créer une seule instance pendant l'exécution, mais que je souhaite créer plusieurs instances isolées pendant les tests unitaires pour garantir des résultats déterministes.

DI utilisant Spring, etc., est une très bonne option mais pas la seule.

codématrice
la source