3.12. Método Modelo (Template Method)

3.12.1. Objetivo

Método Modelo é um Padrão de Projeto Comportamental.

Talvez você já tenha encontrado ele muitas vezes. A ideia é deixar subclasses deste modelo abstrato “terminar” o comportamento de um algoritmo.

Também conhecido como o “Princípio de Hollywood”: “Não nos chame, nós chamamos você”. Esta classe não é chamada pelas subclasses, mas o inverso. Como? Com abstração do curso.

Em outras palavras, este é um esqueleto de um algoritmo, bem adequado às bibliotecas do framework. O usuário que apenas que implementar um método e a superclasse faz o trabalho.

Ele é uma forma fácil de desacoplar classes concretas e reduzir o copia-cola, este é o motivo de encontrar ele em todo lugar.

3.12.2. Diagrama UML

Alt TemplateMethod UML Diagram

3.12.3. Código

Você também pode encontrar este código no GitHub

Journey.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\TemplateMethod;
 6
 7abstract class Journey
 8{
 9    /**
10     * @var string[]
11     */
12    private array $thingsToDo = [];
13
14    /**
15     * This is the public service provided by this class and its subclasses.
16     * Notice it is final to "freeze" the global behavior of algorithm.
17     * If you want to override this contract, make an interface with only takeATrip()
18     * and subclass it.
19     */
20    final public function takeATrip()
21    {
22        $this->thingsToDo[] = $this->buyAFlight();
23        $this->thingsToDo[] = $this->takePlane();
24        $this->thingsToDo[] = $this->enjoyVacation();
25        $buyGift = $this->buyGift();
26
27        if ($buyGift !== null) {
28            $this->thingsToDo[] = $buyGift;
29        }
30
31        $this->thingsToDo[] = $this->takePlane();
32    }
33
34    /**
35     * This method must be implemented, this is the key-feature of this pattern.
36     */
37    abstract protected function enjoyVacation(): string;
38
39    /**
40     * This method is also part of the algorithm but it is optional.
41     * You can override it only if you need to
42     */
43    protected function buyGift(): ?string
44    {
45        return null;
46    }
47
48    private function buyAFlight(): string
49    {
50        return 'Buy a flight ticket';
51    }
52
53    private function takePlane(): string
54    {
55        return 'Taking the plane';
56    }
57
58    /**
59     * @return string[]
60     */
61    final public function getThingsToDo(): array
62    {
63        return $this->thingsToDo;
64    }
65}

BeachJourney.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\TemplateMethod;
 6
 7class BeachJourney extends Journey
 8{
 9    protected function enjoyVacation(): string
10    {
11        return "Swimming and sun-bathing";
12    }
13}

CityJourney.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\TemplateMethod;
 6
 7class CityJourney extends Journey
 8{
 9    protected function enjoyVacation(): string
10    {
11        return "Eat, drink, take photos and sleep";
12    }
13
14    protected function buyGift(): ?string
15    {
16        return "Buy a gift";
17    }
18}

3.12.4. Teste

Tests/JourneyTest.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\TemplateMethod\Tests;
 6
 7use DesignPatterns\Behavioral\TemplateMethod\BeachJourney;
 8use DesignPatterns\Behavioral\TemplateMethod\CityJourney;
 9use PHPUnit\Framework\TestCase;
10
11class JourneyTest extends TestCase
12{
13    public function testCanGetOnVacationOnTheBeach()
14    {
15        $beachJourney = new BeachJourney();
16        $beachJourney->takeATrip();
17
18        $this->assertSame(
19            ['Buy a flight ticket', 'Taking the plane', 'Swimming and sun-bathing', 'Taking the plane'],
20            $beachJourney->getThingsToDo()
21        );
22    }
23
24    public function testCanGetOnAJourneyToACity()
25    {
26        $cityJourney = new CityJourney();
27        $cityJourney->takeATrip();
28
29        $this->assertSame(
30            [
31                'Buy a flight ticket',
32                'Taking the plane',
33                'Eat, drink, take photos and sleep',
34                'Buy a gift',
35                'Taking the plane'
36            ],
37            $cityJourney->getThingsToDo()
38        );
39    }
40}