Singleton: Comment doit-il être utilisé
Edit: D'une autre question j'ai fourni une réponse qui a des liens à beaucoup de questions / réponses sur Singleton: plus d'informations sur Singleton ici:
donc j'ai lu le fil Singletons: bon design ou une béquille?
Et la dispute fait toujours rage.
je vois les Singleton comme un motif (bon et mauvais).
Le le problème avec Singleton n'est pas le modèle mais plutôt les utilisateurs (désolé tout le monde). Tout le monde et son père pensent qu'ils peuvent mettre en œuvre correctement l'un (et d'après les nombreuses entrevues que j'ai faites, la plupart des gens ne peuvent pas). Aussi parce que tout le monde pense qu'ils peuvent mettre en œuvre un Singleton correct, ils abusent du modèle et l'utilisent dans des situations qui ne sont pas appropriées (en remplaçant les variables globales par des Singleton!).
ainsi les principales questions qui doivent être répondues sont:
- Quand devez-vous utiliser un Singleton
- comment implémenter correctement un Singleton
mon espoir pour cet article est que nous pouvons recueillir ensemble dans un seul endroit (plutôt que d'avoir à google et la recherche de plusieurs sites) une source faisant autorité de quand (et ensuite comment) d'utiliser un Singleton correctement. Il serait également approprié d'établir une liste des mesures anti-usag es et des mauvaises mises en œuvre courantes expliquant les raisons pour lesquelles elles ne fonctionnent pas et pour de bonnes implémentations leurs faiblesses.
pour obtenir le roulement de boule:
Je vais lever la main et dire que c'est ce que j'utilise mais a probablement des problèmes.
J'aime "Scott Myers" la manipulation de l'objet dans son livre "Effective C++"
de Bonnes Situations pour utiliser les Singletons (pas beaucoup):
- enregistrement des cadres
- pools de recyclage de fils
/*
* C++ Singleton
* Limitation: Single Threaded Design
* See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
* For problems associated with locking in multi threaded applications
*
* Limitation:
* If you use this Singleton (A) within a destructor of another Singleton (B)
* This Singleton (A) must be fully constructed before the constructor of (B)
* is called.
*/
class MySingleton
{
private:
// Private Constructor
MySingleton();
// Stop the compiler generating methods of copy the object
MySingleton(MySingleton const& copy); // Not Implemented
MySingleton& operator=(MySingleton const& copy); // Not Implemented
public:
static MySingleton& getInstance()
{
// The only instance
// Guaranteed to be lazy initialized
// Guaranteed that it will be destroyed correctly
static MySingleton instance;
return instance;
}
};
OK. Rassemblons quelques critiques et d'autres implémentations.
:- )
24 réponses
vous vous trompez tous. Lire la question. Réponse:
utiliser un Singleton si:
- Vous devez avoir un et un seul objet de type dans le système
ne pas utiliser un Singleton si:
- Vous voulez économiser de la mémoire
- Vous voulez essayer quelque chose de nouveau
- voulez-Vous de montrer comment beaucoup vous vous dites
- parce que tout le monde le fait (Voir culte du cargo programmeur dans wikipedia)
- dans les widgets d'interface utilisateur
- C'est censé être un " cache
- dans les cordes
- En Séances
- je peux aller tout au long de la journée
Comment créer le meilleur singleton:
- La plus petite, mieux c'est. Je suis un minimaliste
- assurez-vous qu'il est sans fil
- assurez-vous qu'il n'est jamais null
- assurez-vous qu'il n'est créé qu'une seule fois
- paresseux ou initialisation du système? À la hauteur de vos exigences
- parfois L'OS ou la JVM crée des singletons pour vous (par exemple en Java chaque définition de classe est un singleton)
- Fournir un destructeur ou en quelque sorte de trouver comment se débarrasser des ressources
- utiliser peu mémoire
Singleton vous donne la capacité de combiner deux mauvais traits dans une classe. C'est mal dans presque tous les sens.
singleton " vous donne:
- l'accès Mondial à un objet, et
- garantie qu'un seul objet de ce type ne pourra jamais être créé
numéro un est simple. Globales sont généralement mauvais. Nous ne devrions jamais faire les objets accessibles dans le monde entier, à moins que nous vraiment besoin.
numéro deux peut sembler logique, mais réfléchissons-y. Quand avez-vous créé pour la dernière fois un nouvel objet au lieu de faire référence à un objet existant? Puisque c'est marqué C++, utilisons un exemple de ce langage. Vous écrivez souvent accidentellement
std::ostream os;
os << "hello world\n";
quand vous aviez l'intention d'écrire
std::cout << "hello world\n";
de bien sûr que non. Nous n'avons pas besoin de protection contre cette erreur, parce que ce genre d'erreur ne se produit. Si c'est le cas, la bonne réponse est de rentrer à la maison et de dormir pendant 12 à 20 heures et d'espérer que vous vous sentirez mieux.
si un seul objet est nécessaire, créez simplement une instance. Si un objet doit être globalement accessible, il doit être global. Mais cela ne signifie pas qu'il devrait être impossible de créer d'autres instances.
The" une seule instance est possible" la contrainte ne nous protège pas vraiment contre les éventuels bugs. Mais il fait rendre notre code très difficile à remanier et à maintenir. Parce que très souvent nous découvrons plus tard que nous avons besoin de plus d'une instance. Nous faire ont plus d'une base de données, nous faire avoir plus d'un objet de configuration, nous ne voulons plusieurs enregistreurs. Nos tests unitaires peuvent vouloir être en mesure de créer et de recréer ces objets test, pour prendre un exemple courant.
donc un singleton devrait être utilisé si et seulement si, nous avons besoin les deux les traits qu'il offre: si nous besoin accès global (qui est rare, parce que les globals sont généralement découragés) et nous besoin pour empêcher quiconque de jamais créer plus d'un instance d'une classe (qui sonne pour moi comme un problème de design). La seule la raison que je peux voir pour cela est si la création de deux instances corromprait notre état d'application - probablement parce que la classe contient un certain nombre de membres statiques ou semblable sottise. Auquel cas la réponse est évidente pour résoudre cette classe. Ça ne devrait pas dépendre du fait d'être la seule instance.
Si vous avez besoin d'un accès global à un objet, d'en faire un, comme std::cout
. Mais ne limitez pas le nombre d'instances qui peuvent être créées.
si vous avez absolument, besoin positif de limiter le nombre d'instances d'une classe à une seule, et il n'y a aucun moyen que la création d'une deuxième instance ne puisse jamais être manipulée en toute sécurité, puis appliquer cela. Mais ne le rendez pas également accessible à l'échelle mondiale.
si vous avez besoin des deux traits, alors 1) Faites-en un seul, et 2) Faites-moi savoir ce dont vous avez besoin pour, parce que j'ai du mal à imaginer un tel cas.
le problème avec les singletons n'est pas leur mise en œuvre. C'est qu'ils confondent deux concepts différents, ni l'un ni l'autre n'est évidemment souhaitable.
1) les Singletons fournissent un mécanisme d'accès global à un objet. Bien qu'ils puissent être légèrement plus threadsafe ou légèrement plus fiable dans les langues sans un ordre d'initialisation bien défini, cet usage est toujours l'équivalent moral d'une variable globale. C'est une variable mondiale habillée dans certains syntaxe maladroite (foo::get_instance () au lieu de g_foo, say), mais il sert le même but (un seul objet accessible à travers tout le programme) et a les mêmes inconvénients.
2) les Singletons empêchent les instanciations multiples d'une classe. Il est rare, IME, que ce genre de caractéristique devrait être cuit dans une classe. C'est normalement une chose beaucoup plus contextuelle; beaucoup de choses qui sont considérées comme une seule et unique sont vraiment juste arrivées à être une seule et unique. IMO a more la solution appropriée est de créer seulement une instance--jusqu'à ce que vous réalisiez que vous avez besoin de plus d'une instance.
Une chose avec des motifs: ne pas généraliser . Ils ont tous les cas où ils sont utiles, et quand ils échouent.
Singleton peut être méchant quand vous devez tester le code. Vous êtes généralement coincé avec une instance de la classe, et peut choisir entre ouvrir une porte dans le constructeur ou une méthode pour réinitialiser l'état et ainsi de suite.
un autre problème est que le Singleton n'est en fait plus qu'une variable globale déguisée. Quand vous avez trop d'état partagé global sur votre programme, les choses ont tendance à revenir en arrière, nous le savons tous.
Il peut faire des suivi des liens de dépendance plus difficile. Quand tout dépend de votre Singleton, il est plus difficile de le changer, de le diviser en deux, etc. Vous êtes généralement coincé avec elle. Cela fait également obstacle à la flexibilité. Enquête sur certains injection de dépendance cadre pour essayer de soulage cette question.
les Singletons permettent essentiellement d'avoir un état global complexe dans des langues qui rendent autrement difficile ou impossible d'avoir des variables globales complexes.
Java utilise en particulier les singletons pour remplacer les variables globales, puisque tout doit être contenu dans une classe. Les plus proches des variables globales sont les variables statiques publiques, qui peuvent être utilisées comme si elles étaient globales avec import static
C++ A variables, mais l'ordre dans lequel les constructeurs mondiaux de variables de classe sont invoqués n'est pas défini. En tant que tel, un singleton vous permet de reporter la création d'une variable globale jusqu'à la première fois que cette variable est nécessaire.
les langages tels que Python et Ruby utilisent très peu les singletons car vous pouvez utiliser des variables globales dans un module à la place.
alors quand est-il bon/mauvais d'utiliser un singleton? À peu près exactement au moment où il serait bon/mauvais d'utiliser un variable.
Modern C++ Design par Alexandrescu a un thread-safe, pouvant être héritées du générique de singleton.
pour ma valeur de 2p, je pense qu'il est important d'avoir des vies définies pour vos singletons (quand il est absolument nécessaire de les utiliser). Normalement, Je ne laisse pas la fonction statique get()
instancier quoi que ce soit, et laisse la configuration et la destruction à une section dédiée de l'application principale. Cela permet de mettre en évidence les dépendances entre les singletons - mais, comme l'a souligné ci-dessus, il est préférable de les éviter si possible.
- comment implémenter correctement un Singleton
il y a un problème que je n'ai jamais vu mentionné, quelque chose que j'ai rencontré à un emploi précédent. Nous avions des singletons C++ qui étaient partagés entre les DLLs, et la mécanique habituelle d'assurer une seule instance d'une classe ne fonctionne tout simplement pas. Le problème est que chaque DLL obtient son propre ensemble de variables statiques, avec L'EXE. Si votre fonction get_instance est en ligne ou en partie d'une bibliothèque statique, chaque DLL se retrouvera avec sa propre copie du "singleton".
la solution est de s'assurer que le code singleton n'est défini que dans une DLL ou une EXE, ou de créer un gestionnaire singleton avec ces propriétés pour éliminer les instances.
le premier exemple n'est pas "thread safe" - si deux threads appellent gettinstance en même temps, cette statique va être une PITA. Une certaine forme de mutex pourrait aider.
comme d'autres l'ont fait remarquer, les principaux inconvénients des monoblocs comprennent l'incapacité de les étendre et la perte du pouvoir d'instancier plus d'une instance, par exemple à des fins d'essai.
Quelques aspects utiles de singletons:
- paresseux ou l'avance d'instanciation
- à portée de main pour un objet qui nécessite l'installation et/ou de l'état
Cependant, vous n'avez pas à utiliser un singleton pour obtenir ces avantage. Vous pouvez écrire un objet normal qui fait le travail, et alors les gens d'y accéder via une usine (un objet). L'usine ne peut s'inquiéter que d'en instancier un, de le réutiliser, etc. en cas de besoin. En outre, si vous programmez vers une interface plutôt que vers une classe concrète, l'usine peut utiliser des stratégies, c'est-à-dire vous pouvez basculer dans et Hors diverses implémentations de l'interface.
enfin, une usine se prête à des technologies d'injection de dépendance comme le ressort, etc.
sont pratiques lorsque vous avez un code lot qui est exécuté lorsque vous initialisez et object. Par exemple, lorsque vous utilisez iBatis lorsque vous configurez un objet persistence, il doit lire toutes les configurations, analyser les cartes, s'assurer que tout est correct, etc.. avant d'arriver à votre code.
si vous le faisiez à chaque fois, les performances seraient très dégradées. En l'utilisant dans un singleton, vous prenez ce coup une fois et puis tous les appels suivants n'ont pas à le faire.
la véritable chute des Singleton est qu'ils cassent l'héritage. Vous ne pouvez pas dériver une nouvelle classe pour vous donner la fonctionnalité étendue à moins que vous ayez accès au code où le Singleton est référencé. Donc, au-delà du fait que le Singleton va rendre votre code étroitement couplé (réparable par un schéma de stratégie ... aka Dependency Injection) il vous empêchera également de fermer des sections du code de révision (bibliothèques partagées).
donc même les exemples de les loggers ou les pools de threads sont invalides et doivent être remplacés par des stratégies.
la plupart des gens utilisent des singletons quand ils essaient de se faire sentir bien à propos de l'utilisation d'une variable globale. Il y a des utilisations légitimes, mais la plupart du temps quand les gens les utilisent, le fait qu'il ne puisse y avoir qu'une seule instance est un fait insignifiant comparé au fait qu'elle est accessible à tous.
parce qu'un singleton ne permet de créer qu'une seule instance, il contrôle efficacement la réplication des instances. par exemple, vous n'avez pas besoin de plusieurs instances d'une recherche - une carte de recherche en morse par exemple, donc l'envelopper dans une classe singleton est apt. Et ce n'est pas parce que vous avez une seule instance de la classe que vous êtes limité sur le nombre de références à cette instance. Vous pouvez faire la queue des appels(pour éviter les problèmes de threading) vers l'instance et effectuer les changements nécessaires. Oui, l' forme générale d'un singleton est un public mondial, vous pouvez certainement modifier le design pour créer un plus accès limité singleton. Je ne l'ai jamais fatigué avant, mais je sais que c'est possible. Et à tous ceux qui ont commenté en disant que le modèle singleton est totalement mauvais, vous devez savoir ceci: oui, il est mauvais si vous ne l'utilisez pas correctement ou dans les limites de la fonctionnalité efficace et du comportement prévisible: ne généralisez pas.
mais quand j'ai besoin de quelque chose comme un Singleton, je finis souvent par utiliser un Schwarz Counter pour l'instancier.
j'utilise des Singletons comme test d'interview.
quand je demande à un développeur de nommer certains modèles de conception, si tout ce qu'ils peuvent nommer est Singleton, ils ne sont pas embauchés.
ci-dessous est la meilleure approche pour mettre en œuvre un thread safe singleton pattern Avec désallocation de la mémoire dans le destructeur lui-même. Mais je pense que le destructeur devrait être facultatif car l'instance de singleton sera automatiquement détruite lorsque le programme se terminera:
#include<iostream>
#include<mutex>
using namespace std;
std::mutex mtx;
class MySingleton{
private:
static MySingleton * singletonInstance;
MySingleton();
~MySingleton();
public:
static MySingleton* GetInstance();
MySingleton(const MySingleton&) = delete;
const MySingleton& operator=(const MySingleton&) = delete;
MySingleton(MySingleton&& other) noexcept = delete;
MySingleton& operator=(MySingleton&& other) noexcept = delete;
};
MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
delete singletonInstance;
};
MySingleton* MySingleton::GetInstance(){
if (singletonInstance == NULL){
std::lock_guard<std::mutex> lock(mtx);
if (singletonInstance == NULL)
singletonInstance = new MySingleton();
}
return singletonInstance;
}
en ce qui concerne les situations où nous avons besoin d'utiliser les classes singleton peut être- Si nous voulons maintenir l'état de l'instance au cours de l'exécution du programme Si nous sommes impliqué dans l'écriture dans le journal d'exécution d'une application où une seule instance du fichier doit être utilisée....et ainsi de suite. Il sera appréciable si quelqu'un peut suggérer des optimisations dans mon code ci-dessus.
Anti-Utilisation:
un problème majeur avec l'utilisation excessive de singleton est que le modèle empêche l'extension facile et le remplacement des implémentations alternatives. Le nom de classe est codé en dur où le singleton est utilisé.
je pense que c'est la version la plus robuste pour C#:
using System;
using System.Collections;
using System.Threading;
namespace DoFactory.GangOfFour.Singleton.RealWorld
{
// MainApp test application
class MainApp
{
static void Main()
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Same instance?
if (b1 == b2 && b2 == b3 && b3 == b4)
{
Console.WriteLine("Same instance\n");
}
// All are the same instance -- use b1 arbitrarily
// Load balance 15 server requests
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}
// Wait for user
Console.Read();
}
}
// "Singleton"
class LoadBalancer
{
private static LoadBalancer instance;
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Lock synchronization object
private static object syncLock = new object();
// Constructor (protected)
protected LoadBalancer()
{
// List of available servers
servers.Add("ServerI");
servers.Add("ServerII");
servers.Add("ServerIII");
servers.Add("ServerIV");
servers.Add("ServerV");
}
public static LoadBalancer GetLoadBalancer()
{
// Support multithreaded applications through
// 'Double checked locking' pattern which (once
// the instance exists) avoids locking each
// time the method is invoked
if (instance == null)
{
lock (syncLock)
{
if (instance == null)
{
instance = new LoadBalancer();
}
}
}
return instance;
}
// Simple, but effective random load balancer
public string Server
{
get
{
int r = random.Next(servers.Count);
return servers[r].ToString();
}
}
}
}
Voici la . version optimisée :
using System;
using System.Collections;
namespace DoFactory.GangOfFour.Singleton.NETOptimized
{
// MainApp test application
class MainApp
{
static void Main()
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Confirm these are the same instance
if (b1 == b2 && b2 == b3 && b3 == b4)
{
Console.WriteLine("Same instance\n");
}
// All are the same instance -- use b1 arbitrarily
// Load balance 15 requests for a server
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}
// Wait for user
Console.Read();
}
}
// Singleton
sealed class LoadBalancer
{
// Static members are lazily initialized.
// .NET guarantees thread safety for static initialization
private static readonly LoadBalancer instance =
new LoadBalancer();
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Note: constructor is private.
private LoadBalancer()
{
// List of available servers
servers.Add("ServerI");
servers.Add("ServerII");
servers.Add("ServerIII");
servers.Add("ServerIV");
servers.Add("ServerV");
}
public static LoadBalancer GetLoadBalancer()
{
return instance;
}
// Simple, but effective load balancer
public string Server
{
get
{
int r = random.Next(servers.Count);
return servers[r].ToString();
}
}
}
}
vous pouvez trouver ce modèle à dotfactory.com .
le modèle de Meyers singleton fonctionne assez bien la plupart du temps, et à l'occasion, il ne paie pas nécessairement pour chercher quelque chose de mieux. Tant que le constructeur ne lancera jamais et qu'il n'y a pas de dépendances entre les singletons.
d'Un singleton est une mise en œuvre pour un accessible dans le monde de l'objet (GAO à partir de maintenant) bien que pas tous les GAOs sont des singletons.
Loggers eux-mêmes ne devraient pas être singletons mais le moyen de se connecter devrait idéalement être globalement accessible, pour découpler où le message de connexion est généré d'où ou comment il se connecte.
Lazy-loading / lazy evaluation est un concept différent et singleton implémente Généralement cela aussi. Il est livré avec beaucoup de ses propres problèmes, en particulier Fil-Sécurité et les problèmes si elle échoue avec des exceptions telles que ce qui semblait être une bonne idée à l'époque se révèle être pas si grand après tout. (Un peu comme la vache mise en œuvre dans les chaînes).
avec cela à l'esprit, GOAs peut être initialisé comme ceci:
namespace {
T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;
}
int main( int argc, char* argv[])
{
T1 t1(args1);
T2 t2(args2);
T3 t3(args3);
T4 t4(args4);
pt1 = &t1;
pt2 = &t2;
pt3 = &t3;
pt4 = &t4;
dostuff();
}
T1& getT1()
{
return *pt1;
}
T2& getT2()
{
return *pt2;
}
T3& getT3()
{
return *pt3;
}
T4& getT4()
{
return *pt4;
}
il n'a pas besoin d'être fait aussi grossièrement que cela, et clairement dans une bibliothèque chargée qui contient des objets que vous voulez probablement un autre mécanisme pour gérer leur durée de vie. (Les mettre dans un objet que vous obtenez lorsque vous chargez la bibliothèque).
quand est-ce que j'utilise des singletons? Je les ai utilisés pour 2 choses - Une table simple qui indique quelles bibliothèques ont été chargées avec dlopen - Un gestionnaire de messages auquel les loggers peuvent s'abonner et auquel vous pouvez envoyer des messages. Requis spécifiquement pour les manipulateurs de signaux.
je ne comprends toujours pas pourquoi un singleton doit être globale.
j'allais produire un singleton où j'ai caché une base de données à l'intérieur de la classe comme une variable statique constante privée et faire des fonctions de classe qui utilisent la base de données sans jamais exposer la base de données à l'utilisateur.
je ne vois pas pourquoi cette fonctionnalité serait mauvais.
je les trouve utiles quand j'ai une classe qui encapsule beaucoup de mémoire. Par exemple, dans un jeu récent sur lequel j'ai travaillé, j'ai une classe de carte d'influence qui contient une collection de très grands tableaux de mémoire contiguë. Je veux que tout soit alloué au démarrage, que tout soit libéré à l'arrêt et que je n'en veuille qu'une copie. Je dois aussi y accéder de nombreux endroits. Je trouve le modèle singleton très utile dans ce cas.
je suis sûr qu'il y en a d'autres solutions mais je trouve celle-ci très utile et facile à mettre en œuvre.
Si vous êtes celui qui a créé le singleton et qui l'utilise, ne faites pas comme singleton (il n'a pas de sens car vous pouvez contrôler la singularité de l'objet sans en faire singleton), mais il est intéressant dans le cas d'un développeur, d'une bibliothèque et vous voulez fournir un seul objet à vos utilisateurs (dans ce cas, l'oms a créé le singleton, mais vous n'êtes pas l'utilisateur).
les Singletons sont des objets afin de les utiliser comme des objets, de nombreuses personnes accède aux singletons directement en appelant la méthode qui le renvoie, mais c'est nocif parce que vous faites votre code sait que l'objet est singleton, je préfère utiliser singletons comme objets, je les passe à travers le constructeur et je les utilise comme objets ordinaires, de cette façon, votre code ne sait pas si ces objets sont singletons ou non et cela rend les dépendances plus claires et il aide un peu pour le remaniement ...
dans les applications de bureau (je sais, seulement nous les dinosaures écrivent plus!) ils sont essentiels pour obtenir des paramètres d'application globaux relativement inchangés - la langue de l'utilisateur, le chemin pour aider les fichiers, les préférences de l'utilisateur etc qui, autrement, devraient propogate dans chaque classe et chaque dialogue.
éditer-bien sûr, ceux-ci devraient être en lecture seule !
un Autre de la mise en œuvre
class Singleton
{
public:
static Singleton& Instance()
{
// lazy initialize
if (instance_ == NULL) instance_ = new Singleton();
return *instance_;
}
private:
Singleton() {};
static Singleton *instance_;
};