Définition du Singleton

En mathématique le Singleton représente un ensemble constitué d’un élément unique. En POO (programmation orientée objet) le patron de conception Singleton reprend ce concept en permettant de rendre une classe statique afin de ne plus avoir besoin de l’instancier quand on l’appelle plusieurs fois. Savoir bien optimiser son code est une partie importante du métier de développeur web ou du de la programmation en général. Plus la réponse serveur est rapide, plus le site se charge rapidement dans le navigateur du client ce qui améliore le SEO et le référencement. En optimisant la mémoire RAM du serveur, ce dernier peut répondre à plus de requêtes en même temps.

Le Singleton représente une classe unique donc une classe statique
Le Singleton représente une classe unique donc une classe statique

Prenons l’exemple d’une classe Lang chargeant des fichiers de traduction et qui exploite les données qui s’y trouvent. Cette classe peut être appelée à différents niveaux de l’application, dans une vue, un contrôleur ou un module. Si à chaque fois on instancie la classe quand on en a besoin, alors on augmente forcément les ressources mémoires allouées au script en cours d’exécution. Ce design pattern améliore donc la clarté du code source et optimise la mémoire RAM allouée.

Exemple concret de ce patron de conception

Pour éviter de devoir instancier la classe à chaque utilisation, le Singleton stocke la classe dans une propriété statique afin d’y accéder à la prochaine utilisation. En théorie il suffit d’appeler une fonction statique nommée généralement getInstance qui renvoie cet attribut s’il n’est pas nul. Au premier appel de la fonction getInstance, le script détecte si la classe a déjà été créée et renvoie l’objet statique. Voici un exemple d’un Singleton PHP dans la classe Lang :

class Lang {
    private static $_instance;
    public static function getInstance():static {
        if (is_null(static::$_instance)) {
            static::$_instance = new Lang();
        }
        return static::$_instance;
    }
}
$lang = Lang::getInstance();

Un peu d’optimisation

Ça fait environ 7 lignes à ajouter au début de toutes les classes que l’on souhaite exploiter sous forme de Singleton. Nous pouvons factoriser le code en utilisant le principe des traits en PHP. Les traits sont une forme d’extension de classe pour inclure des propriétés et des méthodes communes à l’intérieur d’une classe. Pour étendre un trait, il suffit de l’appeler en utilisant le mot clé use. En développement, ce mécanisme est très pratique pour appeler facilement quelques fonctions redondantes qui sont communes à plusieurs classes.

trait Singleton {
    private static $_instance;
    public static function getInstance():static {
        if (is_null(static::$_instance)) {
            $class = static::class;
            static::$_instance = new $class;
        }
        return static::$_instance;
    }
}
class Lang {
    use Singleton;
}
$lang = Lang::getInstance();

Pour le travail en équipe

Quand on travaille à plusieurs sur un projet, il se peut qu’un des développeurs PHP oublie d’utiliser la méthode getInstance et tente d’instancier la classe. Pour éviter cela, nous pouvons ajouter la fonction checkInstance appelée dans le constructeur afin de générer une LogicException provoquant l’arrêt du programme. Cette dernière utilise debug_backtrace pour vérifier le bon ordre d’appel des fonctions.

trait Singleton {
    private static $_instance;
    public static function getInstance():static {
        if (is_null(static::$_instance)) {
            $class = static::class;
            static::$_instance = new $class;
        }
        return static::$_instance;
    }
    public function checkInstance() {
        if (!isset(debug_backtrace()[2]) || debug_backtrace()[2]['function'] != 'getInstance') {
            throw new LogicException('Use getInstance method');
        }
    }
}
class Lang {
    use Singleton;
    public function __construct() {
        self::checkInstance();
    }
}
$lang = Lang::getInstance();
$lang = new Lang(); // Provoque une Fatal error

Limite du Singleton

Pour finir, la principale limite du patron de conception Singleton se trouve dans l’héritage. Comme la classe devient une classe statique nous ne pouvons plus créer de classe enfant à cette dernière. Mais il est tout à fait possible d’appliquer le Singleton aux enfants d’une classe au lieu de la classe mère. Ce design pattern améliore donc la clarté du code et optimise la mémoire allouée au script en cours d’exécution. Il existe bien d’autres patrons de conception, mais le Singleton est peut-être le plus utilisé en programmation orientée objet.