web-dev-qa-db-fra.com

Existe-t-il des alternatives viables au modèle GOF Singleton?

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

Existe-t-il des alternatives spécifiques au modèle GOF Singleton?

Par exemple, plusieurs fois lorsque j'ai utilisé le modèle singleton dans le passé, je suis simplement intéressé à conserver l'état/les valeurs d'une ou plusieurs variables. Cependant, l'état/les valeurs des variables 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, ok? Je suppose que je pose une question purement académique dans votre meilleure voix de bande-annonce de film: "Dans un univers parallèle où il n'y a pas de singleton, que pourrions-nous faire?"

90

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

"Lorsqu'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 au "sommet" de votre système. Cela peut être dans une configuration Spring, ou dans du code, ou défini de différentes manières selon votre système.
  3. Passez l'instance unique dans chaque composant qui en a besoin (injection de dépendance)
74
Jacek Szymański

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 masquent les dépendances.

Pourquoi est-ce important?

Parce que Si vous cachez 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 service, 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 cadre IoC pour gérer cela, ou vous pouvez le faire manuellement, mais l'important est de se débarrasser de votre état global et de rendre les dépendances et les collaborations explicites.

93
Rasmus Faber

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 assurer qu'il n'y a qu'une seule instance d'une classe qui est 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 "Où sont passés tous les singletons?" , 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 en dépend, il n'en recevra une instance que par 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 usine 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.

14
seuvitor

Vous ne devriez pas avoir à sortir de votre chemin 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 entre utiliser un modèle ou ne pas utiliser le modèle. 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 motif 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).

8
Thomas Owens

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 classes simples singletons et les injecter si nécessaire.

8
Kai

Monostate (décrit dans Agile Software Development de Robert C. Martin) est une alternative à 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é.

4
Mark M

Le modèle singleton existe car il existe des situations où un objet unique 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é. 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 de singleton, je recommande d'utiliser le modèle de service "like" avec conteneurs desservis , où, au lieu d'utiliser votre singleton via un 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 souhaitez changer le type d'un objet de singleton à quelque chose d'autre, vous aurez du temps et du temps à le faire. De plus, en tant qu'avantage supplémentaire, vous n'avez pas à transmettre une attribution d'instances d'objet à chaque méthode.

Voir aussi 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, pas les services d'objet fournis par l'objet.

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

4
Pop Catalin

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

(bien que je dirais que ce n'est pas la bonne façon d'utiliser un Singleton en premier lieu)

2
David Koelle

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. Les chances sont que votre MumbleManager contiendra un sac de propriété qui contient votre état.

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

1
plinth

Utilisez un objet simple et un objet d'usine. La fabrique est chargée de contrôler l'instance et les détails de l'objet brut uniquement avec les informations de configuration (qu'elle contient par exemple) et le comportement.

0
David

En fait, si vous concevez à partir de zéro en évitant les singeltons, vous n'aurez peut-être pas à contourner l'utilisation de singletons en utilisant des variables statiques. Lorsque vous utilisez des variables statiques, vous créez également un Singleton plus ou moins, 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 de gérer la situation sans un Singleton.

BTW, personnellement, je n'ai aucun problème avec les singletons et je ne peux pas comprendre les problèmes des autres concernant les singletons. Je ne vois rien de mal à leur sujet. Autrement dit, si vous ne les abusez pas. Toutes les techniques utiles peuvent être abusées et si elles sont abusées, elles conduiront à 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.

0
Mecki

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 dans PHP 4. Je l'ai fait comme un moyen de créer un gestionnaire de base de données 'black-box' qui s'est initialisé automatiquement et n'a pas dû être transmis de haut en bas) appels dans un cadre incomplet et quelque peu cassé.

Après avoir lu certains liens de modèles singleton, je ne suis pas sûr de pouvoir l'implémenter de la même manière. Ce dont nous avions vraiment besoin, c'était de plusieurs objets avec un stockage partagé (par exemple, le véritable handle de base de données) et c'est à peu près ce que mon appel s'est transformé.

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

0
staticsan

Personnellement, pour moi, un moyen beaucoup plus sensé 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 pas penser à des différences de comportement du point de vue de l'utilisateur)

0
user18834

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 de voir si quelque chose exige absolument, définitivement, 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é lors du codage, vous devriez vraiment revoir votre conception et arrêter de coder les 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 les variables goto et globales ne sont pas à éviter). Au lieu de cela, vous devez surveiller son utilisation et vous assurer que c'est la meilleure méthode pour obtenir ce que vous voulez faire efficacement.

0
workmad3

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

0
Manrico Corazzi