3.12. Template Method
3.12.1. Zweck
Template Methode ist ein Verhaltensmuster.
Vielleicht kennst du diesen Fall schon. Die Idee hierbei ist, mehrere Subklassen „finalisieren“ das Verhalten eines Algorithmus.
Z.B. das „Hollywood Prinzip“: „Don’t call us, we call you.“ Diese Klasse wird nicht von der Subklasse aufgerufen, sondern im Gegenteil. Das passiert über eine Abstraktion.
In anderen Worten: es gibt ein Gerüst eines Algorithmus, bestens geeignet für eine Framework-Bibliothek. Der User kann dabei eine Methode implementieren und die Superklasse übernimmt alles weitere.
Es ist ein einfacher Weg, um konkrete Klasse zu entkoppeln und Copy-Paste zu minimieren.
3.12.2. UML-Diagramm
3.12.3. Code
Du findest den Code hierzu auf 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. Test
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}