PHP 模板方法模式
目的
模板方法是一种行为设计模式。
你可能已经遇到过很多次了。它的思想是让这个抽象模板的子类【完成】算法的行为策略。
也就是【好莱坞原则】:【别打给我们,我们打给你】。这个类不是由子类调用的,而是由相反的子类调用的。怎么样?当然是抽象的。
换句话说,这是一个算法框架,非常适合框架库。用户只需要实现一个方法,超类就可以完成这项工作。
它是解耦具体类和减少复制黏贴的一种简单方法,这就是为什么你会发现它无处不在。
UML 图
代码
Journey.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\TemplateMethod; abstract class Journey { /** * @var string[] */ private array $thingsToDo = []; /** * This is the public service provided by this class and its subclasses. * Notice it is final to "freeze" the global behavior of algorithm. * If you want to override this contract, make an interface with only takeATrip() * and subclass it. */ final public function takeATrip() { $this->thingsToDo[] = $this->buyAFlight(); $this->thingsToDo[] = $this->takePlane(); $this->thingsToDo[] = $this->enjoyVacation(); $buyGift = $this->buyGift(); if ($buyGift !== null) { $this->thingsToDo[] = $buyGift; } $this->thingsToDo[] = $this->takePlane(); } /** * This method must be implemented, this is the key-feature of this pattern. */ abstract protected function enjoyVacation(): string; /** * This method is also part of the algorithm but it is optional. * You can override it only if you need to */ protected function buyGift(): ?string { return null; } private function buyAFlight(): string { return 'Buy a flight ticket'; } private function takePlane(): string { return 'Taking the plane'; } /** * @return string[] */ public function getThingsToDo(): array { return $this->thingsToDo; } }
BeachJourney.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\TemplateMethod; class BeachJourney extends Journey { protected function enjoyVacation(): string { return "Swimming and sun-bathing"; } }
CityJourney.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\TemplateMethod; class CityJourney extends Journey { protected function enjoyVacation(): string { return "Eat, drink, take photos and sleep"; } protected function buyGift(): ?string { return "Buy a gift"; } }
测试
Tests/JourneyTest.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\TemplateMethod\Tests; use DesignPatterns\Behavioral\TemplateMethod\BeachJourney; use DesignPatterns\Behavioral\TemplateMethod\CityJourney; use PHPUnit\Framework\TestCase; class JourneyTest extends TestCase { public function testCanGetOnVacationOnTheBeach() { $beachJourney = new BeachJourney(); $beachJourney->takeATrip(); $this->assertSame( ['Buy a flight ticket', 'Taking the plane', 'Swimming and sun-bathing', 'Taking the plane'], $beachJourney->getThingsToDo() ); } public function testCanGetOnAJourneyToACity() { $cityJourney = new CityJourney(); $cityJourney->takeATrip(); $this->assertSame( [ 'Buy a flight ticket', 'Taking the plane', 'Eat, drink, take photos and sleep', 'Buy a gift', 'Taking the plane' ], $cityJourney->getThingsToDo() ); } }