309 votes

Singleton : Comment l'utiliser

Modifier : Dans une autre question, j'ai fourni une réponse qui contient des liens vers de nombreuses questions/réponses sur les singletons : Plus d'informations sur les singletons ici :

J'ai donc lu le fil de discussion Les singletons : bonne conception ou béquille ?
Et la dispute fait toujours rage.

Je considère les singletons comme un modèle de conception (bon et mauvais).

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 pense qu'il peut en implémenter un correctement (et d'après les nombreux entretiens que j'ai réalisés, la plupart des gens ne le peuvent pas). De plus, parce que tout le monde pense pouvoir implémenter un Singleton correct, ils abusent du Pattern et l'utilisent dans des situations qui ne sont pas appropriées (remplacer des variables globales par des Singletons !).

Les principales questions auxquelles il faut répondre sont donc les suivantes :

  • Quand utiliser un Singleton
  • Comment implémenter correctement un Singleton

Mon espoir pour cet article est que nous puissions rassembler en un seul endroit (plutôt que de devoir chercher sur Google et sur de multiples sites) une source faisant autorité sur quand (et ensuite comment) utiliser correctement un Singleton. Une liste d'anti-usages et de mauvaises implémentations courantes expliquant pourquoi elles ne fonctionnent pas et leurs faiblesses pour les bonnes implémentations serait également appropriée.


Alors, lancez-vous :
Je vais lever la main et dire que c'est ce que j'utilise mais qu'il y a probablement des problèmes.
J'aime la façon dont Scott Myers traite le sujet dans son livre "Effective C++".

Les bonnes situations pour utiliser les singletons (pas beaucoup) :

  • Cadres de journalisation
  • Bassins de recyclage des 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 des critiques et d'autres mises en œuvre.
-)

37 votes

Que se passe-t-il si vous décidez plus tard que vous voulez plusieurs enregistreurs ? Ou plusieurs pools de threads ? Si vous ne voulez qu'un seul logger, alors créez une seule instance et rendez-la globale. Les singletons ne sont bons que si vous avez absolument besoin qu'il n'y en ait qu'un seul et qu'il soit global, IMHO.

4 votes

Qui a dit qu'un cadre ne peut avoir qu'une seule instance de logger. Une seule instance représentant le Framework. Framwork peut alors vous donner des loggers spécifiques.

0 votes

Oui. Je n'utiliserais pas un singeltong comme un threadpool. Je lance juste des idées pour susciter des réponses.

192voto

Javaxpert Points 1107

Réponse :

Utilisez un Singleton si :

  • Vous devez avoir un et un seul objet d'un type dans le système.

N'utilisez pas un Singleton si :

  • Vous voulez sauvegarder la mémoire
  • Vous voulez essayer quelque chose de nouveau
  • Tu veux montrer à quel point tu sais
  • Parce que tout le monde le fait (voir programmeur de culte du cargo dans wikipedia)
  • Dans les widgets de l'interface utilisateur
  • C'est censé être un cache
  • Dans les cordes
  • En sessions
  • Je peux y aller toute la journée

Comment créer le meilleur singleton :

  • Plus c'est petit, mieux c'est. Je suis un minimaliste
  • Veillez à ce que les fils soient protégés
  • Assurez-vous qu'il n'est jamais nul
  • Veillez à ce qu'il ne soit créé qu'une seule fois
  • Initialisation paresseuse ou système ? Selon vos besoins
  • Parfois, le système d'exploitation 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 trouver un moyen d'éliminer les ressources.
  • Utilise peu de mémoire

15 votes

En fait, je pense que vous n'avez pas tout à fait raison non plus. Je reformulerais comme suit : "Si vous besoin de pour avoir un et un seul objet d'un type dans le système ET vous besoin de pour y avoir un accès global " L'accent sur le besoin est le mien - ne le faites pas si c'est pratique, seulement si vous DEVEZ l'avoir.

93 votes

Vous avez également tort. Si vous avez besoin d'un et d'un seul objet, vous en créez un et un seul. S'il n'y a aucun moyen logique d'accueillir deux instances sans corrompre irréversiblement l'application, vous devriez envisager d'en faire un singleton. Et puis il y a l'autre aspect, l'accès global : Si vous n'avez pas besoin d'un accès global à l'instance, elle ne devrait pas être un singleton.

0 votes

Je pensais connaître le modèle singleton dans le monde des langages gérés, mais j'ai ensuite découvert le livre d'Alexandrescu - "Modern C++ Design : Generic Programming and Design Patterns Applied". Cela a tout changé. Il explique en détail tous les problèmes liés au singleton en C++, et le projet OSS compagnon, Loki, est ma première dépendance dans tous les projets C++ sur lesquels je travaille.

82voto

jalf Points 142628

Les singletons vous donnent la possibilité de combiner deux mauvais traits en une seule classe. C'est faux dans presque tous les domaines.

Un singleton vous donne :

  1. Accès global à un objet, et
  2. Une garantie qu'il n'y a pas plus d'un objet de ce type ne peut jamais être créé

Le premier point est simple. Les globaux sont généralement mauvais. Nous ne devrions jamais rendre des objets accessibles globalement, sauf si nous voulons vraiment en ont besoin.

Le numéro deux peut sembler logique, mais réfléchissons-y. À quand remonte la dernière fois où vous avez **accidentellement* créé un nouvel objet au lieu de référencer un objet existant ? Puisqu'il s'agit du langage C++, utilisons un exemple tiré de ce langage. Vous arrive-t-il souvent d'écrire accidentellement

std::ostream os;
os << "hello world\n";

Quand vous aviez l'intention d'écrire

std::cout << "hello world\n";

Bien sûr que non. Nous n'avons pas besoin de protection contre cette erreur, car ce genre d'erreur n'arrive tout simplement pas. Si c'est le cas, la bonne réponse est de rentrer chez soi, de dormir pendant 12 à 20 heures et d'espérer se sentir mieux.

Si un seul objet est nécessaire, il suffit de créer une instance. Si un objet doit être accessible à l'échelle mondiale, faites-en un global. Mais cela ne signifie pas qu'il doit être impossible de créer d'autres instances de cet objet.

La contrainte "une seule instance est possible" ne nous protège pas vraiment contre les bogues probables. Mais elle fait rendent notre code très difficile à remanier et à maintenir. Parce que très souvent nous découvrons plus tard que nous avions besoin de plus d'une instance. Nous avons faire ont plus d'une base de données, nous faire ont plus d'un objet de configuration, nous voulons plusieurs enregistreurs. Nos tests unitaires peuvent vouloir être capables de créer et de recréer ces objets à chaque test, pour prendre un exemple courant.

Donc un singleton doit être utilisé si et seulement si, nous avons besoin de les deux les caractéristiques qu'il offre : Si nous besoin de accès global (ce qui est rare, car les globaux sont généralement déconseillés) et nous besoin de pour empêcher quiconque de jamais créer plus d'une instance d'une classe (ce qui me semble être un problème de conception). La seule raison que je vois pour cela est que la création de deux instances corromprait l'état de notre application - probablement parce que la classe contient un certain nombre de membres statiques ou une autre bêtise similaire. Dans ce cas, la réponse évidente est de corriger cette classe. Elle ne devrait pas dépendre du fait d'être la seule instance.

Si vous avez besoin d'un accès global à un objet, faites-en un global, comme par exemple std::cout . Mais ne contraignez pas le nombre d'instances qui peuvent être créées.

Si vous avez absolument besoin de limiter le nombre d'instances d'une classe à une seule, et qu'il n'y a aucun moyen de gérer la création d'une seconde instance en toute sécurité, alors appliquez cette règle. Mais ne la rendez pas non plus accessible à tous.

Si vous avez besoin des deux traits, alors 1) faites-en un singleton, et 2) faites-moi savoir pourquoi vous en avez besoin, parce que j'ai du mal à imaginer un tel cas.

0 votes

Supposons que, dans un seul thread, vous ayez besoin d'accéder à un objet de base de données spécifique qui est partagé par une hiérarchie de classes ORM, une classe ReportGenerator et une classe DataMaintenance. Bien que vous puissiez passer l'instance de la base de données dans le constructeur de chaque objet, cela pourrait rendre la liste d'instanciation vraiment longue, et plutôt difficile à maintenir. Au lieu de cela, vous pourriez créer un objet SharedDatabase en tant que singleton, et le faire circuler de cette façon.

3 votes

Ou vous pouvez en faire un global, et obtenir seulement un des inconvénients d'un singleton. Avec le singleton, vous vous limitez simultanément à une seule instance de cette classe de base de données. Pourquoi faire cela ? Vous pouvez aussi vous demander pourquoi vous avez tant de dépendances que la liste d'instanciation devient "très longue". Sont-elles toutes nécessaires ? Certaines d'entre elles devraient-elles être déléguées à d'autres composants ? Peut-être que certaines d'entre elles pourraient être regroupées dans une structure afin que nous puissions les transmettre comme un seul argument. Il existe de nombreuses solutions, toutes meilleures que les singletons.

0 votes

@ jalf - Voici le problème : voulez-vous plusieurs instances d'un objet de base de données ? Habituellement pas de il n'y a qu'une seule base de données. C'est un exemple où il est utile d'avoir une entité globale qui ne peut avoir qu'une seule instance.

37voto

DrPizza Points 9355

Le problème des singletons n'est pas leur mise en œuvre. C'est qu'ils confondent deux concepts différents, dont aucun n'est évidemment souhaitable.

1) Les singletons fournissent un mécanisme d'accès global à un objet. Bien qu'ils puissent être marginalement plus sûrs pour les threads ou marginalement plus fiables dans les langages sans ordre d'initialisation bien défini, cette utilisation est toujours l'équivalent moral d'une variable globale. C'est une variable globale habillée d'une syntaxe maladroite (foo::get_instance() au lieu de g_foo, par exemple), mais elle sert exactement le même objectif (un objet unique accessible dans tout le programme) et présente exactement les mêmes inconvénients.

2) Les singletons empêchent les instanciations multiples d'une classe. Il est rare, IME, que ce genre de fonctionnalité soit intégrée dans une classe. C'est normalement une chose beaucoup plus contextuelle ; beaucoup de choses qui sont considérées comme une et une seule sont en réalité juste des happenings-to-be-only-one. IMO une solution plus appropriée est de créer une seule instance - jusqu'à ce que vous réalisiez que vous avez besoin de plus d'une instance.

6 votes

D'accord. Deux maux peuvent faire un bien selon certains, dans le monde réel. Mais en programmation, mélanger deux mauvaises idées ne donne pas une bonne idée.

29voto

Paweł Hajdan Points 8004

Une chose avec les motifs : ne pas généraliser . Ils ont tous les cas où ils sont utiles, et où ils échouent.

Singleton peut être désagréable quand vous devez test le code. Vous êtes généralement coincé avec une seule instance de la classe, et vous pouvez choisir entre ouvrir une porte dans le constructeur ou une méthode pour réinitialiser l'état et ainsi de suite.

L'autre problème est que le Singleton n'est en fait rien de plus qu'une variable globale sous une forme déguisée. Lorsque vous avez trop d'état global partagé sur votre programme, les choses ont tendance à revenir en arrière, nous le savons tous.

Il peut faire suivi des dépendances plus difficile. Lorsque tout dépend de votre Singleton, il est plus difficile de le modifier, de le diviser en deux, etc. Vous êtes généralement coincé avec lui. Cela nuit également à la flexibilité. Recherchez des Injection de dépendances pour tenter d'atténuer ce problème.

8 votes

Non, un singleton est bien plus qu'une variable globale déguisée. C'est ce qui le rend particulièrement mauvais. Il combine le caractère global (qui est généralement mauvais) avec un autre concept qui est le suivant également mauvais (celui de ne pas laisser le programmeur instancier une classe s'il décide qu'il a besoin d'une instance) Ils sont souvent utilisé comme variables globales, oui. Et puis ils entraînent aussi l'autre effet secondaire désagréable, et paralysent la base de code.

8 votes

Il convient également de noter que les singletons ne doivent pas nécessairement être accessibles au public. Un singleton peut très bien être interne à la bibliothèque et ne jamais être exposé à l'utilisateur. Ils ne sont donc pas nécessairement "globaux" dans ce sens.

2 votes

@jalf Ne pas permettre à quelqu'un de créer plus d'une instance d'une classe n'est pas une mauvaise chose. S'il ne doit y avoir qu'une seule instance de la classe instanciée, cela renforce l'exigence. Si quelqu'un décide plus tard qu'il a besoin de créer une autre instance, il doit la remanier, car elle n'aurait jamais dû être un singleton en premier lieu.

14voto

Eli Courtwright Points 53071

Les singletons vous permettent d'avoir un état global complexe dans des langages qui, autrement, rendent difficile ou impossible l'utilisation de variables globales complexes.

Java, en particulier, utilise les singletons pour remplacer les variables globales, puisque tout doit être contenu dans une classe. Ce qui se rapproche le plus des variables globales, ce sont les variables statiques publiques, qui peuvent être utilisées comme si elles étaient globales à l'aide de la commande import static

Le C++ dispose de variables globales, mais l'ordre dans lequel les constructeurs de variables globales de classe sont invoqués n'est pas défini. En tant que tel, un singleton vous permet de différer la création d'une variable globale jusqu'à la première fois où cette variable est nécessaire.

Des langages tels que Python et Ruby utilisent très peu les singletons car vous pouvez utiliser des variables globales dans un module à la place.

Quand est-il bon ou mauvais d'utiliser un singleton ? A peu près exactement quand il serait bon/mauvais d'utiliser une variable globale.

0 votes

Quand une variable globale est-elle "bonne" ? Parfois, elles sont la meilleure solution à un problème, mais elles ne sont jamais "bonnes".

2 votes

Une variable globale est bonne lorsqu'elle est utilisée partout, et que tout le monde peut y avoir accès. Une implémentation d'une machine de turing à état unique peut utiliser un singleton.

0 votes

J'aime la couche d'indirection dans cette réponse : "quand il serait bon/mauvais d'utiliser un global". DevSolar et Lee Louviere obtiennent tous deux la valeur avec laquelle ils sont d'accord, même si, au moment de la réponse, on ne pouvait pas savoir qui allait commenter.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X