模板方法模式(Template Method)

目的

模板方法模式是一种行为型的设计模式。

可能你已经见过这种模式很多次了。它是一种让抽象模板的子类「完成」一系列算法的行为策略。

众所周知的「好莱坞原则」:「不要打电话给我们,我们会打电话给你」。这个类不是由子类调用的,而是以相反的方式。怎么做?当然很抽象啦!

换而言之,它是一种非常适合框架库的算法骨架。用户只需要实现子类的一种方法,其父类便可去搞定这项工作了。

这是一种分离具体类的简单办法,且可以减少复制粘贴,这也是它常见的原因。

UML 类图

5Brm3Ch0jM.png

代码

你可以在 GitHub 上找到这些代码

Journey.php

  1. <?php
  2. namespace DesignPatterns\Behavioral\TemplateMethod;
  3. abstract class Journey
  4. {
  5. /**
  6. * @var string[]
  7. */
  8. private $thingsToDo = [];
  9. /**
  10. * 这是当前类及其子类提供的公共服务
  11. * 注意,它「冻结」了全局的算法行为
  12. * 如果你想重写这个契约,只需要实现一个包含 takeATrip() 方法的接口
  13. */
  14. final public function takeATrip()
  15. {
  16. $this->thingsToDo[] = $this->buyAFlight();
  17. $this->thingsToDo[] = $this->takePlane();
  18. $this->thingsToDo[] = $this->enjoyVacation();
  19. $buyGift = $this->buyGift();
  20. if ($buyGift !== null) {
  21. $this->thingsToDo[] = $buyGift;
  22. }
  23. $this->thingsToDo[] = $this->takePlane();
  24. }
  25. /**
  26. * 这个方法必须要实现,它是这个模式的关键点
  27. */
  28. abstract protected function enjoyVacation(): string;
  29. /**
  30. * 这个方法是可选的,也可能作为算法的一部分
  31. * 如果需要的话你可以重写它
  32. *
  33. * @return null|string
  34. */
  35. protected function buyGift()
  36. {
  37. return null;
  38. }
  39. private function buyAFlight(): string
  40. {
  41. return 'Buy a flight ticket';
  42. }
  43. private function takePlane(): string
  44. {
  45. return 'Taking the plane';
  46. }
  47. /**
  48. * @return string[]
  49. */
  50. public function getThingsToDo(): array
  51. {
  52. return $this->thingsToDo;
  53. }
  54. }

BeachJourney.php

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

CityJourney.php

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

测试

Tests/JourneyTest.php

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