Polar Code 🎭

Command Palette

Search for a command to run...

05
Pièce N°05

CHAPITRE 4 : ENJEUX DE LA CRÉATION D’OBJETS

L'odeur du café brûlé traînait encore dans l'air. Je fixais la ligne de code la plus innocente, la plus dangereuse du monde objet.

const service = new PaiementService();

Trois lettres. new. Un opérateur. Une habitude. Une porte ouverte à tous les couplages, toutes les rigidités. La création d'objets, c'est le moment où tout se joue. Où on lie son destin à une implémentation, ou bien où on garde une porte de sortie. C'est là, dans cet instant furtif entre l'inexistence et l'existence, que les architectures se font ou se défont.

Problèmes liés à new

new, c'est l'injonction directe. Le "Fais-le, maintenant, avec ça". C'est le couteau sans manche.

  1. L'instanciation concrète : Vous épousez une classe spécifique. Pour la vie ? Souvent. Changer d'implémentation plus tard signifiera une chirurgie lourde.
    class Rapporteur {
        generateRapport() {
            const exporteur = new ExportExcel(); // Mariage avec Excel.
            // Demain, on veut du PDF ? Bon courage.
            return exporteur.exporter(this.donnees);
        }
    }
    
  2. La dissémination : La connaissance de comment construire l'objet est éparpillée partout dans le code. Si la construction change (un nouveau paramètre, une validation), c'est une chasse aux new qui commence. Une traque épuisante.
    // Dans un service...
    const validateur = new ValidateurCommande(/* règles */);
    // Dans un contrôleur...
    const autreValidateur = new ValidateurCommande(/* mêmes règles ? */);
    // Dans un script batch...
    const encoreValidateur = new ValidateurCommande(/* règles différentes ? */);
    // Changer la signature du constructeur sera un cauchemar.
    
  3. Le manque de flexibilité : Que se passe-t-il si l'objet doit être partagé ? S'il a besoin d'un état particulier après création ? new ne sait pas faire ça. Il est binaire : existence ou néant.

Dépendances concrètes

new crée une dépendance concrète, directe, matérielle. C'est le ciment qui colle vos modules entre eux. Au début, ça tient. Puis ça durcit. Et un jour, c'est le bloc monolithique, impossible à déplacer, à tester, à changer.

class ServiceFacturation {
    private repository: RepositoryFacture;

    constructor() {
        // DÉPENDANCE CONCRÈTE.
        // ServiceFacturation connaît l'existence ET la construction de RepositoryFactureSQL.
        this.repository = new RepositoryFactureSQL(
            'localhost',
            5432,
            'user',
            'password' // Les secrets qui fuient dans le code.
        );
    }

    async facturer(commande: Commande) {
        const facture = new Facture(commande);
        await this.repository.sauvegarder(facture); // Lié à SQL pour l'éternité.
    }
}

// Test unitaire ? Impossible sans une base de données réelle.
// Changer de BDD ? Réécrire la classe.
// Injecter un mock ? Non. Le couplage est dans le constructeur, c'est trop tard.

Le ServiceFacturation est maintenant fusionné avec RepositoryFactureSQL. Ils ne font qu'un. L'effet "changement" est garanti : touchez à l'un, l'autre saigne.

Variantes d’objets

La vie n'est pas binaire. Les objets ont des familles, des déclinaisons.

  1. Familles liées : Parfois, on ne crée pas un objet seul, mais une famille cohérente. Un bouton et une barre de défilement dans un thème "Windows", ou dans un thème "macOS". Utiliser new partout, c'est s'assurer que le bouton Windows se retrouve avec la barre macOS. Un mélange grotesque.
    // Sans contrôle, le chaos règne.
    const bouton = new BoutonStyleWindows();
    const barre = new BarreDeDefilementStyleMac(); // Incohérence UI.
    const menu = new MenuStyleLinux(); // Le mélange des genres.
    
  2. Objets complexes : Un Commande n'est pas juste new Commande(). C'est un client, des lignes, une adresse, des promotions, un statut. Sa construction est un algorithme, avec des étapes, des validations. Le mettre dans un constructeur de 15 paramètres, c'est signer son arrêt de mort.
    // L'horreur absolue. Le constructeur "téléscope".
    const commande = new Commande(
        clientId,
        [ligne1, ligne2],
        adresseLivraison,
        adresseFacturation,
        modePaiement,
        codePromo,
        /* ... 8 autres paramètres ... */
    );
    // Qui se souvient de l'ordre ? Que faire si un paramètre est optionnel ?
    

Cycle de vie

La création, c'est un début. Ensuite, l'objet vit, peut-être est-il partagé, peut-être doit-il être recréé dans un état identique. new est muet sur tout ça.

  1. Singleton ou pas ? : Est-ce que cet objet doit être unique pour toute l'application ? Un cache, une connexion à une base de données ? new le créera partout, gaspillant ressources et incohérence.
    // Chaque appel crée une NOUVELLE connexion. Catastrophique.
    function getDonnees() {
        const db = new DatabaseConnection(); // 1000 appels = 1000 connexions.
        return db.query('...');
    }
    
  2. Gestion des pools : Certains objets sont lourds à créer (connexions, threads). Il faut les réutiliser, les pooler. new ne gère pas les pools.
  3. Reconstruction d'état : Comment recréer un objet exactement dans l'état où il était à un instant T ? Pour une annulation (undo), une sauvegarde/restauration ? new avec les paramètres initiaux n'y suffit pas.

Je me suis levé, faisant craquer la chaise. L'écran affichait maintenant une douzaine d'occurrences de new, surlignées en rouge par l'IDE. Chacune était un point de durcissement, un minuscule engagement vers la rigidité.

Les patterns Créationnels du Gang of Four ne sont pas là pour être "plus jolis". Ils sont des réponses chirurgicales à ces enjeux précis.

  • Vous avez une dépendance concrète qui vous étouffe ? Factory Method, Abstract Factory vous aident à introduire une abstraction.
  • Vous avez un objet complexe à construire étape par étape ? Builder est fait pour ça.
  • Vous avez besoin de contrôler strictement l'unicité ou le cycle de vie ? Singleton, Pool (bien que non-GoF) pointent leur nez.
  • Vous voulez copier un prototype existant plutôt que de tout recommencer ? Prototype.

Ils ne suppriment pas new. Ils le maîtrisent. Ils l'encapsulent, le canalisent, le rendent décision plutôt qu'accident.

La création n'est pas un détail. C'est le premier acte, celui qui détermine tous les autres. Mal gérée, elle vous laisse avec un système grippé, dépendant, fragile. Bien gérée, elle donne la souplesse, la testabilité, la maintenabilité.

Le prochain chapitre ouvrirait le premier tiroir. Celui de la famille Créationnelle. On commencerait par le plus simple, le plus pernicieux aussi : le Singleton. Un pattern dont tout le monde parle, que tout le monde utilise, et que presque tout le monde salit.

Mais pour l'instant, il fallait se souvenir de cette évidence : dans le monde du code, on est responsable de ce qu'on crée. Et la façon dont on le crée détermine à quel point on sera responsable, plus tard, de ses actes.