Le Singleton, le seul, l’unique !

Logo S de superman

Le singleton, le design pattern le plus simple et le plus connu ! Il fait partie de la catégorie des patterns de création, ce qui veut dire qu’il va gérer la création des objets. Mais comment fonctionne-t-il réellement ? Et surtout à quoi ça sert concrètement ? On va voir tout ça, c’est parti !

Le Singleton

Son rôle

Le but d’un singleton est de faire en sorte de contrôler l’instanciation d’une classe de façon à ce qu’une seule et unique instance existe dans tout le programme. De plus, il doit donner la possibilité d’avoir un accès sur cette instance partout dans l’application. Ces conditions sont nécessaires pour qu’une classe se définisse comme un singleton.

Comme tout design pattern, le singleton répond à un besoin spécifique. Ici, il est bien entendu utilisé dans des applications qui ne demandent qu’une seule instance de classe. Cela peut être nécessaire par exemple si la classe perturbe le programme si plusieurs instances sont créées.

L’objet doit aussi être utilisé un peu partout dans l’application ce qui nécessite donc un point d’accès global. Enfin, l’instance de la classe doit être créée et initialisée que quand on l’utilise. Sans rentrer dans les détails, c’est le concept de lazy initialization qui est le fait justement de créer un objet la première fois où il est utilisé. Si la classe est lourde à la création, cela se résume en une perte de performance inutile si elle n’est pas utilisée.

Là, on a les critères qui nécessite l’utilisation d’un singleton, comme je l’ai indiqué dans un article précédent, utiliser les design pattern à tort et à travers peut causer plus de problèmes qu’il n’en résout, si bien entendu il en résout :-). Le singleton à la particularité d’être simple, mais c’est justement ce qui le rend dangereux, mal l’utiliser peut complexifier inutilement le code et rendre la maintenance difficile.

Utiliser un singleton que quand ces critères sont réunis et qu’il n’y a pas d’autres solutions.

Ses caractéristiques

L’implémentation du pattern peut se faire de différentes manières, tout dépend du langage de programmation utilisé tant que cela respecte les critères. Mais en général, l’implémentation suit un format standard.

Pour pouvoir limiter à un le nombre d’instances, il faut contrôler les fonctions qui permettent de créer des objets. Le moyen le plus utilisé et le plus facile est donc de rendre privés les constructeurs de la classe ce qui empêche quiconque de créer une instance de classe. Et de la même manière que pour les attributs privés et les accesseurs, la classe fournit une méthode statique accessible à tous qui va se servir du constructeur privé et renvoyer l’objet crée, il joue le rôle du constructeur public en quelque sorte.

La méthode est évidemment publique pour que tout le monde puisse s’en servir et respecter une des conditions, mais également statique ! Les méthodes statiques appartiennent aux classes et non à des objets. Elles ne peuvent donc pas utiliser les attributs des objets dits non statiques, mais peuvent être utilisées sans qu’aucune instance de classe existe. Dans notre cas, c’est parfaitement adapté.

Cette méthode va effectuer dans un premier temps un contrôle pour savoir si une instance de classe existe déjà ou non. La classe doit donc avoir la possibilité de stocker une instance de cette classe. Un attribut privé doit donc être disponible pour ça, elle doit aussi être statique, car la méthode s’en sert.

Lors de l’appel de la méthode, si une instance n’existe pas dans l’attribut statique, la méthode utilise le constructeur, stocke l’instance de la classe dans l’attribut statique et renvoie l’objet à celui qui l’a appelée. Si elle existe par contre, elle renvoie simplement l’instance de la classe contenue dans l’attribut privé.

Voilà un peu le fonctionnement de ce pattern !

Implémentation en PHP

Diagramme de classe UML pour le pattern Singleton

Exemple de l’implémentation du pattern en PHP, je n’ai mis que le code minimum d’un singleton

<?php
class Singleton
{
private static $instance; // Contient l'instance de la classe
private function __construct() { } // Constructeur en privé, empêche la création d'instances en dehors de la classe.
private function __clone() { } // Empêche la copie d'une instance.
public static function getInstance() {
// Contrôle si une instance existe
if (!isset(self::$instance)) {
self::$instance = new self;
}
return self::$instance;
}
}

Ici, on voit bien que le pattern est bien mis en place, il a toutes les caractéristiques d’un singleton. L’utilisation d’une classe singleton se fait comme tous les autres objets, il n’y a tout simplement que lors de l’initialisation de celui-ci qui est différente. Au lieu de passer par le mot-clé new , on appelle directement la méthode de la classe :

<?php
$obj = Singleton::getInstance();
$obj->methode();

On remarque que je bloque aussi la fonction __clone() , qui est une fonction spéciale de PHP et qui permet de créer une copie d’un objet, c’est en quelque sorte un constructeur de copie. Comme les constructeurs doivent être privés, c’est la raison pour laquelle je la bloque.

Pour d’autres langages, jetez un petit coup d’œil ici !

Dans quel cas ne pas l’utiliser ?

Le singleton est le plus simple des patterns, mais comme je l’ai dit aussi, le plus mal utilisé. Il est même souvent décrit comme un anti-pattern, quelque chose qu’il ne faut absolument pas faire au risque de perturber le développement d’un programme. L’anti-pattern dans ce cas n’est pas le singleton en lui-même, mais l’utilisation abusive qu’on peut en faire en pensant faire quelque chose de propre.

Le singleton est en effet souvent critiqué notamment pour sa réelle utilité dans une application. Le principe même d’avoir un point d’accès global peut être un problème dans une application orientée objet, car il perturbe le concept d’encapsulation et crée des dépendances sur les autres classes qui utilisent l’instance. À cause de ça, cela peut être gênant sur des projets complexes avec de nombreuses personnes dessus au niveau de la compréhension et de la maintenance du code.

Un autre problème est que le singleton est souvent utilisé pour remplacer des objets à portée globale ou des classes purement statiques. Le singleton a seulement l’avantage d’être initialisé à la demande comparé à un objet global qui est créé de toute manière, peu importe qu’il soit utilisé ou non. Et pour le cas des classes purement statique, en plus du lazy initialisation, cela permet de profiter de quelques avantages liés à la programmation objet comme l’héritage ou le polymorphisme. Néanmoins, le singleton rend aussi le code beaucoup plus complexe et difficile à maintenir, c’est pour ça que les 3 critères doivent être respectés avant d’en utiliser un.

En fait, la vérité c’est que le singleton n’est qu’un exemple éducatif introduisant doucement les gens dans le monde des design patterns. Il n’y a que très peu de cas concrets où le singleton est requis.

Mais il en existe quand même !

Cas concret

Bon OK, on voit bien à quoi ça sert, mais sur quoi l’utiliser ?

Et bien par exemple, un module de logs respecte ces 3 critères. On a besoin que d’un objet de ce type-là dans une application en général, on doit pouvoir enregistrer des événements un peu partout dans le programme ce qui nécessite une portée globale et enfin, si pour une quelconque raison le programme n’enregistre aucun log, l’objet n’a pas besoin d’être créée.

Une classe de connexion à une base de données respecte aussi ces conditions qui pourraient nécessiter ce pattern. Si le programme ne se sert que d’une seule et même connexion, si l’ensemble de l’application se sert de la base de données et qu’à un moment, une connexion à une base de données n’est pas obligatoire, le pattern est adapté.

Le singleton est aussi utilisé pour compléter des design pattern, plus complexes comme le builder ou le factory ou une seule instance de classe est requis tout en ayant une portée globale.

Le mot de la fin, le Singleton doit être utilisé que s’il répond obligatoirement aux problèmes cités plus haut et qu’il n’y a pas d’autre manière de faire autrement !

A++ 😎

Partager l’article :
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.