Implémentation du Design Pattern Factory Method pour le Découplage d'Objets

Le design pattern Factory Method, fréquemment désigné sous les termes de Constructeur Virtuel ou Fabrique Polymorphique, est un patron de conception de type créatif (creational pattern). Son rôle fondamental consiste à définir une interface commune pour la création d'objets, tout en déléguant la responsbailité de l'instanciation effective aux sous-classes. En d'autres termes, la classe parente de la fabrique se contente de déclarer le contrat de création, tandis que les classes filles déterminent et génèrent le produit concret approprié.

Limites de la Simple Factory et Solution

Le modèle de fabrique simple (Simple Factory) présente un défaut architectural majeur : la logique de création y est centralisée. Par conséquent, l'ajout d'un nouveau type de produit exige une modification directe de la classe fabrique existante, ce qui viole le principe ouvert/fermé (Open-Closed Principle). Le pattern Factory Method résout cette problématique en externalisant le processus d'instanciation vers des fabriques spécialisées. L'ajout d'une nouvelle entité nécessite uniquement la création d'une nouvelle sous-classe, sans altérer le code d'origine.

Structure du Pattern

  • Product (Produit) : L'interface ou la classe de base définissant le contrat des objets qui seront créés.
  • ConcreteProduct (Produit Concret) : L'implémentation spécifique du produit.
  • Factory (Fabrique) : La classe abstraite qui déclare la méthode de création (la "Factory Method").
  • ConcreteFactory (Fabrique Concrète) : La classe qui hérite de la fabrique abstraite et redéfinit la méthode pour retourner une instance du produit concret.

Avantages et Inconvénients

Avantages

  • Encapsulation : La logique d'instanciation est isolée et masquée au client. Ce dernier n'a besoin de connaître que l'interface du produit, ignorant les détails de sa création.
  • Polymorphisme : Le système repose sur des abstractions, permettant de traiter différents objets de manière totalement uniforme.
  • Extensibilité : L'intégration de nouvelles familles de produits se fait sans modifier les fabriques abstraites ou les clients existants.

Inconvénients

  • Prolifération des classes : Chaque nouveau produit nécessite la création simultanée d'une nouvelle fabrique concrète, ce qui alourdit la'rchitecture globale et le temps de compilation.
  • Complexité accrue : L'introduction de multiples niveaux d'abstraction peut rendre le code plus difficile à appréhender. De plus, l'implémentation peut parfois nécessiter l'usage de techniques avancées comme la réflexion (reflection).

Implémentation en JavaScript

Bien que JavaScript ne soit pas un langage orienté objet classique avec des classes abstraites natives au sens strict, l'utilisation des classes ES6 et de l'héritage prototypal permet d'appliquer fidèlement les concepts du Factory Method. L'astuce consiste à lever des erreurs dans les méthodes de la classe de base pour forcer leur implémentation par les sous-classes.

Exemple de base : Génération de Documents

class Document {
    render() {
        throw new Error("La méthode render() doit être implémentée.");
    }
}

class PdfDocument extends Document {
    render() {
        return "Génération du rapport au format PDF";
    }
}

class ExcelDocument extends Document {
    render() {
        return "Génération du tableur Excel";
    }
}

class HtmlDocument extends Document {
    render() {
        return "Génération de la page Web HTML";
    }
}

class DocumentCreator {
    createDocument() {
        throw new Error("La méthode createDocument() doit être implémentée.");
    }
}

class PdfCreator extends DocumentCreator {
    createDocument() {
        return new PdfDocument();
    }
}

class ExcelCreator extends DocumentCreator {
    createDocument() {
        return new ExcelDocument();
    }
}

class HtmlCreator extends DocumentCreator {
    createDocument() {
        return new HtmlDocument();
    }
}

// Utilisation du pattern
const pdfFactory = new PdfCreator();
const myPdf = pdfFactory.createDocument();
console.log(myPdf.render()); 

const htmlFactory = new HtmlCreator();
const myHtml = htmlFactory.createDocument();
console.log(myHtml.render()); 

Assemblage Complexe : Création d'un Tableau de Bord

Dans des scénarios métier plus complexes, le Factory Method permet d'encapsuler l'instanciation de plusieurs dépendances nécessaires à la construction d'un objet agrégé, réduisant ainsi drastiquement le couplage avec le code appelant.

class Widget {
    display() {
        throw new Error("Méthode display() requise");
    }
}

class BarChart extends Widget {
    constructor() {
        super();
        this.type = "Graphique à barres";
    }
    display() {
        return this.type;
    }
}

class DataTable extends Widget {
    constructor() {
        super();
        this.type = "Tableau de données";
    }
    display() {
        return this.type;
    }
}

class GeoMap extends Widget {
    constructor() {
        super();
        this.type = "Carte géographique";
    }
    display() {
        return this.type;
    }
}

class Dashboard {
    constructor(chart, table, map) {
        this.components = [chart, table, map];
    }
    
    renderDashboard() {
        console.log("Initialisation du tableau de bord avec :");
        this.components.forEach(comp => console.log(`- ${comp.display()}`));
    }
}

// Approche fortement couplée (À éviter dans une architecture évolutive)
// Le client doit connaître et instancier chaque composant manuellement
/*
const manualChart = new BarChart();
const manualTable = new DataTable();
const manualMap = new GeoMap();
const manualDashboard = new Dashboard(manualChart, manualTable, manualMap);
*/

// Approche découplée avec Factory Method
class AnalyticsDashboardCreator {
    createDashboard() {
        // L'instanciation complexe et l'ordre d'assemblage sont cachés ici
        const chart = new BarChart();
        const table = new DataTable();
        const map = new GeoMap();
        return new Dashboard(chart, table, map);
    }
}

const dashboardFactory = new AnalyticsDashboardCreator();
const analyticsBoard = dashboardFactory.createDashboard();
analyticsBoard.renderDashboard();

Étiquettes: JavaScript Design Patterns Factory Method Architecture Logicielle ES6

Publié le 27 juin à 19h09