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.
Défi
FacileConstruisons 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
ReportGeneratorqui définit le squelette de l'algorithme de génération de rapport. Votre méthode templategenerateReport()doit exécuter ces étapes dans l'ordre :gatherData()— virtuelle pure, doit être implémentée par les sous-classesformatReport()— virtuelle pure, doit être implémentée par les sous-classesaddHeader()— un "hook" (crochet) avec un comportement par défaut qui affiche--- Report ---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
SalesReportqui :gatherData()afficheGathering sales data from databaseformatReport()afficheFormatting sales figuresprintReport()afficheSales Total: $[amount]où amount est passé au constructeur
Créez une classe
InventoryReportqui :gatherData()afficheScanning inventory recordsformatReport()afficheOrganizing inventory by categoryaddHeader()surcharge le hook pour afficher=== Inventory Report ===printReport()afficheItems 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 :
- Montant des ventes (entier)
- Nombre d'articles en inventaire (entier)
Créez un
SalesReportavec le montant des ventes et générez-le. Ensuite, créez unInventoryReportavec 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: 250Avec 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: 120Remarquez 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;
}
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
1Fondamentaux de la POO
Fichiers externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d'accès en C++Spécificateurs d'accès en profondeurMasquage d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l'opérateur d'affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif - Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique