Menu
Coddy logo textTech

Patron Template Method

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 99 sur 104.

Le patron Template Method définit le squelette d'un algorithme dans une classe de base, permettant aux sous-classes de redéfinir certaines étapes spécifiques sans modifier la structure globale de l'algorithme. Contrairement au patron Strategy, qui échange des algorithmes entiers, Template Method maintient l'algorithme fixe tout en permettant la personnalisation d'étapes individuelles.

La classe de base implémente la méthode modèle (l'algorithme) et appelle des méthodes abstraites ou virtuelles que les sous-classes doivent fournir :

#include <iostream>

class DataProcessor {
public:
    // Méthode patron - définit le squelette de l'algorithme
    void process() {
        loadData();
        processData();
        saveResults();
    }
    
    virtual ~DataProcessor() = default;
    
protected:
    virtual void loadData() = 0;      // Doit être implémenté
    virtual void processData() = 0;   // Doit être implémenté
    
    // Hook (point d'ancrage) - redéfinition optionnelle avec comportement par défaut
    virtual void saveResults() {
        std::cout << "Saving to default location\n";
    }
};

class CSVProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "Loading CSV file\n";
    }
    void processData() override {
        std::cout << "Parsing CSV data\n";
    }
};

class JSONProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "Loading JSON file\n";
    }
    void processData() override {
        std::cout << "Parsing JSON data\n";
    }
    void saveResults() override {
        std::cout << "Saving to cloud storage\n";
    }
};

int main() {
    CSVProcessor csv;
    csv.process();  // Utilise saveResults par défaut
    
    JSONProcessor json;
    json.process(); // Utilise saveResults personnalisé
}

La méthode process() est la méthode patron — elle définit la séquence fixe d'étapes. Les sous-classes implémentent loadData() et processData() différemment, mais l'ordre ne change jamais. La méthode saveResults() est un « hook » — elle possède une implémentation par défaut que les sous-classes peuvent facultativement redéfinir.

Utilisez le patron Template Method lorsque vous avez un algorithme avec des étapes invariantes mais que vous avez besoin de flexibilité dans des opérations spécifiques, ou lorsque vous voulez éviter la duplication de code entre des classes similaires.

challenge icon

Défi

Facile

Construisons un système de Générateur de Rapports en utilisant le patron de conception Template Method. Vous allez créer un framework où différents types de rapports (comme les rapports de ventes et les rapports d'inventaire) suivent le même processus de génération, mais chaque type de rapport personnalise des étapes spécifiques. C'est un scénario parfait pour le Template Method — le flux de travail global reste fixe tandis que les étapes individuelles varient.

Vous organiserez votre code sur trois fichiers :

  • ReportGenerator.h : Définissez la classe de base abstraite avec la méthode template.

    Créez une classe ReportGenerator qui définit le squelette de l'algorithme de génération de rapport. Votre méthode template generateReport() doit exécuter ces étapes dans l'ordre :

    1. gatherData() — virtuelle pure, doit être implémentée par les sous-classes
    2. formatReport() — virtuelle pure, doit être implémentée par les sous-classes
    3. addHeader() — un "hook" (crochet) avec un comportement par défaut qui affiche --- Report ---
    4. printReport() — virtuelle pure, doit être implémentée par les sous-classes

    La méthode addHeader() sert de hook que les sous-classes peuvent éventuellement surcharger pour personnaliser l'en-tête.

  • Reports.h : Implémentez deux générateurs de rapports concrets.

    Créez une classe SalesReport qui :

    • gatherData() affiche Gathering sales data from database
    • formatReport() affiche Formatting sales figures
    • printReport() affiche Sales Total: $[amount] où amount est passé au constructeur

    Créez une classe InventoryReport qui :

    • gatherData() affiche Scanning inventory records
    • formatReport() affiche Organizing inventory by category
    • addHeader() surcharge le hook pour afficher === Inventory Report ===
    • printReport() affiche Items in stock: [count] où count est passé au constructeur

    Les deux classes doivent accepter leurs valeurs respectives (amount ou count) via leurs constructeurs.

  • main.cpp : Démontrez le patron Template Method.

    Lisez deux entrées :

    1. Montant des ventes (entier)
    2. Nombre d'articles en inventaire (entier)

    Créez un SalesReport avec le montant des ventes et générez-le. Ensuite, créez un InventoryReport avec le nombre d'articles et générez-le. Affichez une ligne vide entre les deux rapports pour la lisibilité.

Par exemple, avec les entrées 15000 et 250 :

Gathering sales data from database
Formatting sales figures
--- Report ---
Sales Total: $15000

Scanning inventory records
Organizing inventory by category
=== Inventory Report ===
Items in stock: 250

Avec les entrées 8500 et 120 :

Gathering sales data from database
Formatting sales figures
--- Report ---
Sales Total: $8500

Scanning inventory records
Organizing inventory by category
=== Inventory Report ===
Items in stock: 120

Remarquez comment les deux rapports suivent exactement la même séquence d'étapes définie dans generateReport(), mais chacun implémente ces étapes différemment. L'InventoryReport démontre également la surcharge de la méthode hook pour personnaliser l'en-tête, tandis que SalesReport utilise le comportement par défaut. C'est le patron Template Method en action — la structure de l'algorithme est verrouillée dans la classe de base, mais les détails sont flexibles.

Aide-mémoire

Le patron de méthode (Template Method pattern) définit le squelette d'un algorithme dans une classe de base, permettant aux sous-classes de redéfinir certaines étapes sans modifier la structure globale de l'algorithme.

La classe de base implémente la méthode modèle (l'algorithme) et appelle des méthodes abstraites ou virtuelles que les sous-classes doivent fournir :

#include <iostream>

class DataProcessor {
public:
    // Méthode modèle - définit le squelette de l'algorithme
    void process() {
        loadData();
        processData();
        saveResults();
    }
    
    virtual ~DataProcessor() = default;
    
protected:
    virtual void loadData() = 0;      // Doit être implémenté
    virtual void processData() = 0;   // Doit être implémenté
    
    // Hook (crochet) - redéfinition optionnelle avec comportement par défaut
    virtual void saveResults() {
        std::cout << "Saving to default location\n";
    }
};

class CSVProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "Loading CSV file\n";
    }
    void processData() override {
        std::cout << "Parsing CSV data\n";
    }
};

class JSONProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "Loading JSON file\n";
    }
    void processData() override {
        std::cout << "Parsing JSON data\n";
    }
    void saveResults() override {
        std::cout << "Saving to cloud storage\n";
    }
};

La méthode modèle définit la séquence fixe d'étapes. Les sous-classes implémentent les méthodes abstraites différemment, mais l'ordre ne change jamais. Les méthodes "hook" ont des implémentations par défaut que les sous-classes peuvent éventuellement redéfinir.

Utilisez le patron de méthode lorsque vous avez un algorithme avec des étapes invariantes mais que vous avez besoin de flexibilité dans des opérations spécifiques, ou lorsque vous voulez éviter la duplication de code entre des classes similaires.

Essayez vous-même

#include <iostream>
#include "Reports.h"

using namespace std;

int main() {
    // Lire les entrées
    int salesAmount;
    int inventoryCount;
    cin >> salesAmount;
    cin >> inventoryCount;
    
    // TODO: Créer un SalesReport avec salesAmount et le générer
    
    // TODO: Imprimer une ligne vide entre les rapports
    
    // TODO: Créer un InventoryReport avec inventoryCount et le générer
    
    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet