Accueil / Tutoriels / Singleton

Singleton

Publié le 23 Mai 2025
Conception

Singleton est l’un des design patterns les plus connus et aussi l’un des plus controversés quand il est mal utilisé. Il s’agit d’un design pattern de conception qui garantit qu’une classe n’a qu’une seule instance et fourni un point d’accès global à cette instance.

Principe

L’idée de base est de contrôler le cycle de vie d’une instance. Plutôt que de laisser un développeur créer des objets, c’est la classe elle-même qui décide si elle doit créer ou non une nouvelle instance.

En règle générale, on implémente une classe avec une méthode getInstance() qui va vérifier si une instance de la classe existe déjà. Si c’est le cas on retourne directement l’instance et dans le cas contraire, on crée l’instance avant de la retourner. Le constructeur de la classe singleton étant volontairement privé, il n’est pas possible d’instancier de manière classique notre classe (avec un new par exemple).

Exemples d’utilisation du pattern Singleton

L’implémentation et l’utilisation du Singleton étant très simple, je ne vais pas m’attarder sur des exemples détaillés. On peut identifier des cas fréquents d’utilisations dans les situations suivantes : gérer des configurations, instancier un service de logs et une connexion à une base de données.

Points d’attentions

Le Singleton est séduisant. Il est simple, accessible et efficace, mais il a tendance à vieillir très mal dans un projet conséquent.

Le Singleton est global et sa création est contrôlée par lui-même. Par conséquent, il est compliqué de tester une classe de manière isolée si elle dépend d’un Singleton. L’état global est partagé, ce qui le rend instable.

Chaque classe utilisant le Singleton fait un appel direct à classe::getInstance(). Elle devient dépendante de cette implémentation.

Même si on parle de design pattern, le Singleton partage les mêmes travers qu’une variable globale. Des effets de bords peuvent survenir à chaque appel, le comportement devient imprévisible si plusieurs parties du code modifient le même état. Le code peut devenir difficile à lire et à comprendre, car le comportement dépend de l’état global du Singleton à un instant T.

Implémenter un Singleton

Le design pattern Singleton repose sur trois principes fondamentaux : un constructeur privé pour empêcher l’instanciation directe (avec un new), le stockage de l’instance statique dans la classe elle-même, et une méthode publique pour récupérer cette instance (appelée souvent getInstance()). Toutefois, selon que l’on utilise un langage mono-thread ou multi-thread, certaines subtilités importantes sont à prendre en considération.

Exemple d'utilisation de Singleton en Go


package logger

import (
   "fmt"
   "sync"
)

type Logger struct{}

var instance *Logger
var once sync.Once

// GetInstance Singleton getter
func GetInstance() *Logger {
   once.Do(func() {
      instance = &Logger{}
   })
   return instance
}

// Info Une méthode pour écrire un log
func (l *Logger) Info(message string) {
   fmt.Println("[INFO]", message)
}

nb : sync.Once garantit que l’instanciation se fait une seule fois, même en présence de goroutines simultanées.


package main

import "singleton/logger"

func main() {
   log := logger.GetInstance()
   log.Info("Lancement de l'application")

   // Réutilisé ailleurs
   otherLog := logger.GetInstance()
   otherLog.Info("Toujours le même logger")
}

Le même exemple en PHP


namespace Practice\DesignPatterns\Singleton;

class Logger
{
   private static ?Logger $instance = null;

   private function __construct() {}

   private function __clone() {}
   public function __wakeup(): void {
       throw new \Exception("Cannot unserialize singleton");
   }

   public static function getInstance(): Logger {
       if (self::$instance === null) {
           self::$instance = new Logger();
       }

       return self::$instance;
   }

   public function info(string $message): void {
       echo "[INFO] $message\n";
   }
}


declare(strict_types=1);

use Practice\DesignPatterns\Singleton\Logger;

require "./vendor/autoload.php";

$log = Logger::getInstance();
$log->info("Lancement de l'application");

$otherLog = Logger::getInstance();
$otherLog->info("Toujours le même logger");

Conclusion

J'espère que grâce à cet article vous êtes désormais plus à l'aise avec le design pattern Singleton. Vous pouvez retrouver mon article sur les patrons de conception ou les autres tutoriels sur Observer, Strategy, Decorator, Chain of responsibility et Factory.