Patrón Template Method
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 99 de 104.
El patrón Template Method define el esqueleto de un algoritmo en una clase base, permitiendo que las subclases sobrescriban pasos específicos sin cambiar la estructura general del algoritmo. A diferencia de Strategy, que intercambia algoritmos completos, Template Method mantiene el algoritmo fijo mientras permite la personalización de pasos individuales.
La clase base implementa el método de plantilla (el algoritmo) y llama a métodos abstractos o virtuales que las subclases deben proporcionar:
#include <iostream>
class DataProcessor {
public:
// Método de plantilla - define el esqueleto del algoritmo
void process() {
loadData();
processData();
saveResults();
}
virtual ~DataProcessor() = default;
protected:
virtual void loadData() = 0; // Debe ser implementado
virtual void processData() = 0; // Debe ser implementado
// Hook - anulación opcional con comportamiento por defecto
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 por defecto
JSONProcessor json;
json.process(); // Usa saveResults personalizado
}El método process() es el método de plantilla: define la secuencia fija de pasos. Las subclases implementan loadData() y processData() de manera diferente, pero el orden nunca cambia. El método saveResults() es un "hook" (gancho): tiene una implementación predeterminada que las subclases pueden sobrescribir opcionalmente.
Utiliza Template Method cuando tengas un algoritmo con pasos invariantes pero necesites flexibilidad en operaciones específicas, o cuando quieras evitar la duplicación de código en clases similares.
Desafío
FácilVamos a construir un sistema de Generador de Informes utilizando el patrón Template Method. Crearás un marco de trabajo donde diferentes tipos de informes (como informes de ventas e informes de inventario) siguen el mismo proceso de generación, pero cada tipo de informe personaliza pasos específicos. Este es un escenario perfecto para el Template Method: el flujo de trabajo general permanece fijo mientras que los pasos individuales varían.
Organizarás tu código en tres archivos:
ReportGenerator.h: Define la clase base abstracta con el método de plantilla.Crea una clase
ReportGeneratorque defina el esqueleto del algoritmo de generación de informes. Tu método de plantillagenerateReport()debe ejecutar estos pasos en orden:gatherData()— virtual puro, debe ser implementado por las subclasesformatReport()— virtual puro, debe ser implementado por las subclasesaddHeader()— un "hook" con comportamiento por defecto que imprime--- Report ---printReport()— virtual puro, debe ser implementado por las subclases
El método
addHeader()sirve como un "hook" que las subclases pueden opcionalmente sobrescribir para personalizar el encabezado.Reports.h: Implementa dos generadores de informes concretos.Crea una clase
SalesReportque:gatherData()imprimaGathering sales data from databaseformatReport()imprimaFormatting sales figuresprintReport()imprimaSales Total: $[amount]donde amount se pasa al constructor
Crea una clase
InventoryReportque:gatherData()imprimaScanning inventory recordsformatReport()imprimaOrganizing inventory by categoryaddHeader()sobrescriba el "hook" para imprimir=== Inventory Report ===printReport()imprimaItems in stock: [count]donde count se pasa al constructor
Ambas clases deben aceptar sus respectivos valores (amount o count) a través de sus constructores.
main.cpp: Demuestra el patrón Template Method.Lee dos entradas:
- Monto de ventas (entero)
- Cantidad de inventario (entero)
Crea un
SalesReportcon el monto de ventas y genéralo. Luego crea unInventoryReportcon la cantidad de inventario y genéralo. Imprime una línea en blanco entre los dos informes para mayor legibilidad.
Por ejemplo, con las entradas 15000 y 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 las entradas 8500 y 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: 120Observa cómo ambos informes siguen exactamente la misma secuencia de pasos definida en generateReport(), pero cada uno implementa esos pasos de manera diferente. El InventoryReport también demuestra la sobrescritura del método "hook" para personalizar el encabezado, mientras que SalesReport utiliza el predeterminado. Este es el patrón Template Method en acción: la estructura del algoritmo está bloqueada en la clase base, pero los detalles son flexibles.
Hoja de referencia
El patrón Template Method define el esqueleto de un algoritmo en una clase base, permitiendo que las subclases sobrescriban pasos específicos sin cambiar la estructura general del algoritmo.
La clase base implementa el método de plantilla (el algoritmo) y llama a métodos abstractos o virtuales que las subclases deben proporcionar:
#include <iostream>
class DataProcessor {
public:
// Método de plantilla - define el esqueleto del algoritmo
void process() {
loadData();
processData();
saveResults();
}
virtual ~DataProcessor() = default;
protected:
virtual void loadData() = 0; // Debe ser implementado
virtual void processData() = 0; // Debe ser implementado
// Hook - sobrescritura opcional con comportamiento por defecto
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";
}
};El método de plantilla define la secuencia fija de pasos. Las subclases implementan los métodos abstractos de manera diferente, pero el orden nunca cambia. Los métodos hook tienen implementaciones por defecto que las subclases pueden sobrescribir opcionalmente.
Utilice Template Method cuando tenga un algoritmo con pasos invariantes pero necesite flexibilidad en operaciones específicas, o cuando desee evitar la duplicación de código en clases similares.
Pruébalo tú mismo
#include <iostream>
#include "Reports.h"
using namespace std;
int main() {
// Leer las entradas
int salesAmount;
int inventoryCount;
cin >> salesAmount;
cin >> inventoryCount;
// TODO: Crear un SalesReport con salesAmount y generarlo
// TODO: Imprimir una línea en blanco entre los informes
// TODO: Crear un InventoryReport con inventoryCount y generarlo
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico