Tu développes une appli, tu gères des connexions à une base de données, tu centralises des logs… et tu te retrouves avec plusieurs objets qui font exactement la même chose en mémoire. Résultat : des bugs, des incohérences, et des ressources gaspillées. Le design pattern Singleton est là pour régler ce problème.
Dans cet article, tu vas découvrir :
- ce qu’est un Singleton et pourquoi il existe
- comment il fonctionne concrètement, avec un principe ultra simple
- des exemples d’implémentation en Java et en Python
- les différences entre lazy et eager initialization
- les enjeux du multithreading et comment rendre un Singleton thread-safe
- les limites et les pièges à connaître avant de l’utiliser partout
Que tu sois développeur junior, étudiant en informatique ou simplement curieux de comprendre les grands classiques du code, ce guide t’apporte toutes les clés pour maîtriser le Singleton de A à Z.
Qu’est-ce que le design pattern singleton ?
Un design pattern, c’est un modèle de conception réutilisable en programmation orientée objet. Le Singleton fait partie des patterns les plus connus et les plus utilisés, quel que soit le langage : Java, Python, C#, PHP, C++…
Son principe tient en une phrase : garantir qu’une classe ne produit qu’un seul et unique objet pendant toute la durée de vie du programme, et fournir un point d’accès global à cet objet.
Concrètement, peu importe combien de fois ton code appelle cette classe, il récupère toujours la même instance en mémoire. Pas de doublon, pas de copie, un seul objet partagé par tout le programme.
Les cas d’usage typiques sont nombreux :
- connexion à une base de données : une seule connexion partagée, pour éviter de multiplier les ouvertures coûteuses
- fichier de configuration : un accès global et unique aux paramètres de l’application
- logger : centraliser tous les messages de journalisation au même endroit
- jeux vidéo : gérer un objet joueur unique ou un gestionnaire de ressources graphiques
À chaque fois, la logique est la même : il n’y a aucune raison d’avoir plusieurs instances, et en créer plusieurs causerait des problèmes.
À quoi sert un singleton en programmation ?
Le Singleton répond à un besoin très concret : éviter les incohérences quand plusieurs parties du code accèdent à une même ressource. Si ton application ouvre cinq connexions à la base de données alors qu’une seule suffit, tu gaspilles de la mémoire et tu risques des conflits.
Avec un Singleton, tu obtiens :
- une cohérence des données → tout le programme travaille sur le même objet
- une optimisation des ressources → pas de création inutile d’objets lourds
- un accès simplifié → n’importe quelle partie du code peut récupérer l’instance via une méthode unique
C’est particulièrement utile pour les ressources système partagées, les pools de connexion ou les registres de configuration. Le Singleton agit comme un point de vérité unique dans ton application.
Comment fonctionne le singleton (principe simple)
Le mécanisme repose sur deux idées :
- Empêcher la création de plusieurs objets
- Centraliser l’accès à un objet unique
Pour y arriver, on utilise trois éléments :
- Un constructeur privé : il bloque l’utilisation de
newdepuis l’extérieur de la classe. Personne ne peut créer un objet directement. - Une variable statique : elle stocke l’unique instance de la classe.
- Une méthode publique (souvent appelée
getInstance()) : c’est le seul moyen d’accéder à l’objet.
Le fonctionnement est limpide : quand on appelle getInstance(), la méthode vérifie si l’instance existe déjà. Si non, elle la crée et la stocke. Si oui, elle retourne celle qui existe. C’est tout.
Exemple d’implémentation du singleton en Java
Voici un Singleton classique en Java, commenté ligne par ligne :
java
public class Singleton {
// Variable statique qui stocke l'unique instance
private static Singleton instance;
// Constructeur privé → impossible de faire new Singleton()
private Singleton() {}
// Méthode publique pour récupérer l'instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Ce qui se passe ici :
- Le constructeur est privé : aucun code extérieur ne peut instancier la classe
- La variable
instanceest statique : elle appartient à la classe, pas à un objet - La méthode
getInstance()vérifie siinstanceestnullavant de créer l’objet - Tous les appels à
getInstance()retournent exactement le même objet
Tu peux vérifier ça facilement :
java
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println(a == b); // true → même objet en mémoire
Implémenter un singleton en Python (méthode simple)
Python offre une grande flexibilité pour créer un Singleton. La méthode la plus courante consiste à redéfinir __new__, la méthode qui contrôle la création d’objets :
python
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
Explication :
_instancestocke l’unique objet de la classe__new__est appelée avant__init__à chaque instanciation- Si aucune instance n’existe, on en crée une. Sinon, on retourne celle qui existe déjà
Tu peux vérifier avec la fonction id() :
python
a = Singleton()
b = Singleton()
print(id(a) == id(b)) # True → même adresse mémoire
Petit fait intéressant : Python embarque déjà des Singletons natifs. Les objets None, True et False sont des instances uniques. Quand tu écris x is None, tu compares littéralement à l’unique objet None en mémoire.
Singleton lazy vs eager : quelles différences ?
Il existe deux grandes approches pour créer l’instance d’un Singleton. Le choix dépend du contexte et des contraintes de ton projet.
| Critère | Lazy (création différée) | Eager (création immédiate) |
|---|---|---|
| Moment de création | À la première utilisation | Au chargement de la classe |
| Consommation mémoire | Économe (créé si besoin) | Consomme dès le départ |
| Thread-safety | Nécessite une gestion manuelle | Thread-safe par défaut |
| Complexité | Plus de code à écrire | Plus simple à implémenter |
| Risque | Bugs possibles en multithreading | Objet créé même s’il n’est jamais utilisé |
Le lazy Singleton (celui montré plus haut en Java) attend qu’on appelle getInstance() pour créer l’objet. C’est économe, mais ça peut poser des problèmes si plusieurs threads appellent la méthode en même temps.
Le eager Singleton crée l’instance directement à la déclaration :
java
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
Ici, l’objet est créé dès que la JVM charge la classe. Pas besoin de vérification, pas de risque de double création. C’est plus simple et naturellement thread-safe.
Particularité en Java : les classes sont chargées à leur première utilisation, ce qui rend le eager Singleton assez proche d’un lazy en pratique. Attention, ce comportement peut varier selon le langage (en C++ par exemple, c’est différent).
Singleton et multithreading : comprendre le thread-safe
C’est le piège classique du Singleton. Imagine deux threads qui appellent getInstance() exactement au même moment, alors que l’instance n’existe pas encore. Les deux voient instance == null, et les deux créent un objet. Résultat : deux instances au lieu d’une. Le pattern est cassé.
Pour éviter ça, plusieurs solutions existent en Java :
La synchronisation simple avec synchronized :
java
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
Ça fonctionne, mais chaque appel à getInstance() est verrouillé, ce qui ralentit les performances.
La double vérification (double-checked locking), plus efficace :
java
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
Ici, le verrou ne s’applique que lors de la première création. Les appels suivants passent directement sans synchronisation. C’est le meilleur compromis entre sécurité et performance.
La solution la plus simple reste le eager Singleton : l’instance est créée au chargement, donc aucun thread ne peut provoquer de conflit. Si ton objet n’est pas gourmand en ressources, c’est souvent le choix le plus raisonnable.
Un dernier point à garder en tête : si ton Singleton contient des données modifiables (mutable state), la thread-safety de la création ne suffit pas. Il faut aussi synchroniser l’accès aux données elles-mêmes, sinon tu t’exposes à des bugs difficiles à tracer.
En résumé : le Singleton est un outil puissant, mais il ne doit pas devenir un réflexe. Utilise-le quand une seule instance est réellement nécessaire, et privilégie l’injection de dépendances dans les projets modernes. Les frameworks actuels (Spring, Django, etc.) gèrent souvent les instances à ta place, ce qui rend le Singleton “manuel” de moins en moins indispensable.

Décodeur de l’ère numérique, Léo explore l’univers du business et des nouvelles technologies pour vous livrer des contenus clairs, concrets et inspirants. Qu’il s’agisse d’intelligence artificielle, d’entrepreneuriat ou d’outils no-code, il vous aide à rester à la page et surtout à prendre une longueur d’avance.
