Padrão Template Method
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 99 de 104.
O padrão Template Method define o esqueleto de um algoritmo em uma classe base, permitindo que subclasses sobrescrevam etapas específicas sem alterar a estrutura geral do algoritmo. Ao contrário do Strategy, que troca algoritmos inteiros, o Template Method mantém o algoritmo fixo enquanto permite a customização de etapas individuais.
A classe base implementa o método template (o algoritmo) e chama métodos abstratos ou virtuais que as subclasses devem fornecer:
#include <iostream>
class DataProcessor {
public:
// Template method - define o esqueleto do algoritmo
void process() {
loadData();
processData();
saveResults();
}
virtual ~DataProcessor() = default;
protected:
virtual void loadData() = 0; // Deve ser implementado
virtual void processData() = 0; // Deve ser implementado
// Hook - sobrescrita opcional com comportamento padrão
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 o saveResults padrão
JSONProcessor json;
json.process(); // Usa o saveResults personalizado
}O método process() é o método template - ele define a sequência fixa de etapas. As subclasses implementam loadData() e processData() de forma diferente, mas a ordem nunca muda. O método saveResults() é um "hook" - ele possui uma implementação padrão que as subclasses podem opcionalmente sobrescrever.
Use o Template Method quando você tiver um algoritmo com passos invariantes, mas precisar de flexibilidade em operações específicas, ou quando quiser evitar a duplicação de código entre classes semelhantes.
Desafio
FácilVamos construir um sistema de Gerador de Relatórios usando o padrão Template Method. Você criará uma estrutura onde diferentes tipos de relatórios (como relatórios de vendas e relatórios de estoque) seguem o mesmo processo de geração, mas cada tipo de relatório personaliza etapas específicas. Este é um cenário perfeito para o Template Method — o fluxo de trabalho geral permanece fixo enquanto as etapas individuais variam.
Você organizará seu código em três arquivos:
ReportGenerator.h: Define a classe base abstrata com o método template.Crie uma classe
ReportGeneratorque defina o esqueleto do algoritmo de geração de relatório. Seu método templategenerateReport()deve executar estas etapas em ordem:gatherData()— virtual pura, deve ser implementada pelas subclassesformatReport()— virtual pura, deve ser implementada pelas subclassesaddHeader()— um hook com comportamento padrão que imprime--- Report ---printReport()— virtual pura, deve ser implementada pelas subclasses
O método
addHeader()serve como um hook que as subclasses podem opcionalmente sobrescrever para personalizar o cabeçalho.Reports.h: Implementa dois geradores de relatórios concretos.Crie uma classe
SalesReportque:gatherData()imprimeGathering sales data from databaseformatReport()imprimeFormatting sales figuresprintReport()imprimeSales Total: $[amount]onde amount é passado para o construtor
Crie uma classe
InventoryReportque:gatherData()imprimeScanning inventory recordsformatReport()imprimeOrganizing inventory by categoryaddHeader()sobrescreve o hook para imprimir=== Inventory Report ===printReport()imprimeItems in stock: [count]onde count é passado para o construtor
Ambas as classes devem aceitar seus respectivos valores (amount ou count) através de seus construtores.
main.cpp: Demonstra o padrão Template Method.Leia duas entradas:
- Valor de vendas (inteiro)
- Contagem de estoque (inteiro)
Crie um
SalesReportcom o valor de vendas e gere-o. Em seguida, crie umInventoryReportcom a contagem de estoque e gere-o. Imprima uma linha em branco entre os dois relatórios para legibilidade.
Por exemplo, com as entradas 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: 250Com as entradas 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: 120Observe como ambos os relatórios seguem exatamente a mesma sequência de etapas definida em generateReport(), mas cada um implementa essas etapas de forma diferente. O InventoryReport também demonstra a sobrescrita do método hook para personalizar o cabeçalho, enquanto o SalesReport usa o padrão. Este é o padrão Template Method em ação — a estrutura do algoritmo está travada na classe base, mas os detalhes são flexíveis.
Folha de consulta
O padrão Template Method define o esqueleto de um algoritmo em uma classe base, permitindo que subclasses sobrescrevam etapas específicas sem alterar a estrutura geral do algoritmo.
A classe base implementa o método template (o algoritmo) e chama métodos abstratos ou virtuais que as subclasses devem fornecer:
#include <iostream>
class DataProcessor {
public:
// Método template - define o esqueleto do algoritmo
void process() {
loadData();
processData();
saveResults();
}
virtual ~DataProcessor() = default;
protected:
virtual void loadData() = 0; // Deve ser implementado
virtual void processData() = 0; // Deve ser implementado
// Hook - sobrescrita opcional com comportamento padrão
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";
}
};O método template define a sequência fixa de etapas. As subclasses implementam métodos abstratos de forma diferente, mas a ordem nunca muda. Métodos hook têm implementações padrão que as subclasses podem opcionalmente sobrescrever.
Use o Template Method quando você tiver um algoritmo com etapas invariantes, mas precisar de flexibilidade em operações específicas, ou quando quiser evitar a duplicação de código entre classes semelhantes.
Experimente você mesmo
#include <iostream>
#include "Reports.h"
using namespace std;
int main() {
// Ler entradas
int salesAmount;
int inventoryCount;
cin >> salesAmount;
cin >> inventoryCount;
// TODO: Criar um SalesReport com salesAmount e gerá-lo
// TODO: Imprimir uma linha em branco entre os relatórios
// TODO: Criar um InventoryReport com inventoryCount e gerá-lo
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas11Conceitos Avançados de POO
Composição vs HerançaMixins via CRTPPimpl IdiomType ErasureEnum Classes e Tipagem ForteTratamento de Exceções em POOHierarquias de Exceções Personalizadas14Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como um Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico