Template Method
Define o esqueleto de um algoritmo em um método, adiando alguns passos para subclasses. Template Method permite que subclasses redefinam certos passos de um algoritmo sem mudar sua estrutura.
Intenção
Definir a estrutura geral de um algoritmo em um método de classe base, permitindo que subclasses sobrescrevam passos específicos. As partes invariantes do algoritmo ficam na classe base, e as partes variantes são adiadas para implementações de subclasse.
Problema
Você tem várias classes que contêm algoritmos quase idênticos, com apenas pequenas diferenças em certos passos. Duplicar o algoritmo inteiro em cada classe viola o DRY e torna a manutenção dolorosa — uma correção de bug ou mudança estrutural precisa ser aplicada em cada cópia.
Solução
Mova a estrutura de algoritmo compartilhada para um único método template em uma classe base abstrata. Divida o algoritmo em passos, tornando abstratos (ou sobrescrevíveis) os passos que variam. Subclasses implementam apenas os passos que diferem, herdando a estrutura fixa da classe base. Métodos hook opcionais podem fornecer pontos de extensão adicionais com comportamento padrão vazio.
Participantes
- AbstractClass — define o método template contendo o esqueleto do algoritmo; declara operações primitivas abstratas que subclasses precisam implementar; pode definir métodos hook com comportamento padrão
- ConcreteClass — implementa as operações primitivas para executar os passos específicos da subclasse no algoritmo
Vantagens
- Elimina duplicação de código ao concentrar a estrutura comum do algoritmo em uma classe base
- Subclasses podem sobrescrever apenas os passos específicos que precisam mudar
- Inversão de controle (Princípio de Hollywood) — a classe base chama métodos da subclasse, não o contrário
- Métodos hook fornecem pontos de extensão opcionais sem exigir que subclasses os sobrescrevam
Desvantagens
- Depende de herança, o que pode tornar o design mais rígido comparado a alternativas baseadas em composição
- Subclasses ficam restritas à estrutura de algoritmo definida na classe base
- Pode ser difícil entender o fluxo quando há muitos passos e hooks
- Adicionar novos passos ao método template pode quebrar subclasses existentes
Analogia do mundo real
Construir uma casa segue um template method. O plano geral é o mesmo: lançar a fundação, erguer as paredes, instalar o encanamento, adicionar o telhado, e finalizar os interiores. Diferentes estilos de casa (colonial, moderno, cabana) seguem a mesma sequência mas customizam passos específicos — o tipo de fundação, os materiais das paredes, e o design do telhado variam. Um arquiteto define o template, e cada construtor preenche os detalhes.
Casos de uso
- Parsers de dados — parsers de CSV, JSON, e XML compartilham o mesmo pipeline parse-validate-transform mas diferem na lógica de parsing
- Geradores de relatório — estrutura comum (cabeçalho, corpo, rodapé) com renderização específica de formato
- IA de jogos — um turno consiste em fases fixas (avaliar, planejar, agir) com lógica diferente por tipo de personagem
- Sistemas de build — passos de compilar, linkar, e empacotar seguem a mesma ordem mas variam por linguagem
- Frameworks de teste — ciclo de vida setup, execute, verify, teardown com lógica de teste definida pelo usuário
- Web scrapers — buscar página, extrair dados, transformar resultados com extração específica do site
Exemplos de código
import java.util.List;
import java.util.Map;
abstract class DataMiner {
// Template method -- defines the algorithm skeleton
public final void mine(String path) {
String raw = openSource(path);
List<String> data = extractData(raw);
List<Map<String, Double>> parsed = parseData(data);
String analysis = analyzeData(parsed);
generateReport(analysis);
}
protected abstract String openSource(String path);
protected abstract List<String> extractData(String raw);
protected abstract List<Map<String, Double>> parseData(List<String> data);
// Shared step with default implementation
protected String analyzeData(List<Map<String, Double>> data) {
double total = data.stream()
.mapToDouble(row -> row.getOrDefault("value", 0.0))
.sum();
return "Total records: " + data.size() + ", Sum of values: " + total;
}
// Hook -- subclasses can optionally override
protected void generateReport(String analysis) {
System.out.println("[Report] " + analysis);
}
}
final class CsvMiner extends DataMiner {
@Override
protected String openSource(String path) {
return "csv-content-of:" + path;
}
@Override
protected List<String> extractData(String raw) {
return List.of("10", "20", "30");
}
@Override
protected List<Map<String, Double>> parseData(List<String> data) {
return data.stream()
.map(v -> Map.of("value", Double.parseDouble(v)))
.toList();
}
}
final class JsonMiner extends DataMiner {
@Override
protected String openSource(String path) {
return "json-content-of:" + path;
}
@Override
protected List<String> extractData(String raw) {
return List.of("15", "25");
}
@Override
protected List<Map<String, Double>> parseData(List<String> data) {
return data.stream()
.map(v -> Map.of("value", Double.parseDouble(v)))
.toList();
}
@Override
protected void generateReport(String analysis) {
System.out.println("[JSON Report] " + analysis);
}
}
public class TemplateMethodDemo {
public static void main(String[] args) {
DataMiner miner = new CsvMiner();
miner.mine("sales.csv");
// [Report] Total records: 3, Sum of values: 60.0
}
}Um framework de mineração de dados onde o template method define o pipeline fixo (abrir, extrair, parsear, analisar, reportar) e CsvMiner/JsonMiner implementam os passos específicos de formato.
Padrões relacionados
Strategy
Define uma família de algoritmos, encapsula cada um deles, e os torna intercambiáveis. Strategy permite que o algoritmo varie independentemente dos clientes que o usam.