1. Template Method (Método Modelo) template

1.1. Definição

📘"Especifica o esqueleto de um algoritmo dentro de um método, transferindo alguns de seus passos pras subclasses. Permite que as subclasses alterem certos passos do algoritmo sem alterar a estrutura dele." [UCPP]

1.2. Aplicabilidade

Pode ser aplicado quando:

  • um método (algoritmo) possuir determinados passos definidos mas outros não; a implementação destes outros passos variar de uma subclasse para outra.

  • você começar a implementar um método em diversas subclasses e verificar que existem partes comuns (código duplicado) e outras partes que mudam de uma subclasse para outra.

1.3. Modelagem do Padrão

template method base

A classe que implementará o Template Method realmente deve ser uma classe abstrata ou uma interface do Java 8+ com um método default (que possui implementação). No diagrama, o templateMethod() é que implementa o padrão. Ele que define a estrutudo do algoritmo a ser implementado. Os métodos como operacaoPrimitivaN() são então métodos abstratos, que definem parte da implementação do algoritmo do templateMethod(). Tais operações primitivas são então chamadas em algum lugar dentro do templateMethod(). Cada subclasse pode ter sua própria implementação de cada uma destas operações.

Um projeto de exemplo para o diagrama apresentado está disponível aqui. Ele deve ser alterado para incluir as mudanças necessárias para o problema específico que você estiver resolvendo com o padrão.

1.4. Princípios utilizados

  • Open/Closed Principle (OCP): separa as partes que mudam (as operações primitivas/comportamentos) de dentro da classe que as utiliza. Isto torna a classe "aberta para extensão e fechada para modificação" (em relação aos comportamentos).

  • Single Responsibility Principle (SRP): a classe que usa os comportamentos não tem a responsabilidade de implementar estes comportamentos.

1.5. Exemplo: Leitura de Retorno de Boletos

Anteriormente aplicamos o padrão Strategy para implementar a leitura de retorno de boletos bancários. Como pudemos perceber, o código do método que lê arquivos do Banco do Brasil é muito parecido com o do Bradesco, havendo muita duplicação de código. Neste caso, podemos adicionalmente aplicar o padrão Template Method para definir uma estrutura comum para o algoritmo de leitura de arquivos de retorno, isolando as partes que alteram em métodos nas subclasses.

O código fonte do projeto convencional para leitura de arquivos de retorno está disponível aqui (zip). Tente primeiro fazer sua implementação a partir da leitura do diagrama, para depois analisar o código disponibilizado.

1.6. Strategy + Template Method

Você pode ter percebido que a modelagem do Template Method usa herança, indo contra a recomendação de Favorecer Composição no lugar de Herança. E se analisarmos tudo que foi abordado no padrão Strategy, composição traz flexibilidade.

Então, se precisarmos trocar a implementação do algoritmo específico de leitura de arquivos de retorno de um banco por outro, podemos aplicar o padrão Strategy em conjunto com o Template Method. Mas neste caso, o algoritmo a ser trocado é apenas uma parte específica de todo o algoritmo de processamento de tais arquivos. Isto porque, a parte de leitura do arquivo não muda, apenas como os dados são extraídos de cada linha obtida que sim.

1.7. Modelagem utilizando Programação Funcional

A modelagem do padrão utilizando programação funcional é extremamente simples. Teremos apenas uma classe concreta (no lugar de abstrata) e não necessariamente precisaremos de sub-classes. Assim, o diagrama básico é apenas uma única classe como abaixo.

template method funcional

O templateMethod() então deve chamar as operações primitivas internamente. Estas são definidas como atributos do tipo Function (por exemplo), indicando que eles armazenam a implementação de uma função. É a mesma lógica que utilizamos para o projeto funcional para o padrão Strategy.

O código fonte do projeto funcional para leitura de arquivos de retorno está disponível aqui (zip).

2. Modelagem convencional SEM o padrão Template Method

Uma modelagem convencional que não utiliza o Template Method é normalmente modelada como apresentado no diagrama a seguir.

no template method

Utilizando o padrão:

  • method() na superclasse é concreto;

  • as operações primitivas serão abstratas na superclasse e terão comportamentos diferentes em cada subclasse.

Sem utilizar o padrão (como é mostrado acima):

  • temos apenas o method() que é abstrato na superclasse e terão comportamentos diferentes em cada subclasse;

  • não temos operações primitivas.

Como já discutido, isso leva à duplicação de código quando as implementações do method() em cada subclasse mudam em apenas algumas partes. Caso as subclasses precisem apenas estender o method() da superclasse (adicionando operações no início ou no final do método), podemos definir uma implementação base na superclasse e apenas estender o método nas subclasses. Isto poderia ser implementado em uma subclasse simplesmente com o código abaixo:

public SubClasse1 extends ClasseAbstrata {
    @Override
    public TipoRetorno method(){
        TipoRetorno valor = super.method();
        operacaoPrimitiva1();
        operacaoPrimitiva2();
        operacaoPrimitivaN();

        return valor;
    }
}

Neste caso, estamos estendendo o método apenas executando algumas operações depois da chamada do método na superclasse. Poderíamos também executar tais operações antes e/ou depois, de acordo com as necessidades, como:

public SubClasse1 extends ClasseAbstrata {
    @Override
    public TipoRetorno method(){
        operacaoPrimitiva1();
        operacaoPrimitiva2();
        TipoRetorno valor = super.method();
        operacaoPrimitivaN();

        return valor;
    }
}

No entanto, o algoritmo base definido no method() na superclasse pode ser completamente modificado por qualquer subclasse, o que normalmente é algo indesejado. Podemos fazer isso apenas eliminando a chamada super.method(). Para isto, poderíamos simplesmente definir o method() na superclasse como final, indicando que o método não pode ser sobrescrito nas subclasses. Por outro lado, tal abordagem impede que as subclasses possam incluir mais operações como mostrado acima.

Adicionalmente, com a abordagem acima, não temos como garantir que cada operação primitiva será incluída nas subclasses no exato local onde elas precisam ser executadas ou se mesmo todas as operações esperadas serão incluídas, o que pode gerar erros e resultados incorretos. Por exemplo, imagine que a operacaoPrimitiva2() precisasse:

  • adicionar conteúdo em um arquivo que só seria criado após a chamada de super.method();

  • ou usar um valor retornado pelo super.method().

Com a implementação mostrada acima, não temos como garantir nada disso. Logo, temos que recorrer ao padrão Template Method.

3. Detalhes de Implementação

Dependendo de como você implementar o padrão, deverá estar atento a alguns detalhes. Em qualquer implementação, uma subclasse pode subescrever tal método e mudar completamente toda a implementação do algoritmo, o que é algo que normalmente queremos evitar quando usamos o padrão. Isto foi discutido na seção acima para a implementação sem o padrão.

No entanto, como as partes do algoritmo que alteram foram isoladas nas operações primitivas, as subclasses não devem modificar o templateMethod() na superclasse (veja o primeiro diagrama). Assim, com o padrão podemos definir o templateMethod() como final (o que não podemos sem o padrão). Isto impede que subclasses possam alterar a implementação dele, mas somente das operações primitivas.

4. Padrões Relacionados

Padrões que possuem similaridades ou podem ser usados em conjunto:

5. Onde o padrão é usado no JDK

Os seguintes métodos não abstratos nas classe abaixo são exemplos de uso do Template Method no JDK:

  • AbstractList.add, AbstractList.addAll, AbstractMap.putAll

  • InputStream.read, OutputStream.write, Reader.read e Writer.write.

6. Exercícios

6.1. Strategy x Template Method

Qual a diferença entre Strategy e Template Method, já que os dois estão relacionados à estrutura para implementação de algoritmos?

6.2. Processo de finalização de uma compra

Considere um sistema de loja onde você pode comprar vários itens. Do ponto de vista da loja, é então registrada uma venda que pode possuir vários itens. Ao finalizar tal venda, algumas operações devem ser finalizadas:

  1. reservar produto no estoque (para impedir que seja vendido para outro usuário);

  2. realizar pagamento;

  3. emitir nota fiscal;

  4. encaminhar solicitação para próximo setor responsável.

Dependendo se o cliente está na loja física ou virtual, algumas etapas do processo de finalização da venda podem ser diferentes ou nem existirem.

Se o cliente está na loja virtual, o processo de realização do pagamento deve ser feito online, a partir da geração de um boleto bancário ou pelo número do cartão de crédito.

Se estiver na loja física, o pagamento pode ser por:

  • dinheiro;

  • carnê para pagamento na loja ou boleto (nos dois casos, não requer dados adicionais do cliente pois tudo deve estar no cadastro dele);

  • cartão de crédito/débito físico, que deve ser inserido na máquina de cartão e o sistema deve aguardar a confirmação da compra pela máquina.

A emissão da nota fiscal deve obrigatoriamente ser eletrônica atualmente. No entanto, se a venda for pela loja física, caso o cliente não tenha email cadastrado, a nota é gerada mas não é enviada a ele. Adicionalmente, o sistema deve solicitar a impressão da nota para entrega ao cliente.

Por fim, o setor para o qual o trâmite após a venda deve seguir também muda, de acordo com a loja em que o cliente está. Na loja virtual, o pedido deve ser enviado para o setor de separação de produtos, enquanto na loja física deve ser enviado para o setor de entrega de produtos.

A partir do diagrama abaixo, implemente as classes apresentadas aplicando o padrão Template Method. Observe que apenas os relacionamentos foram definidos. Os métodos e atributos devem ser definidos por você. Os atributos não tem importância para a implementação do padrão. Assim, defina apenas o que for necessário para a sua implementação.

template method venda