J’aimerais prendre un moment pour parler de quelques expériences que j’ai eues autour de la refactorisation de code, et notamment à propos des Singleton.

Le Singleton est considéré comme un patron de conception, mais ça n’est d’après moi qu’une très mauvaise façon de voir les choses qui ne devrait jamais être utilisé dans aucune application.

En effet, même si son utilisation semble vous simplifier la vie dans un premier temps, elle casse en réalité toute la construction de votre application, la rendant fragile, complexe à débugger, fortement couplée et difficile à tester.

Pire, en se présentant comme un patron de conception, il vous laisse penser que vous êtes en train de faire quelque chose de bien.

Les reproches

Un des travail difficile pour un développeur est de gérer correctement les dépendances entre les différents composants de son code. Par exemple, le composant « Gestion des utilisateurs » pourra avoir besoin du composant « Base de données » pour gérer sa persistance.

Maintenant, si votre composant « Base de données » est implémenté sous forme d’un Singleton, fournir la base de données à votre gestionnaire d’utilisateurs ne sera plus nécessaire, car il accédera à la variable globale constituant votre base de données. Ainsi, le constructeur du gestionnaire d’utilisateurs ne contiendra pas de la base de données utilisée.

Un Singleton est une variable globale

Ce n’est pas une comparaison grossière ou rabaissante, mais la réalité. Un Singleton est construit à l’aide de méthode statiques et d’une classe, ainsi, il se présente comme un outil de la programmation orientée objet.

Mais en réalité, ça n’est ni plus ni moins qu’une variable globale accessible par toutes les classes de votre application.

Et d’après moi, ça n’est pas du tout dans la philosophie du monde objet. Le monde objet est un monde d’instances, un monde dans lequel chaque objet à un cycle de vie maîtrisable et dont son environnement est substituable par un autre. Autrement dit, une instance vit dans un certain contexte et ses interactions avec d’autre objets sont entièrement explicités.

Le Singleton masque les dépendances

A cause de ce système, votre composant « Gestion des utilisateurs » devient fragile. D’une part, il est fortement couplé à la base de données utilisées, et d’autre part, il n’explicite pas cette dépendance, car son instanciation ne nécessite pas la base de données.

C’est justement l’interêt du Singleton. Vous allez me dire. Mais le jour ou vous devrez refactoriser votre code, et ou vous aurez besoin d’utiliser votre gestionnaire d’utilisateurs dans un autre contexte, vous risquez de faire face à des problèmes obscurs. Car rien n’explicite qu’il faut initialiser la base de données avant de faire appel au gestionnaire d’utilisateurs. La dépendance est donc masquée dans le code de la classe, et invisible pour son utilisateur.

Un Singleton n’est pas substituable

Imaginez par exemple que vous ayez besoin d’écrire des tests unitaires, dans ces tests, vous voudrez probablement utiliser des objets faussaires (ou mock) pour faire fonctionner vos composants sans réelle base de données.

Le Singleton ne vous permet pas de faire ça. En réalité, vous pouvez imaginer ajouter une méthode permettant de substituer l’instance globale utilisée, mais cette méthode n’est pas très efficace, car cette instance sera malgré tout unique, il est donc hors de question d’utiliser deux mocks différents en même temps.

La solution

Je pense que la meilleure approche aujourd’hui est d’injecter les dépendances. Autrement dit, passer des références, que ça soit dans le constructeurs ou dans des accesseurs, vers les composants dont votre objet à besoin.

L’injection de dépendances est un mécanisme très employée aujourd’hui. Son intérêt principal est d’expliciter les dépendances entre un objet et un autre, tout en permettant à des frameworks d’automatiser leur construction à l’aide de fichier de configurations par exemple.

Si vous êtes un développeur web et que vous vous posez des questions sur les dépendances, je vous conseille fortement de regarder du coté de Pimple, un conteneur d’injection de dépendances PHP utilisé dans Symfony.

Organiser vos objets de manière à « faire circuler » les références entre les composants n’est pas toujours facile, mais c’est votre travail en tant que développeur, et une bonne gestion et explicitation de ces dernières ne fera qu’améliorer drastiquement la qualité de votre code.