[Delphi] Design Patterns GoF – Template Method

Saudações, programadores!
Estou certo de que, em algum momento (ou vários deles), você já trabalhou com herança de classes no desenvolvimento de software. Trata-se de um recurso valiosíssimo da Orientação a Objetos que contribui para uma arquitetura de fácil manutenção através do reaproveitamento de código. O padrão de projeto Template Method está intimamente associado a este conceito. Confira!

Introdução

Nós, programadores, utilizamos herança com bastante frequência, principalmente quando uma regra de negócio é decomposta em várias regras específicas, mas parte dela é mantida em comum, compartilhada. Quando isso ocorre, geralmente criamos uma classe generalizada (ou popularmente chamada de classe base, classe pai ou superbase) e derivamos novas classes a partir dela, dando origem às classes especializadas (também conhecidas como subclasses ou classes filhas). Optamos por essa abordagem pelo motivo de que algumas classes possuem comportamentos similares e, ao invés de copiar e colar o código, o reaproveitamos.

Um caso clássico que podemos citar sobre herança envolve a recuperação de dados de uma tabela. Considere, por exemplo, em um software contábil, um método que traz os dados de documentos executando os seguintes passos:

Para recuperar dados de outra tabela, como a de impostos, teríamos a mesma sequência de passos, com exceção de que a segunda linha retornaria os dados de impostos ao invés de documentos.

Observe, então, que temos dois comportamentos em comum: conectar e desconectar o banco de dados (primeira e terceira linhas). O método ExecutarConsulta, na segunda linha, poderia ser implementado somente pelas subclasses. Cada uma executaria um tipo de consulta, enquanto o controle da conexão seria feito na classe base, já que é idêntico para qualquer consulta.

Pois bem, neste exemplo, o método ConsultarDados (que executa os três passos) pode ser considerado um Template Method! Recebe essa definição porque consiste em um método que estabelece uma sequência de passos, porém, alguns deles são “delegados” para as subclasses em tempo de execução. Em outras palavras, as subclasses ganham a liberdade de alterar o comportamento de alguns trechos do método, contanto que a sequência de passos sempre permaneça a mesma.

Para concluir o exemplo, o método ExecutarConsulta seria declarado como abstrato na classe base e implementado nas subclasses, utilizando as palavras reservadas virtual, abstract e override do Delphi. Para os desenvolvedores que já estão acostumados a trabalhar com herança, esse conceito fica bem fácil de compreender.

Embora o padrão de projeto proponha uma arquitetura de classes com base em heranças, a operação principal ocorre em um único método, que compreende uma sequência prevista de passos. É ali que tudo ocorre.

O Template Method traz uma breve semelhança com o Strategy, mas há duas diferenças importantes que os distinguem:

  • Com o Strategy, implementamos algoritmos que geram resultados semelhantes. Já com o Template Method, os resultados são sempre diferentes;
  • No Template Method, há uma classe base que executa ações compartilhadas por todas as subclasses, diferente do Strategy, no qual trabalha-se com Interfaces e cada classe possui um algoritmo específico.

Exemplo de codificação do Template Method

Dito isso, avançaremos para um exemplo prático bem interessante. Criaremos uma aplicação para consultar repositórios e usuários do GitHub através de uma API REST disponibilizada pelo próprio serviço. Conforme a documentação da API, as rotas são:

  • Repositórios: https://api.github.com/search/repositories?q={busca}
  • Usuários: https://api.github.com/search/users?q={busca}

No artigo anterior, utilizamos o TIdHTTP para receber o retorno de uma requisição GET. Dessa vez, faremos diferente. O Delphi fornece um conjunto de componentes exclusivo para esse padrão de comunicação, encontrados na paleta REST Client. Utilizaremos os componentes TRESTClient para configurar a URL base, TRESTRequest para enviar a requisição e TRESTResponse para receber a resposta em JSON.

O procedimento consiste em quatro passos: inicializar os objetos, enviar a requisição, processar o retorno e liberar os objetos da memória. Essa sequência de etapas será executada em um método chamado ConsultarDadosGitHub que, como já podemos induzir, é o nosso Template Method.

De todos os Design Patterns já apresentados até o momento, acredito que o Template Method é o que possui o menor número de elementos. Precisamos codificar apenas a Abstract Class, que declara os métodos abstratos, e as Concrete Classes, que implementam os métodos abstratos.

Abstract Class

Iniciaremos, então, pela Abstract Class. Nela, teremos a declaração do método ConsultarDadosGitHub e a maior parte dos passos já implementada. A inicialização de objetos, envio da requisição e liberação dos objetos da memória serão codificados aqui.

O destaque da Abstract Class está na existência de métodos abstratos que fazem parte da sequência de passos. No código a seguir, essa característica é dada ao método ProcessarRetorno. Cada Concrete Class será responsável por implementar este método para processar o JSON de diferentes formas.

Para facilitar a compreensão, procurei adicionar comentários na maioria das linhas:

Concrete Class

Em seguida, codificaremos a Concrete Class referente à consulta de repositórios, encarregada de implementar o método abstrato ProcessarRetorno para converter o JSON em registros de um DataSet. Além disso, no construtor, atribuímos o resource (parâmetro de busca) à variável FParametro, que será consumida pelo método EnviarRequisicao da Abstract Class.

Aproveitando o ensejo, observem a simplicidade na leitura do array JSON utilizando as classes do namespace System.JSON do Delphi.

A Concrete Class para consulta de usuários segue o mesmo padrão. Implementa o método abstrato ProcessarRetorno e define o resource no construtor:

Para finalizar, elaborei um formulário bem simples para atuar como Client:

Formulário de exemplo para uso do Template Method

Em ação!

No botão “Consultar”, basta chamar o Template Method (ConsultarDadosGitHub) da Concrete Class desejada. Como este exemplo é didático, não adicionei tratamento de exceções, mas eles são importantes!

Exemplo de uso do Template Method

Feito, pessoal!

Conclusão

Ao analisar este exemplo, a proposta do Template Method pode nos remeter à uma única necessidade: conectar e desconectar de um serviço ou um banco de dados. No entanto, estes são apenas cenários tradicionais. O Template Method é adequado para qualquer situação em que partes de um algoritmo devem ser definidos em tempo de execução como, por exemplo, a manipulação de um arquivo:

É importante destacar também que os métodos abstratos não devem necessariamente aparecer no meio do algoritmo, como no código anterior. Não existe essa uma regra. Os métodos abstratos podem surgir em qualquer posição, como intercalados:

O essencial, na verdade, é a existência um método que execute uma sequência de passos, mas alguns deles são definidos em tempo de execução por classes derivadas. Aos poucos, você notará que a implementação do Template Method é tão comum quanto parece. 🙂

O projeto de exemplo deste artigo está disponível para download no link abaixo. Neste projeto, adicionei mais alguns parâmetros na rota de busca para trazer 100 resultados, já que, por padrão, a API retorna somente 30.

 

Grande abraço, pessoal!
Até breve.


 

André Celestino