Um template é um documento modelo que define algumas partes que são fixas e outras partes variáveis que devem ser introduzidas quando um documento for criado a partir do template, ou seja, são lacunas que precisam ser completadas e que irão variar de documento para documento.
O template provê uma estrutura que pode ser facilmente reaproveitada, simplificando a criação de documentos.
Conceito de template
De forma similar, um Template Method é um modelo de algoritmo que possui algumas partes fixas e algumas partes variáveis.
As partes variáveis são lacunas que precisam ser completadas para que o algoritmo faça realmente sentido. As lacunas são representadas como métodos hook que podem ser implementados nas subclasses.
Caso seja uma lacuna obrigatória, o método deve ser definido como abstrato e caso a implementação seja opcional, o método pode ser concreto e normalmente possui uma implementação vazia.
O algoritmo é representado através de um método na superclasse que coordena a execução dos métodos hook.
O contexto para utilização do padrão é quando podemos separar o comportamento base, comum a todos os algoritmos, criando um template com pontos de extensão.
Usos conhecidos: os métodos-template são tão fundamentais que eles podem ser encontrados em quase todas as classes abstratas.
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 a estrutura do mesmo.
implementar as partes invariantes de um algoritmo uma só vez e deixar para as subclasses a implementação do comportamento que pode variar.
quando o comportamento comum entre subclasses deve ser fatorado e concentrado numa classe comum para evitar a duplicação de código
"refatorar para generalizar": (1) identifica as diferenças no código existente e, então, (2) separa as diferenças em novas operações. Por fim, (3) substitui o código que apresentava as diferenças por um método-template que chama uma dessas novas operações.
controlar extensões de subclasses. Você pode definir um método-template que chama métodos hook em pontos específicos, permitindo extensões somente nesses pontos.
Metamodelo original Template Method
Modelo de aplicação Template Method
AbstractClass:
define operações primitivas abstratas que as subclasses concretas definem para implementar passos de um algoritmo.
implementa um método-template que define o esqueleto de um algoritmo.
O método-template invoca operações primitivas, bem como operações definidas em AbstractClass ou ainda outros objetos.
ConcreteClass: implementa as operações primitivas para executarem os passos específicos do algoritmo na subclasse.
ConcreteClass depende de AbstractClass para implementar os passos invariantes do algoritmo.
public abstract class AbstractClass {
public void metodoTemplate() {
operacao1();
operacao2();
}
protected abstract void operacao1();
protected abstract void operacao2();
}
public class ConcreteClass extends AbstractClass {
public void operacao1() {
System.out.println("Operação primitiva 01");
}
public void operacao2() {
System.out.println("Operação primitiva 02");
}
}
A estrutura da superclasse define os métodos que são chamados, a ordem de execução e o que é retornado por eles, em uma única classe que será usada pelo cliente para executar os algoritmos.
Cada algoritmo específico é uma subclasse que herda dessa superclasse e sobrescreve os métodos para implementar sua própria lógica.
Os métodos-template chamam os seguintes tipos de operações:
operações concretas (ou em ConcreteClass ou em classes-clientes);
operações concretas de AbstractClass (isto é, operações que são úteis para subclasses em geral);
operações primitivas (isto é, operações abstratas);
métodos fábrica (ver Factory Method);
operações-gancho (métodos hook) que fornecem comportamento-padrão que subclasses podem estender se necessário. Uma operação-gancho frequentemente não executa nada por padrão.
É importante para os métodos-template especificar quais operações podem ser redefinidas e quais são operações abstratas devem ser redefinidas (Ver Modificadores de Métodos). Para reutilizar uma classe abstrata efetivamente, os codificadores de subclasses devem compreender quais as operações são projetadas para redefinição (boa documentação).
Os métodos-template são uma técnica fundamental para a reutilização de código. São particularmente importantes em bibliotecas de classe porque são os meios para a fatoração dos comportamentos comuns. Por existir apenas uma única classe para chamar os algoritmos, o código de utilização fica o mesmo para qualquer que seja a implementação do lado cliente.
Os métodos-template conduzem a uma estrutura de inversão de controle, algumas vezes chamada de “o princípio de Hollywood”: “não nos chame, nós chamaremos você”. Isso se refere a como uma classe-mãe chama as operações de uma subclasse, e não o contrário.
Caso um novo comportamento (subclasse) surja e precise de um novo gancho, basta definir um método vazio na superclasse para que os comportamentos já existentes não precisem ser modificados.
Assim, além de ser fácil adicionar novos comportamentos, também é fácil realizar mudanças no algoritmo base e incorporar novos requisitos à solução.
Infelizmente, é fácil esquecer de chamar a operação herdada.
A herança "é uma carta que só pode ser jogada uma vez". Isso significa que uma classe que precise de comportamentos de duas outras classes só poderá fazer o uso da herança para uma delas.
Métodos template podem ser difíceis de manter para algoritmos com muitos passos.
Strategy: Métodos-template usam a herança para variar parte de um algoritmo, enquanto estratégias usam a delegação para variar o algoritmo inteiro. Assim como no Strategy, o Template Method também é um padrão comportamental que busca simplificar as responsabilidades dos objetos. O problema a ser resolvido é que temos um conjunto de algoritmos que, apesar de seguir um mesmo fluxo, precisa de alguma flexibilidade.
Factory Method: métodos fábrica são frequentemente chamados por métodos-template.
O padrão de projeto Template Method é um padrão comportamental que define o esqueleto de um algoritmo em uma superclasse base, permitindo que as subclasses possam redefinir partes específicas do algoritmo sem alterar a sua estrutura geral.
O padrão é útil quando se deseja definir um conjunto de comportamentos comuns e de comportamentos variáveis, mantendo um esquema geral de execução desses comportamentos.
O uso do padrão Template Method permite que as subclasses forneçam a implementação específica de certas etapas da sequência, sem alterar a estrutura básica do algoritmo. Isso simplifica a criação de novas subclasses e ajuda a garantir que o algoritmo seja executado corretamente em todas as subclasses.
Extrair Classe
Método template
Para aplicar o padrão de projeto Template Method em um projeto, você pode seguir os seguintes passos:
Identificar as operações que devem ser executadas em uma sequência fixa e que podem ser implementadas de maneira diferente em cada subclasse.
Criar uma classe abstrata que declare essas operações em uma sequência fixa, implementando as operações comuns a todas as subclasse.
Criar métodos abstratos para as operações que devem ser implementadas de maneira diferente em cada subclasse.
Criar subclasses que estendam a classe abstrata e implementem as operações abstratas de acordo com as necessidades de cada classe.
Criar o método template (método modelo) será o método que implementa a sequência fixa e chama as operações abstratas e concretas necessárias para executar a ação completa. Considere definir o método template como final para evitar que as subclasses sobrescrevam este método.
Testar a funcionalidade do método template, verificando a execução dele para cada subclasse.
Refatorar o código, se necessário, para garantir que o padrão Template Method esteja sendo aplicado corretamente e de forma clara.
Nesse exemplo, a classe ExameSaude é a classe abstrata que define o Template Method realizarExame(). Essa classe define as etapas que todos os exames de saúde devem seguir, mas deixa a implementação detalhada das etapas para as subclasses concretas ExameSangue e ExameUrina. Essas subclasses implementam os métodos abstratos prepararPaciente(), coletarAmostras() e processarAmostras() que definem as particularidades de cada exame específico.
// Esta é a AbstractClass do padrão Template Method.
public abstract class ExameSaude {
// Método template
public final void realizarExame() {
prepararPaciente(); // Comportamento variável
coletarAmostras(); // Comportamento variável
processarAmostras(); // Comportamento variável
emitirResultado(); // Comportamento comum
}
protected abstract void prepararPaciente(); // Método gancho
protected abstract void coletarAmostras(); // Método gancho
protected abstract void processarAmostras(); // Método gancho
private void emitirResultado() {
System.out.println("Apresentando os dados do resultado do exame...");
}
}
// Esta é uma ConcreteClass do padrão Template Method.
public class ExameSangue extends ExameSaude {
@Override
protected void prepararPaciente() {
System.out.println("Apresenta as instruções de preparo para o exame de urina.");
}
@Override
protected void coletarAmostras() {
System.out.println("Coletando amostra de sangue...");
}
@Override
protected void processarAmostras() {
System.out.println("Processando amostra de sangue...");
}
}
// Esta é uma ConcreteClass do padrão Template Method.
public class ExameUrina extends ExameSaude {
@Override
protected void prepararPaciente() {
System.out.println("Apresenta as instruções de preparo para o exame de urina.");
}
@Override
protected void coletarAmostras() {
System.out.println("Coletando amostra de urina...");
}
@Override
protected void processarAmostras() {
System.out.println("Processando amostra de urina...");
}
}