O Padrão de Projeto Template Method cria uma estrutura de algoritmo dentro de um método, de modo que alguns passos que vão estar contidos neste método, seja transferido a responsabilidade para suas classes filhas. Ou seja, o Template Method divide um algoritmo em passos, e efetua a chamada desses passos ou etapas em um método específico que será utilizado como template.
Veja no diagrama, o método templateMethod, utiliza-se de passos, method1 e method2, para criação do seu esqueleto. Só que quem vai implementar esses passos são as classes que a estender através de herança.
No artigo que fiz sobre o Padrão de Projeto Builder, utilizei um exemplo de Hamburger, nesse artigo do Padrão de Projeto Method Factory, vamos fazer uma Pizza. Imagine que você quer construir uma classe para fazer pizza só que terá duas variações, uma com ingredientes comuns e outra com ingredientes veganos. A lógica de fazer a pizza é a mesma, segue alguns passos, são eles:
- Preparar a Massa (prepareDough)
- Selecionar Ingredientes (add)
- Montar a Pizza (assemble)
- Assar a Pizza (bake)
Veja que essas ações se repetem, o que muda são as formas que se implementam estas ações. Com isso em mente, vamos criar nossa classe PizzaMaker abstrata de modo que suas filhas (comum e vegana) possam definir seus métodos concretos:
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\TemplateMethod; abstract class PizzaMaker { abstract protected function prepareDough(): void; abstract protected function addIngredients(): void; abstract protected function bake(): void; abstract protected function assemble(): void; // Template method public function makePizza(): void { $this->prepareDough(); $this->addIngredients(); $this->assemble(); $this->bake(); } }
Agora, para cada contrato assinado através dos métodos abstratos, suas filhas terão a obrigação de implementar concretamente:
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\TemplateMethod; class CommonPizza extends PizzaMaker { protected function prepareDough(): void { echo "Preparing common dough...\n"; } protected function addIngredients(): void { echo "Adding common ingredients...\n"; } protected function assemble(): void { echo "Assembling pizza...\n"; } protected function bake(): void { echo "Baking pizza...\n"; } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\TemplateMethod; class VeganPizza extends PizzaMaker { protected function prepareDough(): void { echo "Preparing vegan dough...\n"; } protected function addIngredients(): void { echo "Adding vegan ingredients...\n"; } protected function assemble(): void { echo "Assembling pizza...\n"; } protected function bake(): void { echo "Baking pizza...\n"; } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Tests\Behavioral\TemplateMethod; use Growthdev\DesignPatterns\Behavioral\TemplateMethod\CommonPizza; use Growthdev\DesignPatterns\Behavioral\TemplateMethod\VeganPizza; use PHPUnit\Framework\TestCase; final class PizzaMakerTest extends TestCase { public function testCanMakeCommonPizza(): void { $pizzaMaker = new CommonPizza(); $pizzaMaker->makePizza(); $this->expectOutputString( "Preparing common dough...\n" . "Adding common ingredients...\n" . "Assembling pizza...\n" . "Baking pizza...\n" ); } public function testCanMakeVeganPizza(): void { $pizzaMaker = new VeganPizza(); $pizzaMaker->makePizza(); $this->expectOutputString( "Preparing vegan dough...\n" . "Adding vegan ingredients...\n" . "Assembling pizza...\n" . "Baking pizza...\n" ); } }
Pontos de atenção do uso do Padrão Template Method
O Template Method é um padrão de projeto relativamente simples. Por esta razão é bastante utilizado pelos programadores. Observe que o uso de métodos abstratos, reforça o Open Closed Principle (OCP), que diz que uma classe deve ser aberta para extensões e fechada para alterações.Porém, tome cuidado para não ferir o Princípio da substituição de Liskov (LSP), que nos diz que temos que reforçar a consistência para que a classe pai ou sua classe filha possam ser usadas da mesma maneira sem erros. Ou seja, analise bem o problema para não gerar consequências como estas listadas a seguir em sua modelagem das classes:
- Sobrescrever/implementar um método que não faz nada;
- Retornar valores de tipos diferentes da classe base;
- Disparar uma exceção inesperada, etc..
Vou citar um exemplo de violão, imagine que vai ter alguma pizza que não assa, você come ela crua (é apenas um exemplo), então literalmente a classe filha estaria obrigada a implementar este método abstrato, só que não faria sentido. De resto, é um bom pattern para algumas situações em nossos projetos.
Espero que você esteja gostando desta série de artigos sobre os Padrões de Projetos. O resumo com os links para outros padrões, você encontra em:
Resumo dos Padrões de Projetos (Design Patterns)
Lembrando que todos os códigos-fonte dos exemplos, estão disponíveis no meu Github:
https://github.com/growthdev-repo/design-patterns
Espero que tenha gostado deste artigo e se você quiser ajudar o canal, compartilhe com o maior número de pessoas que você puder. Até o Próximo artigo!
Confiança Sempre!!!
Fontes:
- https://springframework.guru/gang-of-four-design-patterns/template-method-pattern/
- https://stackabuse.com/template-method-design-pattern-in-python/
- https://code.tutsplus.com/tutorials/solid-part-3-liskov-substitution-interface-segregation-principles–net-36710
- Gamma, E., Helm, R., Johnson, R., Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley, 2010.
Seja o primeiro a comentar