Design pattern Singleton

Rédigé par Mathieu G. - Ecrit le 28/04/2007 et mis à jour le 29/12/2022

Singleton garantit qu’une classe n’a qu’une seule instance et fournit un point d’accès global à cette instance.


Description du problème

Certaines applications possèdent des classes qui doivent être instanciées une seule et unique fois. C’est par exemple le cas d’une classe qui implémenterait un pilote pour un périphérique, ou encore un système de journalisation. En effet, instancier deux fois une classe servant de pilote à une imprimante provoquerait une surcharge inutile du système et des comportements incohérents.

On peut alors se demander comment créer une classe, utilisée plusieurs fois au sein de la même application , qui ne pourra être instancié qu’une seule fois ?

Une première solution, régulièrement utilisée, est d’instancier la classe dès le lancement de l’application dans une variable globale (c’est à dire une variable accessible depuis n’importe quel emplacement du programme). Cependant cette solution doit être évitée car en plus d’enfreindre le principe d’encapsulation elle comporte de nombreux inconvénients. En effet, rien ne garantit qu’un développeur n’instanciera pas une deuxième fois la classe à la place d’utiliser la variable globale définie. De plus, on est obligé d’instancier les variables globales dès le lancement de l’application et non à la demande (ce qui peut avoir un impact non négligeable sur la performance de l’application). Enfin, lorsqu’on arrive à plusieurs centaines de variables globales le développement devient rapidement ingérable surtout si plusieurs programmeurs travails simultanément.

Mais si on n’utilise pas ce stratagème comment faire ? C’est simple il suffit d’utiliser le design pattern Singleton.


Définition de la solution

L’objectif est d’ajouter un contrôle sur le nombre d’instances que peut retourner une classe.

La première étape consiste à empêcher les développeurs d’utiliser le ou les constructeur(s) de la classe pour l’instancier. Pour cela il suffit de déclarer privé tous les constructeurs de la classe. Attention dans certains langages une classe sans constructeur possède un constructeur implicite par défaut (c’est notamment le cas de Java). Il faut donc que celui-ci soit déclaré explicitement en privé.

Une fois cette étape accomplie, il est possible d’instancier cette classe uniquement depuis elle même, ce qui n’a pas beaucoup de sens. Comment allons nous faire pour permettre aux développeurs de l’utiliser ?

Nous allons construire un pseudo constructeur. Pour cela il faut déclarer une méthode statique qui retournera un objet correspondant au type de la classe. L’avantage de cette méthode par rapport à un constructeur, est que l’on peut contrôler la valeur que l’on va retourner. Le fait que cette méthode soit déclarée statique permet de l’appeler sans posséder d’instance de cette classe. A noter que, par convention, ce pseudo constructeur est nommé getInstance.

Pour en finir avec le concept de base du Singleton voyons comment implémenter cette méthode.

Tout d’abord il faut créer un attribut statique qui va permettre de stocker l’unique instance de la classe. Ensuite, dans le pseudo constructeur on va tester cet attribut. Si celui-ci est nul alors on crée une instance de la classe et on stocke sa valeur dans cet attribut. Sinon c’est que l’attribut possède déjà une instance de la classe. Dans tous les cas la méthode retourne la valeur de l’attribut possédant l’unique instance de la classe.


Diagramme UML


Extension : Multiton

Une variante du Singleton existe elle est nommée Multiton. Même si celle-ci est régulièrement utilisée il ne s’agit pas ici d’un design pattern officiel du Gang of Four. Comme nous venons de la voir le pattern Singleton garantit qu’une classe n’a qu’une seule instance. Le Multiton reprend ce principe en garantissant qu’il existe une seule instance par clé pour une classe. Ainsi il est par exemple possible de créer une classe qui possédera deux instances identifiées par deux clés différentes.

Pour cela on remplace l’attribut uniqueInstance par un tableau associatif (il s’agit d’un type abstrait de données composé d’un ensemble fini de clefs et d’un ensemble fini de valeurs, où chaque clef est associée à une valeur) ou table de hashage . On associe alors une clé à une instance. Puis on transforme la fonction getInstance qui prend maintenant en paramètre une clé. Si cette clé existe dans le tableau associatif, on retourne l’instance correspondante sinon on crée une nouvelle instance associée à la clé que l’on ajoute à uniqueInstance. Ainsi on peut facilement gérer un ensemble d’instances avec leurs clés associées.


Conséquences

Jusqu’à maintenant nous avons passé sous silence un problème qui peut mettre en péril l’implémentation du pattern Singleton : le multithread (capacité pour un programme de lancer plusieurs traitements simultanés c'est à dire processus). En effet, si l’on implémente de façon basique le pattern Singleton, dans le cas d’un programme multithread, on peut se retrouver avec une classe Singleton possédant plusieurs instances.

Le problème réside dans l’enchaînement des instructions. Un premier processus exécute la fonction getInstance constate que l’attribut uniqueInstance est nul. Un deuxième processus s’exécute et lui aussi constate (via getInstance) que l’attribut uniqueInstance est nul. Il va donc créer une instance et retourner celle-ci. Lorsque le premier processus va reprendre son exécution il va à son tour créer une nouvelle instance (étant donné qu’il a déjà effectué le test sur l’attribut) et retourner celle-ci. On se retrouve alors avec deux instances pour une classe Singleton.

Pour résoudre ce problème, on dispose de deux solutions. La plus simple consiste à instancier l’attribut uniqueInstance dès sa déclaration dans la classe. Ainsi l’implémentation de la méthode getInstance se limite à retourner l’attribut uniqueInstance. Cette approche est fiable et simple à mettre en place mais on perd l’instanciation à la demande.

L’autre approche consiste à s’assurer que la fonction getInstance ne pourra être exécutée que par un seul processus à la fois. Chaque langage dispose de sa spécification pour indiquer la particularité de cette méthode. Par exemple, en Java on utilisera le mot clef « synchornized ». Cette solution bien que satisfaisante, peut réduire les performances d’un programme multithread. Il existe alors des stratagèmes suivant les langages pour pallier à cette lacune.

On peut alors se demander quand doit-on mettre en place ces solutions spécifiques aux multithread. Le meilleur conseil est de toujours implémenter le pattern Singleton comme si le programme utilisait le multithread. Car même si ce n’est pas le cas aujourd’hui, il se peut que dans le futur le multithread apparaisse dans votre application.


Exemple d'implémentation

Singleton en Java : journalisation d’un compte bancaire

Commentaires

Singleton

Soumis par ould le Mardi 12/04/2011 12:38

c’est tellement bien fait que j’ai envi de te demander d’autres tuto....les threads par exemple.
merci en tt cas pour ces tutos simples et pertinents.

Singleton

Soumis par Mathieu G. le Dimanche 17/04/2011 21:46

Merci c’est ce type de remarque qui donne envie d’écrire d’autres articles.
N’hésite pas à t’inscrire à la newsletter ou au flux RSS pour être tenu informé des nouveautés à venir.
A bientôt.

Singleton

Soumis par dévJunior le Samedi 11/06/2011 17:37

Pour être honnête je ne suis pas habitué à remercier quelqu’un pour un article ou autres, mais faut dire que c’est très bien expliquer contrairement à d’autres "tutos" sur des technique de programmation ! Je te remercie, grâce à toi je sais maintenant concrètement quand et comment il faut utiliser certains patrons de conception, en espérant que tu en ajoute d’autres !!
Alors de la part de tout les développeurs qui apprennent sans dire merci, MERCI :p

Singleton

Soumis par Mathieu G. le Dimanche 19/06/2011 21:07

Bonjour,
Que dire d’autre que, merci pour ce commentaire.
Ce type de message me motive pour passer du temps à rédiger d’autres articles en sachant qu’ils vont être utile.
A bientôt.

Clair, précis et accessible.

Soumis par Ilias le Mardi 29/11/2011 21:23

Clair, précis et accessible.
Je vous remercie beaucoup, et je vous encourage à en faire d'autres patterns.
Vous êtes un bon prof :)

Merci Ilias

Soumis par Mathieu G. le Mardi 29/11/2011 21:44

Merci pour ton commentaire, c'est motivant :)
Maintenant que le nouveau site est en ligne je compte bien rajouter d'autres articles sur les patterns afin d'en faire profiter tout le monde.
A bientôt.

Remise en cause du Singleton

Soumis par David le Mardi 20/12/2011 16:37

Je ne suis pas un expert des design patterns mais j'entends depuis peu que le singleton est a bannir car il revient en fait à utiliser des variables globales.
D'ailleurs dans Zend Framework 2 tous les getInstance() ont été supprimés et remplacés par un système d'injection de dépendance.

Remise en cause du Singleton

Soumis par Mathieu G. le Mardi 20/12/2011 22:48

Merci David pour ton commentaire qui va me donner l'occasion d'éclaircir ces points.
Tout d'abord il est bien plus judicieux d'utiliser le design pattern Singleton que les variables globales pour les raisons citées dans le début de l'article.
Je peux d'ailleurs en ajouter une autre : quand on utilise une variable globale on risque un conflit de nom (si un développeur écrase par mégarde cette variable globale en y assignant un autre objet par exemple).

Cependant, réguliérement le design pattern Singleton est utilisé pour faciliter l'accessibilité à un objet depuis n'importe quel endroit du code (comme le permettrait une variable globale).
Or, même si le Singleton peut aider à répondre à cette problèmatique il n'a pas été conçu pour cela et il existe donc d'autres techniques qui répondent mieux à ce besoin.
En revanche, si le problème rencontré est d'empécher la création de plusieurs instances d'une même classe (constructeur gourmand en ressource...) le design pattern Singleton est parfait.

D'aprés ce que j'ai pu lire, l'utilisation du design pattern Singleton sera réduite dans le Zend Framework 2 mais conservée pour répondre spécifiquement au besoin cité ci-dessus.

Merci, tout d`abord pour le

Soumis par erwann le Mercredi 18/01/2012 22:56

Merci, tout d`abord pour le boulot effectué.
J'entend souvent parler de l'utilisation abusive des singleton, et je la comprend.
Mais pourrais tu donner quelques renseignements sur ces techniques qui evite de devoir recourrir au singleton.
merci

Merci, tout d`abord pour le

Soumis par Mathieu G. le Lundi 23/01/2012 22:06

Merci Erwann pour ton commentaire.
Pour éviter l'utilisation des variables globales, puisque c'etait l'origine du problème évoqué par David je propose deux pistes.
Tout d'abord il faut essayer de bien réfléchir à l'architecture de son programme afin d'éviter de lier fortement les classes et les objets (utiliser des interfaces, le design pattern fabrique...).
C'est vraiment bénéfique car on obtient un code facilement maintenable et réutilisable.
Si malgré une bonne réflexion certaines données doivent être partagées par un grand nombre de classes on peut utiliser le design pattern Registre (link is external).
Mais attention il faut vraiment limiter au maximum les données à gérer via un registre.

Woaw

Soumis par Doude le Jeudi 01/03/2012 10:05

Trop bon tuto !! La lecture se fait toute seule, les bons termes sont employés aux bons endroits, la preuve tu n'as même pas besoin de schema ou de code pour expliquer le fonctionnement du Singleton même si celui-ci reste assez simple. Je dois écrire que 2 ou 3 fois par an sur des forums et là je suis obligé de m'exprimer, je t'encourage donc vivement à continuer à faire partager ta vision des patterns parceque tu t'exprime vraiment bien, il n'y a aucune ambiguïté dans tes explications, sort nous d'autres tuto, on attend que ça !!!!!!

Woaw

Soumis par Mathieu G. le Dimanche 11/03/2012 17:28

Avec un commentaire comme celui-ci je suis forcé de continuer :)
Merci d'avoir pris le temps de donner ton avis.

Encouragement

Soumis par Houssem le Lundi 14/05/2012 21:25

un bon travail clair et simlpeMerci beacoup , votre ami Houssem developeur Tunisien :)

Encouragement

Soumis par Mathieu G. le Samedi 19/05/2012 16:06

Encore merci Houssem pour ces encouragements.
Je suis heureux de voir que mes articles profitent au plus grand nombre :)

Singleton

Soumis par Guillaume B le Jeudi 19/12/2013 09:49

Le Singleton pose un problème de taille, il est très simple d'utilisation, en revanche ils introduise un état global à l'instance (équivalent d'une variable globale). Le problème majeur de l'état global c'est qu'il peut-être modifier de n'importe où sans réellement le savoir (quand on est plusieurs sur un même dev c'est fréquent). Mais il est aussi impossible de tester (test unitaire) un état global pour les mêmes raisons. De plus le Singleton pose un problème de responsabilité. On lui donne la responsabilité de s'instancier ce qui ne respecte pas du tout les principes d'un développement SOLID (les 5 principes de base de la POO).
Aujourd'hui on parle plutôt de "Singleton" ou "singleton". Le second (avec un petit "s") n'est pas réellement un singleton mais plutôt une instance managé par une classe Manager. Il est presque aussi facile de mettre en place ce pattern Manager qu'un Singleton.
Le principe de fonctionnement d'un manager de singleton :On instancie le manager en début de programme, et on injecte cette instance partout ou il est nécessaire d'accéder aux singleton qu'il contiendra. Lors de l'instanciation du Manager on lui passe une configuration qui lui permet de faire le liens entre un appelle à une instance et une classe a instancier (exemple un tableau avec en clé le nom du singleton, en valeur le nom de sa classe), le futur singleton sera instancié sur demande (Lazzy Loading). Le Manager lorsque vous lui demanderez votre singleton, instanciera la classe s'il ne possède aucune instance, et retournera l'instance.
Pour aller plus loin, ce Manager peut-être parfaitement utilisé pour en faire des multitons aussi en changeant légèrement la logique.
Un exemple d'utilisation de ce pattern est le ServiceManager de ZF2, qui a pour rôle de fournir des instances sous forme de service accessible depuis n'importe quel controller via un ServiceLocator.

Singleton

Soumis par Anass chentouf le Jeudi 23/04/2015 09:59

Bonjour a tous,Singleton est le seul DP du GoF dont la solution tient en une seule classe. Alors pourquoi Singleton est, malgré tout, un DP au meme titre que les autres?

Singleton

Soumis par Mathieu G. le Vendredi 04/12/2015 22:10

Je justiferai ce choix par le fait que le design pattern Singleton répond à une problématique récurrente en conception et que sans connaître celui-ci la solution qu'il proprose n'est pas si simple à trouver par soi-même (bien qu'il soit composé d'une unique classe).


Ajouter un commentaire

Saisissez la lettre figurant en majuscule dans le mot "patterRns" ?

Tous les champs sont obligatoires.
La publication n'est pas instantanée, elle intervient après un processus de modération.