J'ai lu différentes opinions sur le motif singleton. Certains soutiennent qu'il devrait être évité à tout prix et d'autres qu'il peut être utile dans certaines situations.
Une situation dans laquelle j'utilise des singletons est quand j'ai besoin d'une fabrique (disons un objet f de type F) pour créer des objets d'une certaine classe A. La fabrique est créée une fois à l'aide de certains paramètres de configuration, puis est utilisée chaque fois qu'un objet de le type A est instancié. Ainsi, chaque partie du code qui veut instancier A récupère le singleton f et crée la nouvelle instance, par exemple
F& f = F::instance();
boost::shared_ptr<A> a = f.createA();
Donc, le scénario général est que
- Je n'ai besoin que d'une seule instance d'une classe soit pour des raisons d'optimisation (je n'ai pas besoin de plusieurs objets d'usine) ou pour partager un état commun (par exemple, l'usine sait combien d'instances de A elle peut encore créer)
- J'ai besoin d'un moyen d'avoir accès à cette instance f de F à différents endroits du code.
Je ne souhaite pas savoir si ce modèle est bon ou mauvais, mais en supposant que je souhaite éviter d'utiliser un singleton, quel autre modèle puis-je utiliser?
Les idées que j'avais étaient (1) d'obtenir l'objet d'usine à partir d'un registre ou (2) de créer l'usine à un moment donné pendant le démarrage du programme, puis de transmettre l'usine comme paramètre.
Dans la solution (1), le registre lui-même est un singleton, donc je viens de déplacer le problème de ne pas utiliser un singleton de l'usine au registre.
Dans le cas (2), j'ai besoin d'une source initiale (objet) d'où provient l'objet d'usine, j'ai donc peur de retomber à un autre singleton (l'objet qui fournit mon instance d'usine). En suivant cette chaîne de singletons, je peux peut-être réduire le problème à un singleton (toute l'application) par lequel tous les autres singletons sont directement ou indirectement gérés.
Cette dernière option (en utilisant un singleton initial qui crée tous les autres objets uniques et injecte tous les autres singletons aux bons endroits) serait-elle une solution acceptable? Est-ce la solution qui est implicitement suggérée lorsque l'on conseille de ne pas utiliser de singletons, ou quelles sont les autres solutions, par exemple dans l'exemple illustré ci-dessus?
MODIFIER
Puisque je pense que le point de ma question a été mal compris par certains, voici quelques informations supplémentaires. Comme expliqué par exemple ici , le mot singleton peut indiquer (a) une classe avec un objet instance unique et (b) un modèle de conception utilisé pour créer et accéder à un tel objet.
Pour clarifier les choses, utilisons le terme objet unique pour (a) et motif singleton pour (b). Donc, je sais ce que sont le modèle singleton et l'injection de dépendances (BTW, dernièrement, j'utilise fortement DI pour supprimer les instances du modèle singleton d'un code sur lequel je travaille).
Mon point est qu'à moins que le graphe d'objet entier soit instancié à partir d'un seul objet vivant sur la pile de la méthode principale, il sera toujours nécessaire d'accéder à certains objets uniques via le modèle singleton.
Ma question est de savoir si la création et le câblage d'un graphique d'objet complet dépendent de la méthode principale (par exemple via un cadre DI puissant qui n'utilise pas le modèle lui-même) est la seule solution sans modèle singleton .
Réponses:
Votre deuxième option est une bonne façon de procéder - c'est une sorte d'injection de dépendance, qui est le modèle utilisé pour partager l'état dans votre programme lorsque vous voulez éviter les singletons et les variables globales.
Vous ne pouvez pas contourner le fait que quelque chose doit créer votre usine. Si ce quelque chose se trouve être l'application, qu'il en soit ainsi. Le point important est que votre usine ne devrait pas se soucier de quel objet l'a créée, et les objets qui reçoivent l'usine ne devraient pas dépendre de la provenance de l'usine. Ne laissez pas vos objets obtenir un pointeur vers le singleton d'application et lui demander la fabrique; Demandez à votre application de créer l'usine et de la donner aux objets qui en auront besoin.
la source
Le but d'un singleton est de faire en sorte qu'une seule instance puisse jamais exister dans un certain domaine. Cela signifie qu'un singleton est utile si vous avez de bonnes raisons d'appliquer le comportement de singleton; dans la pratique, c'est rarement le cas cependant, et le multi-traitement et le multi-thread brouille certainement le sens de «unique» - est-ce une instance par machine, par processus, par thread, par requête? Et votre implémentation singleton prend-elle en compte les conditions de concurrence?
Au lieu de singleton, je préfère utiliser l'un des deux:
La raison en est qu'un singleton est un état global déguisé, ce qui signifie qu'il introduit un haut degré de couplage dans toute l'application - n'importe quelle partie de votre code peut saisir l'instance de singleton de n'importe où avec un minimum d'effort. Si vous utilisez des objets locaux ou passez des instances, vous avez beaucoup plus de contrôle et vous pouvez garder vos étendues petites et vos dépendances étroites.
la source
La plupart des gens (y compris vous) comprennent complètement mal ce qu'est réellement le modèle Singleton. Le modèle Singleton signifie uniquement qu'une instance d'une classe existe et qu'il existe un mécanisme permettant au code dans toute l'application d'obtenir une référence à cette instance.
Dans le livre GoF, la méthode d'usine statique qui renvoie une référence à un champ statique n'était qu'un exemple à quoi ce mécanisme pourrait ressembler, et une avec de graves inconvénients. Malheureusement, tout le monde et leur chien se sont accrochés à ce mécanisme et ont pensé que c'était à cela que ressemblait Singleton.
Les alternatives que vous citez sont en fait aussi des singletons, juste avec un mécanisme différent pour obtenir la référence. (2) entraîne clairement trop de contournements, sauf si vous n'avez besoin de la référence qu'à quelques endroits près de la racine de la pile d'appels. (1) sonne comme un cadre d'injection de dépendance brute - alors pourquoi ne pas en utiliser un?
L'avantage est que les infrastructures DI existantes sont flexibles, puissantes et bien testées. Ils peuvent faire bien plus que gérer des Singletons. Cependant, pour utiliser pleinement leurs capacités, ils fonctionnent mieux si vous structurez votre application d'une certaine manière, ce qui n'est pas toujours possible: idéalement, il y a des objets centraux qui sont acquis via le framework DI et ont toutes leurs dépendances transitoirement peuplées et sont puis exécuté.
Edit: En fin de compte, tout dépend de la méthode principale de toute façon. Mais vous avez raison: la seule façon d'éviter complètement l'utilisation de global / statique est d'avoir tout configuré à partir de la méthode principale. Notez que DI est le plus populaire dans les environnements de serveur où la méthode principale est opaque et configure le serveur et le code d'application consiste essentiellement en rappels qui sont instanciés et appelés par le code du serveur. Mais la plupart des frameworks DI permettent également un accès direct à leur registre central, par exemple le ApplicationContext de Spring, pour des "cas spéciaux".
Donc, fondamentalement, la meilleure chose que les gens ont trouvée jusqu'à présent est une combinaison intelligente des deux alternatives que vous avez mentionnées.
la source
Il existe quelques alternatives:
Injection de dépendance
Chaque objet a ses dépendances qui lui sont transmises lors de sa création. En règle générale, un framework ou un petit nombre de classes d'usine sont responsables de la création et du câblage des objets
Registre des services
Chaque objet reçoit un objet de registre de service. Il fournit des méthodes pour demander différents objets différents qui fournissent différents services. Le cadre Android utilise ce modèle
Gestionnaire racine
Il existe un seul objet qui est la racine du graphe d'objet. En suivant les liens de cet objet, tout autre objet peut éventuellement être trouvé. Le code a tendance à ressembler à:
la source
Singleton::instance
partout dans le code client.Si vos besoins du singleton peuvent se résumer à une seule fonction, pourquoi ne pas simplement utiliser une simple fonction d'usine? Les fonctions globales (probablement une méthode statique de classe F dans votre exemple) sont intrinsèquement singletons, avec unicité imposée par le compilateur et l'éditeur de liens.
Certes, cela tombe en panne lorsque le fonctionnement du singleton ne peut pas être réduit à un seul appel de fonction, bien qu'un petit ensemble de fonctions connexes puisse peut-être vous aider.
Cela étant dit, les points 1 et 2 de votre question montrent clairement que vous ne voulez vraiment que quelque chose. Selon votre définition du motif singleton, vous l'utilisez déjà ou très proche. Je ne pense pas que vous puissiez avoir quelque chose sans que ce soit un singleton ou du moins très proche. C'est tout simplement trop proche de la signification de singleton.
Comme vous le suggérez, à un moment donné, vous devez avoir quelque chose, donc peut-être que le problème n'est pas d'avoir une seule instance de quelque chose, mais de prendre des mesures pour empêcher (ou au moins décourager ou minimiser) l'abus. Déplacer autant d'états que possible du "singleton" vers les paramètres est un bon début. Rétrécir l'interface du singleton est également utile, car il y a moins de possibilités d'abus de cette façon. Parfois, il suffit de le sucer et de rendre le singleton très robuste, comme le tas ou le système de fichiers.
la source
Si vous utilisez un langage multiparadigm comme C ++ ou Python, une alternative à une classe singleton est un ensemble de fonctions / variables enveloppées dans un espace de noms.
Conceptuellement parlant, un fichier C ++ avec des variables globales libres, des fonctions globales gratuites et des variables statiques utilisées pour cacher des informations, le tout enveloppé dans un espace de noms, vous donne presque le même effet qu'une "classe" singleton.
Il ne tombe en panne que si vous souhaitez hériter. J'ai vu beaucoup de singletons qui auraient été mieux ainsi.
la source
Encapsulation de singletons
Scénario de cas. Votre application est orientée objet et nécessite 3 ou 4 singletons spécifiques, même si vous avez plus de classes.
Avant l'exemple (C ++ comme pseudocode):
Une façon consiste à supprimer partiellement certains singletons (globaux), en les encapsulant tous dans un singleton unique, qui a pour membres, les autres singletons.
De cette façon, au lieu de "plusieurs singletons, approche, contre, pas de singleton du tout, approche", nous avons le "encapsuler tous les singletons en une seule, approche".
Après l'exemple (C ++ comme pseudocode):
Veuillez noter que les exemples ressemblent davantage à des pseudocodes et ignorent les bugs mineurs ou les erreurs de syntaxe et envisagent la solution à la question.
Il y a aussi autre chose à considérer, car le langage de programmation utilisé peut affecter la façon d'implémenter votre implémentation singleton ou non singleton.
À votre santé.
la source
Vos exigences d'origine:
Ne vous alignez pas sur la définition d'un singleton (et sur ce à quoi vous faites plutôt référence plus tard). De GoF (ma version est 1995) page 127:
Si vous n'avez besoin que d'une seule instance, cela ne vous empêche pas d'en créer d'autres.
Si vous voulez une seule instance accessible globalement, créez une seule instance globalement accessible . Il n'est pas nécessaire d'avoir un modèle nommé pour tout ce que vous faites. Et oui, les instances uniques accessibles globalement sont généralement mauvaises. Leur donner un nom ne les rend pas moins mauvais .
Pour éviter l'accessibilité globale, les approches courantes «créer une instance et la transmettre» ou «demander au propriétaire de deux objets de les coller ensemble» fonctionnent bien.
la source
Que diriez-vous d'utiliser le conteneur IoC? Avec une certaine réflexion, vous pouvez vous retrouver avec quelque chose comme:
Vous devrez spécifier quelle implémentation
IFoo
utiliser à un démarrage, mais il s'agit d'une configuration unique que vous pouvez facilement remplacer plus tard. La durée de vie d'une instance résolue peut être normalement contrôlée par un conteneur IoC.La méthode statique singleton void pourrait être remplacée par:
La méthode getter singleton statique pourrait être remplacée par:
L'exemple est pertinent pour .NET, mais je suis certain que des résultats similaires peuvent être obtenus dans d'autres langues.
la source