3.12. 模板方法模式
3.12.1. 目的
模板方法是一种行为设计模式。
你可能已经遇到过很多次了。它的思想是让这个抽象模板的子类【完成】算法的行为策略。
也就是【好莱坞原则】:【别打给我们,我们打给你】。这个类不是由子类调用的,而是由相反的子类调用的。怎么样?当然是抽象的。
换句话说,这是一个算法框架,非常适合框架库。用户只需要实现一个方法,超类就可以完成这项工作。
它是解耦具体类和减少复制黏贴的一种简单方法,这就是为什么你会发现它无处不在。
3.12.2. UML 图
3.12.3. 代码
在 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. 测试
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}