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