Pular para o conteúdo
Comportamentais

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

DataMiner.java
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.