Existe-t-il des alternatives viables au GOF Singleton Pattern?

91

Avouons-le. Le motif Singleton est un sujet très controversé avec des hordes de programmeurs des deux côtés de la clôture. Il y a ceux qui pensent que le Singleton n'est rien de plus qu'une variable globale glorifiée, et d'autres qui ne jurent que par le modèle et l'utilisent sans cesse. Cependant, je ne veux pas que la controverse sur Singleton soit au cœur de ma question. Tout le monde peut avoir un bras de fer et se battre et voir qui gagne pour tout ce que je tiens . Ce que j'essaie de dire, c'est que je ne crois pas qu'il y ait une seule bonne réponse et je n'essaie pas intentionnellement d'attiser les querelles partisanes. Je suis simplement intéressé par les alternatives singleton lorsque je pose la question:

Existe-t-il des alternatives spécifiques au GOF Singleton Pattern?

Par exemple, plusieurs fois lorsque j'ai utilisé le modèle singleton dans le passé, je suis simplement intéressé par la préservation de l'état / des valeurs d'une ou plusieurs variables. L'état / les valeurs des variables, cependant, peuvent être conservés entre chaque instanciation de la classe en utilisant des variables statiques au lieu d'utiliser le modèle singleton.

Quelle autre idée avez-vous?

EDIT: Je ne veux pas vraiment que ce soit un autre article sur «comment utiliser correctement le singleton». Encore une fois, je cherche des moyens de l'éviter. Pour le plaisir, d'accord? Je suppose que je pose une question purement académique dans votre meilleure voix de bande-annonce: "Dans un univers parallèle où il n'y a pas de singleton, que pourrions-nous faire?"

CodageSansCommentaires
la source
Quoi? Ce n'est ni bon ni mauvais, mais comment puis-je le remplacer? Pour tous ceux qui disent que c'est bien - ne participez pas. Vous tous qui dites que c'est mauvais, prouvez-le en me montrant comment je peux vivre sans. Cela me paraît argumentatif.
S.Lott
@CodingWithoutComents: j'ai lu tout le message. C'est comme ça que j'ai eu le sentiment de "ne répondez pas si vous pensez que les Singletons vont bien".
S.Lott
2
Eh bien, si c'est comme ça que ça s'est passé, je m'excuse. Je pensais avoir pris des mesures importantes pour éviter la polarisation. Je pensais avoir posé la question de manière à ce que les amoureux et les ennemis des Singletons puissent tous les deux partir avec cela en tant que programmeur, nous avons tous le choix - que ce n'est jamais un seul bon moyen
CodingWithoutComments
Si j'utilise des Singletons, je n'ai aucune contribution possible sur la façon de les contourner. Cela me semble polarisant.
S.Lott
9
J'utilise des Singletons tous les jours, mais est-ce que cela m'empêche de penser qu'il pourrait y avoir une meilleure façon de faire les choses? Les modèles de conception n'existent que depuis 14 ans. Est-ce que je les considère comme une vérité biblique? Arrêtons-nous d'essayer de sortir des sentiers battus? N'essayons-nous pas de faire progresser la discipline de CS?
CodingWithoutComments

Réponses:

76

Alex Miller dans " Patterns I Hate " cite ce qui suit:

"Quand un singleton semble être la réponse, je trouve qu'il est souvent plus sage de:

  1. Créez une interface et une implémentation par défaut de votre singleton
  2. Construisez une seule instance de votre implémentation par défaut en «haut» de votre système. Cela peut être dans une configuration Spring, ou dans le code, ou défini de différentes manières en fonction de votre système.
  3. Passez l'instance unique dans chaque composant qui en a besoin (injection de dépendances)
Jacek Szymański
la source
2
Je sais que c'est près de 5 ans, mais pourriez-vous nous en dire plus? Je pense qu'on m'a appris que la bonne façon de créer un singleton était avec une interface. Je serais intéressé si ce que je faisais était «OK», et pas aussi terrible qu'on me l'a dit.
Pureferret
97

Pour comprendre la bonne façon de contourner les Singletons, vous devez comprendre ce qui ne va pas avec les Singletons (et l'état global en général):

Les singletons cachent les dépendances.

Pourquoi est-ce important?

Parce que si vous masquez les dépendances, vous avez tendance à perdre la trace de la quantité de couplage.

Vous pourriez dire que

void purchaseLaptop(String creditCardNumber, int price){
  CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
  Cart.getInstance().addLaptop();
}

est plus simple que

void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart, 
                    String creditCardNumber, int price){
  creditCardProcessor.debit(creditCardNumber, amount);
  cart.addLaptop();
}

mais au moins la deuxième API indique clairement quels sont les collaborateurs de la méthode.

Ainsi, le moyen de contourner les singletons n'est pas d'utiliser des variables statiques ou des localisateurs de services, mais de changer les classes Singleton en instances, qui sont instanciées dans la portée où elles ont un sens et injectées dans les composants et les méthodes qui en ont besoin. Vous pouvez utiliser un framework IoC pour gérer cela, ou vous pouvez le faire manuellement, mais l'important est de vous débarrasser de votre état global et de rendre les dépendances et les collaborations explicites.

Rasmus Faber
la source
+1 Pour discuter de la racine du problème, pas seulement de la manière de le contourner.
Phil
9
Dans une application multiniveau à part entière, la deuxième méthode crée souvent une quantité massive de code inutile. En polluant le code des niveaux intermédiaires avec une logique à transmettre aux niveaux inférieurs, des objets qui sont autrement inutiles à ce niveau, ne créons-nous pas des dépendances qui ne le sont pas? Supposons que le niveau 4 d'une pile de navigation lance une action en arrière-plan, comme un téléchargement de fichier. Supposons maintenant que vous souhaitiez alerter l'utilisateur une fois l'opération terminée, mais d'ici là, l'utilisateur peut se trouver dans une partie totalement différente de l'application qui ne partage que le niveau 1 en commun avec la vue de départ. Des tonnes de code inutile ...
Hari Karam Singh
1
@RasmusFaber Un modèle Singleton ne masque pas les dépendances. Vous vous plaignez du fait que le masquage des dépendances est une préoccupation distincte du modèle. Il est vrai que le motif permet de commettre facilement cette erreur, mais il est très possible d'exécuter en harmonie les motifs IoC et Singleton. Par exemple, je peux créer une bibliothèque tierce qui a besoin d'une gestion spéciale du cycle de vie de ses instances, et toute personne qui utilise ma bibliothèque doit toujours "passer" une instance de mon singleton à ses classes en utilisant l'injection de dépendances classique plutôt que "sky hook" mon singleton de chaque point.
Martin Bliss
@HariKaramSingh, vous pouvez utiliser un modèle d'abonnement / publication ou un bus d'événements à l'échelle du système (qui doit être partagé entre toutes les parties concernées, pas un singleton) pour contourner ce problème.
Victor Sorokin
4
De plus, les modèles de pub / sub ou d'observateur peuvent être tout aussi mauvais et "perdre la trace du couplage" que quiconque a dû passer au débogage à travers un framework qui repose fortement sur eux l'attestera. Au moins avec les singleton, le nom de la méthode invoquée est au même endroit que le code appelant :) Personnellement, je pense que toutes ces stratégies ont leur place et aucune ne peut être implémentée aveuglément. Les codeurs bâclés fabriqueront des spaghettis à partir de n'importe quel modèle et les codeurs responsables n'ont pas besoin de se charger excessivement de limitations idéologiques pour créer un code élégant.
Hari Karam Singh
14

La meilleure solution que j'ai trouvée consiste à utiliser le modèle d'usine pour construire des instances de vos classes. En utilisant le modèle, vous pouvez vous assurer qu'il n'y a qu'une seule instance d'une classe partagée entre les objets qui l'utilisent.

Je pensais que ce serait compliqué à gérer, mais après avoir lu ce billet de blog "Where Have All the Singletons Gone?" , cela semble si naturel. Et en passant, cela aide beaucoup à isoler vos tests unitaires.

En résumé, que devez-vous faire? Chaque fois qu'un objet dépend d'un autre, il en recevra une instance uniquement via son constructeur (pas de nouveau mot-clé dans votre classe).

class NeedyClass {

    private ExSingletonClass exSingleton;

    public NeedyClass(ExSingletonClass exSingleton){
        this.exSingleton = exSingleton;
    }

    // Here goes some code that uses the exSingleton object
}

Et puis, l'usine.

class FactoryOfNeedy {

    private ExSingletonClass exSingleton;

    public FactoryOfNeedy() {
        this.exSingleton = new ExSingletonClass();
    }

    public NeedyClass buildNeedy() {
        return new NeedyClass(this.exSingleton);
    }
}

Comme vous n'instancierez votre fabrique qu'une seule fois, il y aura une seule instanciation d'exSingleton. Chaque fois que vous appelez buildNeedy, la nouvelle instance de NeedyClass sera fournie avec exSingleton.

J'espère que ça aide. Veuillez signaler toute erreur.

seuvitor
la source
5
pour vous assurer que votre Factory n'est instanciée qu'une fois que vous avez besoin d'un constructeur privé et que vous définissez la méthode buildNeedy comme une méthode statique.
Julien Grenier
16
Julien a raison, ce correctif a un défaut fondamental, c'est que vous dites implicitement que vous ne pouvez instancier qu'une seule usine. Si vous prenez les précautions nécessaires pour vous assurer qu'une seule usine est instanciée (comme l'a dit Julien), vous vous retrouvez avec ... un singleton! En réalité, cette approche ajoute simplement une couche d'abstraction inutile au-dessus du singleton.
Bob Somers
Il existe une autre façon de n'avoir qu'une seule instance. Vous ne pouvez instancier votre usine qu'une seule fois. Pas besoin d'imposer cela par le compilateur. Cela aide également avec les tests unitaires où vous souhaitez une instance différente.
vendredi
C'est la meilleure solution que j'ai jamais vue au problème qui est mal résolu avec les singletons - ajouter des dépendances sans encombrer chaque appel de méthode (et donc redessiner l'interface et refactoriser ses dépendances). Une légère modification est parfaite pour ma situation où il n'est pas important qu'il n'y ait qu'une seule instance du "singleton", mais il est important que tout le monde partage la même instance par défaut (la modification est: au lieu d'utiliser une fabrique, utilisez static méthodes pour définir en externe l'instance commune et utiliser cette instance pendant la construction).
meustrus
Donc, fondamentalement, l'avantage de cette approche est que vous n'avez qu'à mélanger autour d' une seule instance d'objet (l'usine) plutôt que potentiellement tout un tas d'objets (pour lesquels vous choisissez de ne pas utiliser Singletons)?
Hari Karam Singh
9

Spring ou tout autre conteneur IoC fait un assez bon travail à cet égard. Étant donné que les classes sont créées et gérées en dehors de l'application elle-même, le conteneur peut créer des singletons de classes simples et les injecter si nécessaire.

Kai
la source
Vous avez des liens sur les sujets susmentionnés?
CodingWithoutComments
Ces frameworks semblent spécifiques à java - d'autres options spécifiques au langage? Ou indépendant de la langue?
CodingWithoutComments
pour .NET: Castle Windsor castleproject.org/container/index.html Microsoft Unity msdn.microsoft.com/en-us/library/cc468366.aspx Unity est en fait un framework d'injection de dépendances, mais cela fonctionnera probablement pour ce sujet.
Erik van Brakel
1
pourquoi il n'y a pas trop de votes pour cela? IOC est la solution parfaite pour éviter Singleton.
Sandeep Jindal
@CodingWithoutComments Je sais que PHP a le conteneur de services Symfony. Les mecs rubis ont cependant d'autres moyens d'injecter des dépendances. Avez-vous une langue spécifique qui vous intéresse?
Bevelopper
9

Vous ne devriez pas avoir à vous mettre en quatre pour éviter tout schéma. L'utilisation d'un motif est soit une décision de conception, soit un ajustement naturel (il se met simplement en place). Lorsque vous concevez un système, vous avez le choix d'utiliser un modèle ou de ne pas l'utiliser. Cependant, vous ne devriez pas faire tout votre possible pour éviter tout ce qui est finalement un choix de conception.

Je n'évite pas le modèle Singleton. Soit c'est approprié et je l'utilise, soit ce n'est pas approprié et je ne l'utilise pas. Je pense que c'est aussi simple que cela.

La pertinence (ou son absence) du Singleton dépend de la situation. C'est une décision de conception qui doit être prise et les conséquences de cette décision doivent être comprises (et documentées).

Thomas Owens
la source
J'allais dire la même chose, mais cela ne répond pas tout à fait à sa question :)
Swati
2
Veuillez indiquer dans votre réponse quand cela est approprié ou non. Jolie s'il-vous-plaît.
CodingWithoutComments
Cela répond à la question. Je n'ai pas de techniques pour éviter l'utilisation du Singleton Pattern parce que je ne fais pas tout mon possible pour l'éviter, et je ne pense pas qu'un développeur devrait le faire.
Thomas Owens
Je ne pense pas que vous puissiez généraliser quand c'est approprié ou non. Tout est spécifique au projet et dépend grandement de la conception du système en question. Je n'utilise pas de «règles empiriques» pour des décisions comme celle-ci.
Thomas Owens
Hmm. Je ne comprends pas pourquoi cela est rejeté. J'ai répondu à la question - Quelles sont vos techniques pour éviter l'utilisation du motif Singleton? - en disant que je n'ai pas de techniques parce que je ne fais pas tout mon possible pour éviter le modèle. Les contrevenants pourraient-ils expliquer leur raisonnement?
Thomas Owens
5

Monostate (décrit dans Agile Software Development de Robert C. Martin) est une alternative au singleton. Dans ce modèle, les données de la classe sont toutes statiques mais les getters / setters ne sont pas statiques.

Par exemple:

public class MonoStateExample
{
    private static int x;

    public int getX()
    {
        return x;
    }

    public void setX(int xVal)
    {
        x = xVal;
    }
}

public class MonoDriver
{
    public static void main(String args[])
    {
        MonoStateExample m1 = new MonoStateExample();
        m1.setX(10);

        MonoStateExample m2 = new MonoStateExample();
        if(m1.getX() == m2.getX())
        {
            //singleton behavior
        }
    }
}

Monostate a un comportement similaire à singleton mais le fait d'une manière où le programmeur n'est pas nécessairement conscient du fait qu'un singleton est utilisé.

Mark M
la source
Cela mérite plus de votes; Je suis venu ici de Google à la recherche d'un rafraîchissement MonoState!
mahemoff
@Mark Il me semble que cette conception est très difficile à verrouiller en termes de sécurité des threads. Dois-je créer un verrou d'instance pour chaque variable statique dans MonoStateExample, ou est-ce que je crée un verrou que toutes les propriétés exploitent? Les deux ont des ramifications sérieuses qui sont facilement résolues exécutées dans un modèle Singleton.
Martin Bliss
Cet exemple est une alternative au modèle Singleton, mais ne résout pas les problèmes de modèle Singleton.
Sandeep Jindal
4

Le modèle singleton existe car il existe des situations dans lesquelles un seul objet est nécessaire pour fournir un ensemble de services .

Même si c'est le cas, je considère toujours l'approche de création de singletons en utilisant un champ / propriété statique global représentant l'instance, inappropriée. C'est inapproprié car cela crée une dépendance dans le code entre le champ statique et l'objet non, les services fournis par l'objet.

Donc, au lieu du modèle classique, singleton, je recommande d'utiliser le modèle de service `` comme '' avec des conteneurs desservis , où au lieu d'utiliser votre singleton via un champ statique, vous obtenez une référence à celui-ci via une méthode demandant le type de service requis.

*pseudocode* currentContainer.GetServiceByObjectType(singletonType)
//Under the covers the object might be a singleton, but this is hidden to the consumer.

au lieu d'un seul global

*pseudocode* singletonType.Instance

De cette façon, lorsque vous voulez changer le type d'un objet de singleton à autre chose, vous aurez du temps à le faire. De plus, comme avantage supplémentaire, vous n'avez pas à transmettre une part d'instances d'objet à chaque méthode.

Voir également Inversion de contrôle , l'idée est qu'en exposant des singletons directement au consommateur, vous créez une dépendance entre le consommateur et l'instance d'objet, et non les services d'objet fournis par l'objet.

Mon avis est de masquer l'utilisation du motif singleton chaque fois que cela est possible, car il n'est pas toujours possible de l'éviter, ou souhaitable.

Pop Catalin
la source
Je ne suis pas tout à fait votre exemple de conteneurs avec services. Avez-vous des ressources en ligne auxquelles vous pourriez vous connecter?
CodingWithoutComments
Jetez un œil à "Castle Project - Windsor Container" castleproject.org/container/index.html . Malheureusement, il est très difficile de trouver des publications abstraites sur ce sujet.
Pop Catalin
2

Si vous utilisez un Singleton pour représenter un seul objet de données, vous pouvez plutôt passer un objet de données en tant que paramètre de méthode.

(bien que, je dirais que c'est la mauvaise façon d'utiliser un Singleton en premier lieu)

David Koelle
la source
1

Si votre problème est que vous souhaitez conserver l'état, vous voulez une classe MumbleManager. Avant de commencer à travailler avec un système, votre client crée un MumbleManager, où Mumble est le nom du système. L'État est conservé à travers cela. Il y a de fortes chances que votre MumbleManager contienne un sac de propriétés qui contient votre état.

Ce type de style ressemble beaucoup à C et pas très à un objet - vous constaterez que les objets qui définissent votre système auront tous une référence au même MumbleManager.

socle
la source
Sans plaider pour ou contre Singleton, je dois dire que cette solution est un anti-pattern. MumbleManager devient une "classe de Dieu" qui contient toutes sortes de connaissances disparates, violant la responsabilité unique. Cela peut aussi bien être un Singleton pour tous les soins de l'application.
Martin Bliss
0

Utilisez un objet simple et un objet usine. La fabrique est responsable de la réglementation de l'instance et des détails de l'objet simple uniquement avec les informations de configuration (qu'elle contient par exemple) et le comportement.

David
la source
1
Une usine n'est-elle pas souvent un singleton? C'est ainsi que je vois généralement les modèles Factory implémentés.
Thomas Owens
0

En fait, si vous concevez à partir de zéro en évitant les singeltons, vous n'aurez peut-être pas à contourner le fait de ne pas utiliser de singletons en utilisant des variables statiques. Lorsque vous utilisez des variables statiques, vous créez également plus ou moins un Singleton, la seule différence est que vous créez différentes instances d'objet, mais en interne, elles se comportent toutes comme si elles utilisaient un Singleton.

Pouvez-vous peut-être donner un exemple détaillé où vous utilisez un Singleton ou où un Singleton est actuellement utilisé et que vous essayez d'éviter de l'utiliser? Cela pourrait aider les gens à trouver une solution plus sophistiquée sur la façon dont la situation pourrait être gérée sans Singleton du tout.

BTW, je n'ai personnellement aucun problème avec les Singletons et je ne peux pas comprendre les problèmes que les autres ont concernant les Singletons. Je ne vois rien de mal à leur sujet. Autrement dit, si vous n'en abusez pas. Toute technique utile peut être abusée et si elle est abusée, cela conduira à des résultats négatifs. L'héritage est une autre technique couramment utilisée à mauvais escient. Personne ne dirait toujours que l'héritage est quelque chose de mauvais simplement parce que certaines personnes en abusent horriblement.

Mecki
la source
0

Personnellement, pour moi, un moyen beaucoup plus judicieux d'implémenter quelque chose qui se comporte comme singleton est d'utiliser une classe entièrement statique (membres statiques, méthodes statiques, propriétés statiques). La plupart du temps, je l'implémente de cette manière (je ne peux penser à aucune différence de comportement du point de vue de l'utilisateur)

user18834
la source
0

Je pense que le meilleur endroit pour contrôler le singleton est au niveau de la conception de la classe. À ce stade, vous devriez être en mesure de cartographier les interactions entre les classes et voir si quelque chose exige absolument qu'une seule instance de cette classe existe à tout moment de la vie des applications.

Si tel est le cas, alors vous avez un singleton. Si vous lancez des singletons pour plus de commodité pendant le codage, vous devriez vraiment revoir votre conception et arrêter de coder lesdits singletons :)

Et oui, «police» est le mot que je voulais dire ici plutôt que «éviter». Le singleton n'est pas quelque chose à éviter (de la même manière que goto et les variables globales ne sont pas quelque chose à éviter). Au lieu de cela, vous devriez surveiller son utilisation et vous assurer que c'est la meilleure méthode pour faire ce que vous voulez de manière efficace.

workmad3
la source
1
Je comprends tout à fait ce que vous dites workmad. Et je suis entièrement d'accord. Cependant, votre réponse ne répond toujours pas spécifiquement à ma question. Je ne voulais pas que ce soit une autre question de "quand est-il approprié d'utiliser le singleton?" J'étais juste intéressé par des alternatives viables au singleton.
CodingWithoutComments
0

J'utilise singleton principalement comme "conteneur de méthodes", sans aucun état. Si j'ai besoin de partager ces méthodes avec de nombreuses classes et que je veux éviter le fardeau de l'instanciation et de l'initialisation, je crée un contexte / session et j'initialise toutes les classes là-bas; tout ce qui fait référence à la session a également accès au "singleton" ainsi contenu.

Manrico Corazzi
la source
0

N'ayant pas programmé dans un environnement intensément orienté objet (par exemple Java), je ne suis pas complètement au courant des subtilités de la discussion. Mais j'ai implémenté un singleton en PHP 4. Je l'ai fait comme un moyen de créer un gestionnaire de base de données «boîte noire» qui s'initialise automatiquement et qui n'a pas à passer des appels de fonction de haut en bas dans un cadre incomplet et quelque peu cassé.

Après avoir lu quelques liens de modèles singleton, je ne suis pas tout à fait sûr de pouvoir l'implémenter à nouveau de la même manière. Ce qui était vraiment nécessaire, c'était de multiples objets avec stockage partagé (par exemple, le handle de base de données réel) et c'est à peu près ce que mon appel s'est transformé.

Comme la plupart des modèles et algorithmes, utiliser un singleton «juste parce que c'est cool» est la mauvaise chose à faire. J'avais besoin d'un appel vraiment «boîte noire» qui ressemblait beaucoup à un singleton. Et l'OMI, c'est le moyen d'aborder la question: soyez conscient du modèle, mais regardez aussi sa portée plus large et à quel niveau son instance doit être unique.

statique
la source
-1

Que voulez-vous dire, quelles sont mes techniques pour l'éviter?

Pour «l'éviter», cela implique qu'il y a de nombreuses situations que je rencontre dans lesquelles le motif singleton est naturellement un bon ajustement, et donc que je dois prendre des mesures pour désamorcer ces situations.

Mais il n'y en a pas. Je n'ai pas à éviter le modèle singleton. Cela ne se produit tout simplement pas.

DrPizza
la source