A receita-base do pão
Toda receita de pão segue a mesma sequência: misturar, sovar, fermentar, assar. O que muda entre o pão francês e a focaccia é o recheio de cada etapa — a farinha, o tempo de fermentação, a temperatura. Nenhum padeiro assa antes de fermentar. A sequência é a receita-base; as variações preenchem os espaços.
O problema
Processos irmãos costumam compartilhar mais do que aparentam: a ordem das etapas é igual, e só o miolo de cada uma muda conforme o formato, o cliente ou o canal.
Duplicar a sequência espalha uma decisão que deveria ser única. Quando um passo novo entra (uma checagem de segurança, por exemplo), é preciso lembrar de todas as cópias.
O padrão aplica o princípio de Hollywood — 'não nos chame, nós chamamos você'. A inversão é o ponto: a base controla o fluxo e chama a subclasse nos momentos certos, em vez de a subclasse orquestrar a base. É o mesmo mecanismo que faz um framework ser framework, e não biblioteca.
A comparação com Strategy é o eixo da decisão. Template Method varia por herança, em tempo de compilação: uma subclasse por variação, e o objeto não muda de comportamento depois de criado. Strategy varia por composição, em tempo de execução: você injeta o algoritmo e pode trocá-lo. Template Method economiza cerimônia quando as variações são poucas e estáveis; Strategy paga essa cerimônia para ganhar flexibilidade.
Vale notar o parentesco com Factory Method: ele é, na prática, um Template Method cujo passo variável é 'qual objeto criar'. Reconhecer isso evita tratá-los como coisas totalmente distintas.
Passos obrigatórios, padrões e ganchos
Nem todo buraco é igual. Um passo pode ser abstrato (a subclasse é obrigada), ter implementação padrão (sobrescrever é opcional) ou ser um gancho vazio (existe só para quem quiser se pendurar).
Abstrato comunica 'sem isto o algoritmo não existe' — ler e gravar, no exemplo. O compilador cobra, o que é bom: a subclasse não nasce pela metade.
Padrão razoável cobre o caso comum e permite especializar. validar filtrando linhas vazias serve para quase todos; quem tem regra própria sobrescreve. Reduz o atrito de criar uma variação nova.
Gancho vazio (aoTerminar) não faz nada e existe para observação ou ajuste fino. É o mais barato de oferecer e o mais fácil de abusar: cada gancho é API pública que você promete manter.
A regra prática é ser econômico. Cada ponto de extensão amplia o contrato com quem estende, e reduzir ganchos depois é quebra de compatibilidade.
ImportadorRelatorio.importar()a ordem mora aqui e não é sobrescrita
1. ler()abstrato — cada formato lê do seu jeito
2. validar()padrão razoável — sobrescrever é opcional
3. gravar()abstrato — obrigatório
4. aoTerminar()gancho vazio — só para quem quiser
Com Template Method: a ordem é única
- a base declara a sequência uma vez
- subclasses preenchem só o que lhes é próprio
- passo novo entra na base e vale para todos
- o compilador cobra os passos obrigatórios
O custo é herança: a variação fica presa em tempo de compilação e a base vira contrato difícil de mudar depois que muita gente estendeu.
- 1
Escrever o esqueleto
Na base, o método-template chama os passos na ordem certa e não deve ser sobrescrito.
- 2
Abrir os buracos
Declare passos abstratos (obrigatórios), com padrão razoável ou ganchos vazios.
- 3
Preencher nas subclasses
Cada variação implementa só o que lhe é próprio — sem reordenar o fluxo.
- 4
Chamar o template
O cliente invoca importar(); a base orquestra e chama os passos no momento certo.
Navegue pelas camadas
Camada 1/3O método-template
use ← →Camada 1
O método-template
o roteiro, fechado para alteração
Implementa o algoritmo chamando os passos na ordem. Deve ser marcado como final (ou documentado como não-sobrescrevível): permitir que alguém o substitua devolve a ordem ao caos que o padrão veio resolver.
No código
importar() { ler(); validar(); gravar(); aoTerminar(); }Se violar: subclasse que sobrescreve o próprio template method esvazia o padrão — cada uma volta a ter a sua ordem.
Estrutura
Código
abstract class ImportadorRelatorio {
// O template method: a ordem e final, ninguem sobrescreve
importar(caminho: string): number {
const linhas = this.ler(caminho);
const validas = this.validar(linhas);
this.gravar(validas);
this.aoTerminar(validas.length); // gancho opcional
return validas.length;
}
// passos obrigatorios: cada formato le e grava do seu jeito
protected abstract ler(caminho: string): string[];
protected abstract gravar(linhas: string[]): void;
// passo com padrao razoavel: a subclasse sobrescreve se quiser
protected validar(linhas: string[]): string[] {
return linhas.filter((l) => l.trim().length > 0);
}
// gancho: nao faz nada por padrao
protected aoTerminar(_total: number): void {}
}
class ImportadorCSV extends ImportadorRelatorio {
protected ler(caminho: string): string[] {
return ["a,1", "b,2"]; // leria o arquivo
}
protected gravar(linhas: string[]): void {
console.log("gravando", linhas.length, "linhas");
}
}
new ImportadorCSV().importar("vendas.csv");from abc import ABC, abstractmethod
class ImportadorRelatorio(ABC):
# O template method: a ordem e fixa
def importar(self, caminho):
linhas = self.ler(caminho)
validas = self.validar(linhas)
self.gravar(validas)
self.ao_terminar(len(validas)) # gancho opcional
return len(validas)
# passos obrigatorios
@abstractmethod
def ler(self, caminho): ...
@abstractmethod
def gravar(self, linhas): ...
# passo com padrao razoavel
def validar(self, linhas):
return [l for l in linhas if l.strip()]
# gancho: nao faz nada por padrao
def ao_terminar(self, total):
pass
class ImportadorCSV(ImportadorRelatorio):
def ler(self, caminho):
return ["a,1", "b,2"]
def gravar(self, linhas):
print("gravando", len(linhas), "linhas")
ImportadorCSV().importar("vendas.csv")Casos de uso
Ciclo de vida em framework de UI
Cenário
Um componente precisa ser montado, renderizado, atualizado e destruído sempre na mesma ordem, e o autor do componente só quer dizer o que fazer em cada momento.
Como o padrão entra
O framework implementa o ciclo e chama métodos de ciclo de vida do componente nos pontos certos. É Template Method em escala: o fluxo pertence ao framework, o conteúdo ao desenvolvedor.
Trade-off: Os ganchos viram API pública e quase imutável — mudar quando um deles é chamado quebra todo o ecossistema, o que empurra frameworks a acumular métodos legados por anos.
Pipeline de importação de arquivos
Cenário
Um ERP importa planilhas de vendas em CSV, XML e um formato posicional legado; todos passam por leitura, validação, deduplicação e gravação transacional.
Como o padrão entra
A base implementa a sequência e a transação; cada formato só implementa o parser e o mapeamento. Adicionar um formato novo é uma classe pequena, sem risco de esquecer a deduplicação.
Trade-off: Quando um formato precisa de um passo extra fora da sequência (o legado exige reconciliação antes de validar), a tentação é adicionar mais um gancho — e a base incha até virar o problema.
Casos de teste com preparação comum
Cenário
Uma suíte de integração precisa subir banco, aplicar migrações, rodar o teste e limpar tudo, sempre nessa ordem, para dezenas de cenários.
Como o padrão entra
A classe-base de teste implementa setup e teardown ao redor de um método abstrato com o corpo do caso. É o padrão por trás de praticamente todo framework de teste xUnit.
Trade-off: Hierarquias de classes-base de teste tendem a crescer em profundidade, e depois de três níveis fica difícil saber qual setup rodou — a herança que economizava passa a esconder.
O template que a subclasse reescreve
Compila, roda, passa no code review. E quebra depois.
A classe base existe, mas o método-template virou hook aberto: cada subclasse redefine a ordem inteira. O padrão vira herança comum sem a invariante que justificava o esqueleto.
abstract class ImportadorRelatorio {
// O "template" deixou de ser final — cada subclasse reescreve a ordem.
importar(caminho: string): number {
return this.rodar(caminho);
}
protected abstract rodar(caminho: string): number;
}
class ImportadorCSV extends ImportadorRelatorio {
protected rodar(caminho: string): number {
const linhas = this.ler(caminho);
this.gravar(linhas); // <- validar sumiu
return linhas.length;
}
protected ler(c: string) { return ["a,1"]; }
protected gravar(_l: string[]) {}
}
// A ordem unica morreu. Cada formato tem a sua sequencia —
// e o bug "esquecemos de validar" volta a ser possivel.O que quebra
- Ao corrigir um passoA correção na base não alcança quem sobrescreveu o fluxo — as cópias divergem de novo.
- Na auditoriaUm formato 'esquece' validar e ninguém percebe, porque a ordem não é única.
- Na leituraPara entender o algoritmo, é preciso abrir cada subclasse — o esqueleto sumiu.
A saída: Marque o template como final (ou documente como não-sobrescrevível). Subclasse preenche passos e ganchos — nunca a ordem. Se a ordem precisa variar, o padrão certo é outro (Strategy).
Armadilhas
Base que vira depósito
Cada necessidade nova de uma subclasse vira um gancho a mais na base. Depois de um tempo, a classe abstrata tem quinze métodos protegidos, metade deles usada por uma única subclasse. O sinal de alerta é um gancho com um só implementador: aquilo provavelmente não pertence à base.
Herança onde cabia composição
Template Method consome a única herança disponível em linguagens sem herança múltipla e amarra a variação em tempo de compilação. Se as variações se combinam (formato × destino × política de erro), a hierarquia explode em classes cruzadas. Aí o padrão certo é Strategy: injetar as partes em vez de herdá-las.
Chamar métodos sobrescrevíveis no construtor
Se o construtor da base chama um passo que a subclasse implementa, ele executa antes de os campos da subclasse serem inicializados — e lê
undefined/nullsem erro aparente. É um bug clássico de herança, e o Template Method o convida ao juntar fluxo e hierarquia.
Quando usar × quando evitar
Use quando
3- A sequência é estável e as variações são pontuais.
- A ordem é uma invariante que não deve ficar a cargo de quem estende.
- Você está oferecendo pontos de extensão explícitos num framework.
Evite quando
3- As variações se combinam e a hierarquia explodiria — prefira Strategy.
- As subclasses precisam mudar a ordem, não só o conteúdo.
- A base já acumula ganchos usados por um implementador só.
Testar o que ficou
3 armadilhas de Template Method viram perguntas, com o nome do padrão escondido, misturadas com padrões parecidos.
Onde isto aparece de verdade
Você já usa isto, talvez sem saber o nome.
beforeEach / it / afterEachO framework de teste define o esqueleto da execução; você preenche os passos.Hooks de ciclo de vidaO framework decide quando montar, atualizar e desmontar; você diz o que acontece em cada etapa.
O padrão em uma linha
Sem nome de domínio, sem cerimônia. Só a ideia.
// Esqueleto na base; passos nas subclasses.
class Teste { run() { this.setup(); this.test(); this.teardown(); } }O que ele cobra
1 salto a mais entre a chamada e o efeito.
- A herança amarra a subclasse ao esqueleto do pai, que não muda sem impacto abaixo
- O fluxo se lê pela metade na superclasse e pela metade nas subclasses
Não vale se: os passos variáveis não compartilham um esqueleto comum — aí compor com Strategy acopla menos que herdar.