Mis à jour le
Dans le paysage des patrons de conception, le Singleton occupe une place à part. Décrit en 1994 dans le livre de référence du « Gang of Four » (Design Patterns, de Gamma, Helm, Johnson et Vlissides), c’est un patron de création dont le but tient en une phrase : garantir une instanciation unique d’une classe et fournir un point d’accès global à cet objet. Sa mise en œuvre soulève pourtant de vraies questions de gestion mémoire, de concurrence et d’architecture.
En Java, C++ et PHP, le design pattern s’écrit différemment, mais le principe est le même. On l’utilise typiquement pour un système de journalisation, une configuration centrale, un cache ou un accès à une ressource matérielle. Mal employé, il devient en revanche une variable globale déguisée, qui complique les tests et couple fortement le code.
Exemple typique : une application qui écrit ses logs dans un fichier. Si chaque module crée son propre objet de journalisation, plusieurs objets écrivent dans le même fichier sans se coordonner. Avec un Singleton, tous les modules passent par le même objet, qui gère seul l’accès au fichier.
En bref
- Le Singleton limite une classe à une seule instance, accessible partout dans le programme.
- Son intérêt est fort pour les ressources partagées : logs, configuration, cache, connexion à une base.
- Le principe repose sur un constructeur privé et une méthode statique du type getInstance.
- La concurrence peut fragiliser une implémentation naïve si elle n’est pas protégée.
- Des exemples de code en Java, C++ et PHP montrent des variantes adaptées à chaque langage.
- Ce motif de création doit être utilisé avec mesure, car il peut masquer une architecture trop couplée.
Singleton design pattern : le principe d’une instance unique en Java, C++ et PHP
Un Singleton empêche de créer plusieurs objets là où un seul suffit, et donne à tous les composants un accès au même objet. Il ressemble à une variable globale, avec deux différences : l’objet n’est créé que par la classe elle-même, et il peut n’être créé qu’au premier usage.
Dans un journal d’opérations, par exemple, deux instances pourraient écrire des événements dans un ordre incohérent ou ouvrir deux fois la même ressource. Une instance unique simplifie la cohérence.
Pourquoi une seule instance change la logique d’une application
La question n’est pas seulement technique, elle est aussi architecturale. Pourquoi vouloir un seul objet ? Parce que certaines ressources n’ont aucun intérêt à être répliquées, voire deviennent dangereuses si elles le sont. Un gestionnaire d’impression, un cache de configuration ou un service de journalisation sont des exemples classiques.
Côté code client, on ne fabrique plus l’objet soi-même : on passe par une méthode unique, souvent nommée getInstance.
Pour approfondir l’univers des motifs de conception et leurs usages, un détour par cette lecture sur les usages du Singleton chez les développeurs éclaire bien la logique de centralisation.
Le mécanisme du constructeur privé et de getInstance
La mécanique classique tient en trois éléments. Le constructeur est privé, ce qui interdit new en dehors de la classe. Une méthode statique vérifie si l’instance existe déjà, la crée au besoin, puis renvoie toujours le même objet.
L’instance est conservée dans un attribut statique de la classe, relu à chaque appel.
| Élément | Rôle dans le Singleton | Effet recherché |
|---|---|---|
| Constructeur privé | Bloque l’instanciation directe | Empêche la création sauvage d’objets |
| Méthode statique getInstance | Fournit l’accès à l’objet | Offre un point d’entrée unique |
| Attribut statique | Stocke l’unique instance | Assure la réutilisation |
| Initialisation paresseuse | Crée l’objet au premier besoin | Évite un coût inutile au démarrage |
Cette version simple a un défaut : si plusieurs threads appellent getInstance en même temps, ils peuvent créer chacun une instance. C’est le problème de concurrence traité plus bas.
Exemples de code du Singleton en Java, C++ et PHP
Voici des exemples de code courts, dans la forme la plus idiomatique de chaque langage.
Dans chaque cas, on retrouve une classe fermée à la création directe, une instance stockée et un accès contrôlé ; seules changent les garanties offertes par le langage.
Singleton en Java : une instance centrale et lisible
En Java, la version la plus simple et sûre initialise l’instance au chargement de la classe : private static final Logger INSTANCE = new Logger();, un constructeur private Logger() {} et public static Logger getInstance() { return INSTANCE; }. La JVM garantit que l’initialisation d’une classe est faite une seule fois, même avec plusieurs threads.
Pour une création au premier appel, l’idiome du « holder » (une classe interne statique qui contient l’instance) offre la même garantie sans verrou. La double vérification avec verrou est aussi possible, mais l’attribut doit alors être déclaré volatile, faute de quoi elle n’est pas fiable.
Enfin, une énumération à un seul élément, public enum Logger { INSTANCE; }, est la forme recommandée par Joshua Bloch dans Effective Java : elle résiste aussi à la sérialisation et à la réflexion, qui peuvent sinon créer une seconde instance.
Singleton en C++ : contrôle précis et vigilance sur la gestion mémoire
En C++, la gestion mémoire demande plus d’attention. La forme la plus utilisée aujourd’hui est le « Singleton de Meyers » : une méthode statique qui contient une variable locale statique, static Logger& instance() { static Logger inst; return inst; }. Elle renvoie une référence plutôt qu’un pointeur, ce qui évite de gérer un delete.
L’objet est créé au premier appel et détruit automatiquement à la fin du programme. Depuis C++11, la norme garantit que l’initialisation d’une variable locale statique est thread-safe : pas besoin de verrou. Le point délicat reste l’ordre de destruction, si d’autres objets statiques utilisent le Singleton pendant leur propre destruction.
Il faut aussi interdire les copies, sinon une seconde instance peut apparaître : Logger(const Logger&) = delete; et Logger& operator=(const Logger&) = delete;, en plus d’un constructeur privé.
Singleton en PHP : centraliser sans alourdir l’application
En PHP, on retrouve le même trio : private static ?self $instance = null;, un constructeur privé et public static function getInstance(): self { return self::$instance ??= new self(); }. On rend aussi __clone() privé pour empêcher la duplication par clone.
Une particularité importante : en PHP classique, chaque requête HTTP s’exécute dans un contexte neuf. Le Singleton ne vit donc que le temps d’une requête ; il évite d’ouvrir plusieurs connexions à la base pendant cette requête, mais ne partage rien d’une requête à l’autre. Les frameworks modernes préfèrent d’ailleurs un conteneur d’injection de dépendances, qui fournit un service « partagé » sans Singleton codé en dur.
Pour relier cette logique à d’autres usages concrets, un second éclairage utile se trouve dans cet article sur le rôle du Singleton dans l’outillage des équipes, qui montre comment la centralisation peut aussi servir la maintenance.
Concurrence, erreurs courantes et limites du Singleton design pattern
En mode multithread, deux threads peuvent tester en même temps que l’instance n’existe pas encore, puis la créer chacun de leur côté. L’unicité n’est alors plus garantie.
Les parades dépendent du langage, mais reviennent à s’assurer qu’un seul thread peut créer l’objet :
- Initialisation dès le chargement : simple et sûre, mais moins souple si l’objet n’est finalement jamais utilisé.
- Synchronisation de getInstance : efficace contre les doubles créations, au prix d’un léger impact sur les performances.
- Double vérification : réduit le coût du verrou, mais exige en Java un attribut
volatilepour être correcte. - Tests unitaires : plus difficiles, car l’état de l’instance persiste d’un test à l’autre et on ne peut pas la remplacer facilement par un faux objet.
Le Singleton peut aussi masquer un couplage excessif : chaque classe qui appelle getInstance() dépend directement d’une implémentation précise, sans que cette dépendance apparaisse dans sa signature.
Quand le pattern devient une fausse bonne idée
Si un service est utilisé partout, la question à se poser est souvent celle de l’organisation du projet, plutôt que celle de l’accès global.
L’alternative la plus courante est l’injection de dépendances : on crée une seule instance au démarrage et on la passe aux classes qui en ont besoin. On garde l’unicité sans l’accès global, et les tests peuvent fournir un faux objet.
Multiton, variantes et cas d’usage du patron de conception
Autour du Singleton gravitent plusieurs variantes, dont le Multiton. Ici, l’idée change subtilement : il ne s’agit plus d’une seule instance absolue, mais d’une instance par clé. On conserve le principe de contrôle, tout en autorisant plusieurs objets, chacun associé à un identifiant précis.
Cette logique convient, par exemple, à une connexion par base de données ou à un objet par région. Un tableau associatif sert de registre : si la clé existe, l’instance est renvoyée ; sinon, elle est créée et stockée.
| Patron | Nombre d’instances | Cas d’usage fréquent |
|---|---|---|
| Singleton | Une seule | Journalisation, configuration, connexion partagée |
| Multiton | Une par clé | Gestion multi-clients, ressources segmentées |
| Façade | Variable | Interface simplifiée pour sous-systèmes complexes |
| Poids mouche | Plusieurs, avec états partagés | Optimisation d’objets nombreux et légers |
Le Singleton se combine aussi avec d’autres motifs. Une Façade est souvent implémentée comme un Singleton, puisqu’un seul point d’entrée suffit. Les fabriques abstraites, les monteurs et les prototypes peuvent eux aussi l’utiliser.
Attention enfin aux systèmes distribués : un Singleton est unique par processus, pas pour toute l’application. Avec plusieurs serveurs ou plusieurs workers, il y aura autant d’instances que de processus ; pour une ressource vraiment unique, il faut un mécanisme externe (base de données, verrou distribué).
Le Singleton est-il adapté à toutes les applications ?
Non. Il convient surtout aux ressources partagées qui doivent rester uniques, comme une configuration centrale, un logger ou une connexion commune. Lorsqu’il devient un contournement pratique à un problème d’architecture, il vaut mieux envisager une autre solution.
Pourquoi le constructeur doit-il être privé ?
Le constructeur privé empêche les créations directes et force le code client à passer par la méthode statique getInstance. C’est ce verrou qui permet de contrôler l’unicité de l’objet.
Le Singleton pose-t-il un problème en concurrence ?
Oui, si l’implémentation est naïve. Deux threads peuvent créer deux instances au même moment. Il faut donc protéger l’accès ou initialiser l’objet de manière sûre selon le langage utilisé.
Quels sont les inconvénients les plus fréquents ?
Le motif peut compliquer les tests, masquer un couplage excessif et rendre certains projets moins flexibles. Il faut donc l’employer avec discernement, surtout dans les architectures évolutives.
Le Singleton existe-t-il différemment en Java, C++ et PHP ?
Le principe reste le même, mais les détails changent. En Java, on privilégie l’initialisation statique ou l’énumération ; en C++, la variable locale statique (thread-safe depuis C++11) et l’interdiction des copies ; en PHP, une propriété statique, sachant que l’instance ne vit que le temps d’une requête.





