Les constructeurs nommés comme alternative aux constructeurs multiples en PHP

En PHP et, contrairement à d’autre langage, il n’est pas possible d’avoir plusieurs constructeurs dans une classe. Pouvoir définir plusieurs constructeurs peut-être intéressants dans de nombreux cas, comme par exemple, pouvoir construire un objet à partir de différents types de données. Si cela n’est pas possible en PHP, il est possible d’utiliser des constructeurs nommés pour avoir un fonctionnement similaire.

Mais qu’est-ce qu’un constructeur nommé? Il s’agit d’une méthode statique que l’on va pouvoir appeler afin de construire une instance de classe. Ces méthodes ont l’avantage d’être potentiellement plus explicites que le constructeur de base parce qu’elles vont pouvoir ajouter une sémantique lors de la construction de notre objet.

Prenons un premier exemple:

readonly class Color
{
    public function __construct(
        public int $red,
        public int $blue,
        public int $green,
    ) {}
}

Le code précédent définit un objet permettant de stocker une couleur. Cette dernière est composant d’un niveau de rouge, de bleu et de vert. Le constructeur principal permet ainsi de renseigner les valeurs correspondantes. Mais dans certains cas d’utilisation, nous pourrions avoir envie de créer une couleur depuis sa valeur hexadécimale. Pour cela, nous pouvons imaginer introduire un constructeur nommé fromHexCode pour réaliser cette tâche:

readonly class Color
{
    public static function fromHexCode(string $code): self
    {
        $code = ltrim($code, '#');

        $red = hexdec(substr($code, 0, 2));
        $blue = hexdec(substr($code, 2, 2));
        $green = hexdec(substr($code, 4, 2));

        return new self($red, $blue, $green);
    }

    public function __construct(
        public int $red,
        public int $blue,
        public int $green,
    ) {}
}

Les utilisations de constructeur nommé sont multiples et permettent de simplifier la création d’objets, de fournir des valeurs par défaut en fonction d’un contexte d’utilisation tout en encapsulant la logique de création potentiellement complexe au sein de l’objet lui-même. Cela améliore ainsi la lisibilité et la compréhension du code.

C’est également une technique appréciée en Domain Driven Design pour expliciter le processus métier qui a conduit à la création de la donnée. Prenons par exemple la création d’un utilisateur dans une application, nous pourrions imaginer la modélisation suivante:

class User
{
    public static function fromRegistration(string $name, string $email, string $password): self
    {
        $user = new self($name, $email, $password);

        // additionnal business logic related to registration

        return $user;
    }

    public static function fromRegistration(string $name, string $email): self
    {
        $user = new self($name, $email, $password);

        // additionnal business logic related to social login

        return $user;
    }

    private function __construct(
        public string $name,
        public string $email,
        public ?string $password = null,
    ) {}
}

Ce dernier exemple définit une classe User avec un constructeur privé. Pour pouvoir créer une instance de notre objet, il sera nécessaire d’utiliser un des constructeurs nommés définis dans cette même classe. Le constructeur nommé à utiliser se fera en fonction du contexte métier dans lequel le traitement est effectué. Il y a ici un réel avantage à utiliser cette méthodologie, car elle permet de voir les éléments nécessaires à la création de notre utilisateur en fonction de l’action effectuée dans l’application.

Car si un utilisateur se doit d’avoir un nom, un email et potentiellement un mot de passe, le code précédent rend visible le fait qu’il est obligatoire de renseigner toutes ces informations dans le cas d’un enregistrement de l’utilisateur depuis notre projet, alors que le mot de passe n’est pas nécessaire dans le cas d’une connexion d’un réseau social.

Le concept de constructeur nommé est très puissant et peut se révéler extrêmement utile pour la maintenance et la compréhension d’une base de code. Le principe a été très brièvement introduit dans cet article et je vous recommande vivement de vous y intéresser plus en profondeur, ces derniers étant bien souvent sous-exploités.