Pattern Template Method
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 99 di 104.
Il pattern Template Method definisce lo scheletro di un algoritmo in una classe base, consentendo alle sottoclassi di sovrascrivere passaggi specifici senza modificare la struttura complessiva dell'algoritmo. A differenza di Strategy, che sostituisce interi algoritmi, Template Method mantiene fisso l'algoritmo consentendo di personalizzare i singoli passaggi.
La classe base implementa il metodo template (l’algoritmo) e chiama metodi astratti o virtuali che le sottoclassi devono fornire:
#include <iostream>
class DataProcessor {
public:
// Metodo template - definisce la struttura dell'algoritmo
void process() {
loadData();
processData();
saveResults();
}
virtual ~DataProcessor() = default;
protected:
virtual void loadData() = 0; // Deve essere implementato
virtual void processData() = 0; // Deve essere implementato
// Hook - ridefinizione facoltativa con comportamento predefinito
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(); // Usa saveResults predefinito
JSONProcessor json;
json.process(); // Usa saveResults personalizzato
}Il metodo process() è il metodo modello: definisce la sequenza fissa di passaggi. Le sottoclassi implementano loadData() e processData() in modo diverso, ma l’ordine non cambia mai. Il metodo saveResults() è un «hook»: ha un’implementazione predefinita che le sottoclassi possono facoltativamente sovrascrivere.
Usa Template Method quando hai un algoritmo con passaggi invarianti ma hai bisogno di flessibilità in operazioni specifiche, oppure quando vuoi evitare la duplicazione del codice tra classi simili.
Sfida
FacileCreiamo un sistema di generazione di report usando il pattern Template Method. Creerai un framework in cui diversi tipi di report (come i report sulle vendite e quelli sull'inventario) seguono lo stesso processo di generazione, ma ogni tipo di report personalizza passaggi specifici. È uno scenario perfetto per il Template Method: il flusso di lavoro complessivo rimane fisso, mentre i singoli passaggi variano.
Organizzerai il codice in tre file:
ReportGenerator.h: definisci la classe base astratta con il metodo template.Crea una classe
ReportGeneratorche definisca lo scheletro dell'algoritmo di generazione dei report. Il tuo metodo templategenerateReport()dovrebbe eseguire questi passaggi nell'ordine indicato:gatherData(): virtuale pura, deve essere implementata dalle sottoclassiformatReport(): virtuale pura, deve essere implementata dalle sottoclassiaddHeader(): un hook con comportamento predefinito che stampa--- Report ---printReport(): virtuale pura, deve essere implementata dalle sottoclassi
Il metodo
addHeader()funge da hook che le sottoclassi possono, facoltativamente, sovrascrivere per personalizzare l'intestazione.Reports.h: implementa due generatori di report concreti.Crea una classe
SalesReportche:gatherData()stampaGathering sales data from databaseformatReport()stampaFormatting sales figuresprintReport()stampaSales Total: $[amount], dove amount viene passato al costruttore
Crea una classe
InventoryReportche:gatherData()stampaScanning inventory recordsformatReport()stampaOrganizing inventory by categoryaddHeader()sovrascrive l'hook per stampare=== Inventory Report ===printReport()stampaItems in stock: [count], dove count viene passato al costruttore
Entrambe le classi dovrebbero accettare i rispettivi valori (amount o count) tramite i propri costruttori.
main.cpp: mostra il pattern Template Method.Leggi due valori in input:
- Importo delle vendite (intero)
- Quantità di articoli in inventario (intero)
Crea un
SalesReportcon l'importo delle vendite e generane il report. Poi crea unInventoryReportcon la quantità di articoli in inventario e generane il report. Stampa una riga vuota tra i due report per una maggiore leggibilità.
Ad esempio, con gli input 15000 e 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: 250Con gli input 8500 e 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: 120Nota come entrambi i report seguono esattamente la stessa sequenza di passaggi definita in generateReport(), ma ciascuno li implementa in modo diverso. Inoltre, InventoryReport mostra come sovrascrivere il metodo hook per personalizzare l'intestazione, mentre SalesReport usa quella predefinita. Ecco il pattern Template Method in azione: la struttura dell'algoritmo è fissata nella classe base, ma i dettagli sono flessibili.
Provalo tu
#include <iostream>
#include "Reports.h"
using namespace std;
int main() {
// Leggi i valori di input
int salesAmount;
int inventoryCount;
cin >> salesAmount;
cin >> inventoryCount;
// TODO: Crea un SalesReport con salesAmount e generarlo
// TODO: Stampa una riga vuota tra i report
// TODO: Crea un InventoryReport con inventoryCount e generarlo
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online