Question

Regardons les choses en face. Le motif Singleton est un sujet hautement controversé avec des programmeurs hordes sur les 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 motif et l'utilisent sans cesse. Je ne veux pas que la singleton Controversy soit au cœur de nos préoccupations de ma question, cependant. Tout le monde peut avoir un bras de fer et se battre pour savoir qui gagne pour tous ses soucis . Ce que j'essaie de dire, c'est que je ne crois pas qu'il existe une seule bonne réponse et que je n'essaye pas intentionnellement d'enflammer les querelles partisanes. Je suis simplement intéressé par singleton-alternatives quand je pose la question:

Existe-t-il une alternative spécifique au modèle GOF Singleton?

Par exemple, lorsque j’ai utilisé le modèle singleton dans le passé, j’ai tout simplement voulu préserver l’état / les valeurs d’une ou de plusieurs variables. Cependant, l’état / les valeurs des variables peuvent être conservés entre chaque instanciation de la classe à l’aide de variables statiques au lieu du modèle singleton.

Quelle autre idée avez-vous?

MODIFIER: je ne souhaite pas vraiment que ce soit un autre message sur "comment utiliser le singleton correctement". 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?"

Était-ce utile?

La solution

Alex Miller dans " Patterns I Hate " ; cite le texte suivant:

"Lorsqu'un singleton semble être la solution, 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 dans le haut de la page & # 8220; haut & # 8221; de votre système. Cela peut être dans une configuration Spring, ou dans un code, ou défini de différentes façons en fonction de votre système.
  3. Passez l'instance unique à chaque composant qui en a besoin (injection de dépendance)

Autres conseils

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 masquez les dépendances, vous aurez tendance à perdre de vue la quantité de couplage.

Vous pourriez soutenir 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.

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

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’une seule instance d’une classe est partagée par 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. De plus, il est très utile d’isoler vos tests unitaires.

En résumé, que devez-vous faire? Lorsqu'un objet dépend d'un autre, il n'en reçoit 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 ensuite, l'usine.

class FactoryOfNeedy {

    private ExSingletonClass exSingleton;

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

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

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

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

Spring ou tout autre conteneur IoC fait relativement bien son travail dans ce domaine. Comme 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.

Vous ne devriez pas avoir à faire tout votre possible pour éviter toute tendance. L'utilisation d'un motif est soit une décision de conception, soit un ajustement naturel (elle tombe simplement en place). Lorsque vous concevez un système, vous avez le choix d'utiliser un motif ou de ne pas utiliser le motif. 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 ou ce n'est pas approprié et je ne l'utilise pas. Je crois que c'est aussi simple que cela.

L’adéquation (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).

Le modèle singleton existe, car il peut arriver qu'un objet unique soit nécessaire pour fournir un ensemble de services .

Même si tel est le cas, je considère toujours l'approche consistant à créer des singletons en utilisant un champ / propriété statique global représentant l'instance, ce qui est 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.

Ainsi, au lieu du modèle classique, singleton, je vous recommande d'utiliser le modèle de service "like" avec les 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

Ainsi, lorsque vous voulez changer le type d'un objet de singleton à autre chose, vous aurez facilement le temps de le faire. De plus, en tant qu’avantage supplémentaire, vous n’avez pas à transmettre un nombre d’instances d’objet à chaque méthode.

Voir aussi Inversion de contrôle , l'idée est que en exposant les 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 cacher l'utilisation du modèle de singleton chaque fois que cela est possible, car il n'est pas toujours possible de l'éviter ni souhaitable.

Monostate (décrit dans Développement de logiciels agiles de Robert C. Martin) est une alternative au singleton. Dans ce modèle, les données de la classe sont toutes statiques, mais les accesseurs / paramètres sont non 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 à celui de singleton mais le fait d'une manière où le programmeur n'est pas nécessairement conscient du fait qu'un singleton est utilisé.

Si vous utilisez un singleton pour représenter un seul objet de données, vous pouvez plutôt transmettre 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)

Si votre problème est que vous voulez conserver l'état, vous voulez une classe MumbleManager. Avant de commencer à travailler avec un système, votre client crée un gestionnaire MumbleManager, où Mumble est le nom du système. Etat est retenu à travers cela. Les chances sont que votre MumbleManager contiendra un sac de propriété qui détient votre état.

Ce style a une apparence 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 gestionnaire MumbleManager.

Utilisez un objet simple et un objet usine. L’usine est responsable de la gestion de l’instance et des détails de l’objet brut uniquement avec les informations de configuration (qu’il contient par exemple) et le comportement.

En fait, si vous concevez dès le départ en évitant les Singeltons, vous ne serez peut-être pas obligé de ne pas utiliser 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, ils se comportent tous comme s'ils 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 élégante, à savoir comment gérer la situation sans passer par Singleton.

BTW, je n'ai personnellement aucun problème avec Singletons et je ne comprends pas les problèmes que d'autres personnes ont avec Singletons. Je ne vois rien de mal à leur sujet. Autrement dit, si vous ne les abusez pas. Toute technique utile peut être utilisée à mauvais escient et, si on en abuse, cela aboutira à des résultats négatifs. Une autre technique fréquemment mal utilisée est l'héritage. Pourtant, personne ne dirait que l'héritage est quelque chose de grave simplement parce que certaines personnes en abusent horriblement.

Personnellement pour moi & # 1072; Une façon beaucoup plus sensée 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 vois aucune différence de comportement du point de vue de l’utilisateur)

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

Si tel est le cas, vous avez un singleton. Si vous lancez des singletons pour faciliter 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 les variables goto et globales ne doivent pas être évités). Au lieu de cela, vous devriez surveiller son utilisation et vous assurer que c'est la meilleure méthode pour obtenir ce que vous voulez faire de manière efficace.

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 / une session et initialise toutes les classes dans cette classe. tout ce qui se rapporte à la session a également accès au "singleton". ainsi contenu.

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 moyen de créer un gestionnaire de base de données «boîte noire» qui s’initialisait automatiquement et ne devait pas être transmis d’appels de fonction dans un cadre incomplet et quelque peu cassé.

Après avoir lu quelques liens de motifs singleton, je ne suis pas tout à fait sûr de le mettre en œuvre de la même manière. Ce dont nous avions vraiment besoin, c’était de multiples objets avec stockage partagé (par exemple, le descripteur de base de données proprement dit) et c’est à peu près ce que mon appel a transformé.

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

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

Pour " éviter " cela implique que je rencontre souvent de nombreuses situations dans lesquelles le modèle de singleton convient naturellement, et que je dois donc prendre des mesures pour désamorcer ces situations.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top