Pular para o conteúdo
GoF Padrões de Projeto Comportamentais
Comportamental escopo de classe
GoF, p. 325

Método Modelo

Template Method

A superclasse fixa o esqueleto; as subclasses preenchem os buracos.

Complexidade simples Frequência de uso moderada
Intençãocomo o livro define

Definir o esqueleto de um algoritmo em uma operação, postergando alguns passos para as subclasses. Template Method permite que subclasses redefinam certos passos de um algoritmo sem mudar sua estrutura.

Analogiauma imagem do mundo real

A planta padrão do condomínio

Todas as casas do condomínio seguem a mesma planta: fundação, estrutura, paredes, telhado, acabamento — nessa ordem, sempre. O comprador escolhe o acabamento e o material das paredes. A sequência das etapas é imutável; o conteúdo de algumas delas, não.

O problema

Vários processos seguem exatamente a mesma sequência de etapas, diferindo apenas em algumas delas. O resultado é código quase duplicado entre classes, com a estrutura repetida por toda parte.

A solução

Quebre o algoritmo em etapas. Coloque na superclasse um método `template` que chama as etapas na ordem correta e é declarado final. Etapas comuns ganham implementação padrão; as que variam viram abstratas ou “hooks” opcionais.

Sintomascomo reconhecer no seu código
  • Três classes com o mesmo método, diferindo em duas linhas.
  • Copiar e colar uma sequência de etapas para criar uma variante.

Estrutura

«abstrata»MineradorDeDados+ minerar() «final»# abrir()*# extrair()*# analisar()# aoTerminar() «hook»MineradorCSV# abrir()# extrair()MineradorPDF# abrir()# extrair()MineradorAPI# abrir()# aoTerminar()
Fig. 22 — Template Method herda / implementa cria usa / contém
Ver este diagrama sendo desenhado, traço a traço
Participantes
AbstractClass
Define o método template (a ordem fixa) e declara as etapas.
ConcreteClass
Implementa as etapas abstratas e, opcionalmente, sobrescreve hooks.
Hook
Etapa com implementação vazia/padrão que a subclasse pode ou não sobrescrever.

Implementação

Listagem 22 Pipeline de importação com etapas fixas e variáveis
class ImportadorDeDados {
  // ── O TEMPLATE METHOD: a ordem é lei, ninguém sobrescreve ──
  importar(caminho) {
    const bruto      = this.ler(caminho);            // ① abstrato
    const registros  = this.analisar(bruto);         // ② abstrato
    const validos    = registros.filter(r => this.validar(r));  // ③ padrão, sobrescrevível
    const finais     = validos.map(r => this.transformar(r));   // ④ hook (identidade)
    this.persistir(finais);                          // ⑤ padrão
    this.aoConcluir(finais);                         // ⑥ hook (vazio)
    return { lidos: registros.length, importados: finais.length };
  }

  // ── Etapas OBRIGATÓRIAS (abstratas) ─────────────────
  ler(caminho)      { throw new Error('subclasse deve implementar ler()'); }
  analisar(bruto)   { throw new Error('subclasse deve implementar analisar()'); }

  // ── Etapas com PADRÃO (podem ser sobrescritas) ──────
  validar(r)     { return r != null && Object.keys(r).length > 0; }
  persistir(rs)  { console.log(`💾 gravando ${rs.length} registros`); }

  // ── HOOKS: existem só para serem opcionalmente estendidos ──
  transformar(r) { return r; }
  aoConcluir(rs) { /* vazio de propósito */ }
}

class ImportadorCSV extends ImportadorDeDados {
  ler(c)        { return 'nome,idade\nAna,30\nBruno,\nCarla,41'; }
  analisar(txt) {
    const [cab, ...linhas] = txt.split('\n');
    const cols = cab.split(',');
    return linhas.map(l => Object.fromEntries(l.split(',').map((v, i) => [cols[i], v])));
  }
  validar(r)    { return super.validar(r) && r.idade !== ''; }   // estende o padrão
  transformar(r){ return { ...r, idade: Number(r.idade) }; }
}

class ImportadorJSON extends ImportadorDeDados {
  ler(c)        { return '[{"nome":"Dan","idade":22}]'; }
  analisar(txt) { return JSON.parse(txt); }
  aoConcluir(rs){ console.log('📣 webhook disparado'); }          // usa o hook
}

console.log(new ImportadorCSV().importar('x.csv'));    // { lidos: 3, importados: 2 }
console.log(new ImportadorJSON().importar('x.json'));  // 📣 webhook … { lidos: 1, importados: 1 }
Na práticaonde ele já existe
  • java.util.AbstractList / AbstractMap
  • javax.servlet.http.HttpServlet — doGet, doPost são hooks
  • Métodos de ciclo de vida de frameworks (setUp/tearDown de testes)

Consequências

A favor
  • Elimina duplicação: a estrutura do algoritmo existe uma vez só.
  • Deixa os clientes sobrescrever apenas partes bem definidas.
  • A ordem das etapas fica protegida contra alterações acidentais.
Contra
  • Limita a flexibilidade ao esqueleto pré-definido.
  • Viola o Princípio de Substituição de Liskov se uma subclasse suprimir uma etapa padrão.
  • Quanto mais etapas, mais difícil de manter.
  • Herança acopla fortemente: a subclasse depende de detalhes internos da superclasse.
Use quando
  • Clientes precisam estender só partes específicas de um algoritmo, não sua estrutura.
  • Várias classes têm algoritmos quase idênticos, com pequenas diferenças.
Evite quando
  • Você precisa trocar o algoritmo inteiro em tempo de execução — use Strategy.
  • A hierarquia de herança já está profunda demais.

Relações com outros padrões

alternativa a
Strategy
Template Method: herança, em tempo de compilação, partes do algoritmo. Strategy: composição, em tempo de execução, o algoritmo todo.
costuma andar junto
Factory Method
Factory Method é frequentemente uma etapa dentro de um Template Method.
parecido / fácil de confundir
Builder
Ambos definem uma sequência de etapas; Builder usa composição, Template Method usa herança.

Verificação

Questãoa resposta está marcada

O que é um “hook” em Template Method?

  1. Uma etapa com implementação vazia ou padrão, que a subclasse pode sobrescrever mas não precisa
  2. Um método que a subclasse é obrigada a implementar
  3. Um callback registrado em tempo de execução
  4. O método que define a ordem das etapas

Hooks são pontos de extensão opcionais. Etapas abstratas são obrigatórias; o método template é o que define a ordem e normalmente é final.