Singleton : guide complet pour développeurs juniors
Famille : Créationnel · Série : Design Patterns GoF · Article 2/24 · Popularité : #1 sur 23
Le Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global contrôlé.
En une phrase
Le Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global contrôlé.
Le problème sans ce pattern
Tu charges la configuration .env à plusieurs endroits avec new Config(). Résultat : flags incohérents, double connexion, tests impossibles à isoler.
// Anti-pattern : plusieurs instances
const a = new AppConfig();
const b = new AppConfig();
// a et b peuvent diverger si le constructeur relit le disque
Symptômes dans ton code
- Fichiers qui grossissent à chaque nouvelle variante.
- Tests difficiles : trop de mocks ou d'effets de bord cachés.
- Tu as peur de toucher une classe car « tout dépend de tout ».
L'idée du pattern Singleton
Cache le constructeur et expose getInstance() (lazy) ou exporte un module unique (pattern module ES).
| Rôle | Responsabilité |
|---|---|
Singleton |
Instance unique + accès global |
| Client | Appelle getInstance() au lieu de new |
| Rôle | Responsabilité |
|---|---|
| Singleton | Stocke l'instance statique, constructeur privé |
| Client | Utilise getInstance() |
Analogie du quotidien
Comme le maire d'une ville : une seule personne occupe le poste ; les services administratifs passent par lui, on n'en nomme pas un nouveau par formulaire.
Exemple complet en TypeScript
class AppConfig {
private static instance: AppConfig | null = null;
private constructor(
public readonly apiUrl: string,
public readonly env: 'dev' | 'prod',
) {}
static getInstance(): AppConfig {
if (!AppConfig.instance) {
AppConfig.instance = new AppConfig(
import.meta.env.VITE_API_URL ?? 'http://localhost:3000',
(import.meta.env.MODE === 'production' ? 'prod' : 'dev') as 'dev' | 'prod',
);
}
return AppConfig.instance;
}
}
const a = AppConfig.getInstance();
const b = AppConfig.getInstance();
console.log(a === b); // true
Ce qu'il faut retenir du code
- Le client dépend d'abstractions, pas de détails partout.
- Chaque nouvelle variante = nouvelle classe (ou module), pas un
ifde plus. - Nomme tes types pour le métier (noms métier explicites, pas
Strategy1).
Exemple en Python
class AppConfig:
_instance: "AppConfig | None" = None
def __init__(self, api_url: str, env: str) -> None:
if AppConfig._instance is not None:
raise RuntimeError("Utilise AppConfig.get()")
self.api_url = api_url
self.env = env
@classmethod
def get(cls) -> "AppConfig":
if cls._instance is None:
cls._instance = cls("https://api.example.com", "prod")
return cls._instance
Quand utiliser Singleton
- Ressource réellement unique (pool de connexions maîtrisé, identifiant machine).
- Coût d'initialisation élevé partagé par toute l'application.
Quand ne pas utiliser Singleton
- Tests unitaires : l'état global pollue les tests parallèles.
- Plusieurs instances légitimes (un panier par utilisateur).
- En TypeScript moderne : préfère un module
config.tsexporté ou l'injection de dépendances.
Erreurs fréquentes des juniors
- Singleton « par défaut » sur toutes les classes.
- God Object : tout l'état métier dans l'instance unique.
- Oublier le thread-safety en Java/C# (double-checked locking).
Patterns proches
- Factory Method : Délègue la création sans imposer une instance unique
- Injection de dépendances : Passe Config en paramètre — plus testable
Dans le monde réel
Node.js : certains drivers utilisent un pool singleton. En front, évite le singleton DOM ; préfère un store (Zustand, Pinia) avec un seul provider.
Questions fréquentes (FAQ)
C'est obligatoire en entretien ? Non — on teste surtout ta capacité à reconnaître le problème. Le nom Singleton aide à communiquer en équipe.
Ça remplace les frameworks ? Non — React, Express ou Spring implémentent souvent ces idées pour toi. Comprendre Singleton te permet de les utiliser correctement.
Je dois tout refactoriser ? Non — applique le pattern quand la douleur est réelle (nouveaux bugs à chaque feature).
Mini test unitaire (idée)
// Exemple de test : mocke les collaborateurs, vérifie le comportement public
describe('Singleton', () => {
it('fonctionne avec une variante', () => {
// Arrange → Act → Assert
});
});
Adapte ce squelette à ton framework (Jest, Vitest, pytest).
Pas à pas : implémenter en 5 étapes
- Nomme le problème — est-ce vraiment Singleton ?
- Dessine les rôles sur papier (client, abstraction, implémentations).
- Écris un test qui décrit le comportement attendu.
- Implémente une variante — valide avant d'en ajouter d'autres.
- Documente en équipe — « ici on utilise Singleton parce que… ».
Checklist code review
- [ ] Le client ne dépend pas de classes concrètes inutiles
- [ ] Pas de sur-abstraction sur un cas unique
- [ ] Tests sur chaque variante / handler / état
- [ ] Nommage métier clair
Exercice pratique (25–35 min)
Remplace ton Logger.getInstance() par un logger injecté dans chaque service. Écris 2 tests : avec mock, sans état partagé.
Résumé
Une instance, un accès — utile pour de vraies ressources uniques ; dangereux comme variable globale déguisée.
Navigation dans la série
- Précédent : Introduction
- Suivant : Factory Method