3.11. Template Method
3.11.1. Purpose
Template Method is a behavioral design pattern.
Perhaps you have encountered it many times already. The idea is to letsubclasses of this abstract template “finish” the behavior of analgorithm.
A.k.a the “Hollywood principle”: “Don’t call us, we call you.” Thisclass is not called by subclasses but the inverse. How? With abstractionof course.
In other words, this is a skeleton of algorithm, well-suited forframework libraries. The user has just to implement one method and thesuperclass do the job.
It is an easy way to decouple concrete classes and reduce copy-paste,that’s why you’ll find it everywhere.
3.11.2. UML Diagram
3.11.3. Code
You can also find this code on GitHub
Journey.php
- <?php
- namespace DesignPatterns\Behavioral\TemplateMethod;
- abstract class Journey
- {
- /**
- * @var string[]
- */
- private $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
- *
- * @return null|string
- */
- protected function buyGift()
- {
- 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
- namespace DesignPatterns\Behavioral\TemplateMethod;
- class BeachJourney extends Journey
- {
- protected function enjoyVacation(): string
- {
- return "Swimming and sun-bathing";
- }
- }
CityJourney.php
- <?php
- 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";
- }
- }
3.11.4. Test
Tests/JourneyTest.php
- <?php
- namespace DesignPatterns\Behavioral\TemplateMethod\Tests;
- use DesignPatterns\Behavioral\TemplateMethod;
- use PHPUnit\Framework\TestCase;
- class JourneyTest extends TestCase
- {
- public function testCanGetOnVacationOnTheBeach()
- {
- $beachJourney = new TemplateMethod\BeachJourney();
- $beachJourney->takeATrip();
- $this->assertEquals(
- ['Buy a flight ticket', 'Taking the plane', 'Swimming and sun-bathing', 'Taking the plane'],
- $beachJourney->getThingsToDo()
- );
- }
- public function testCanGetOnAJourneyToACity()
- {
- $beachJourney = new TemplateMethod\CityJourney();
- $beachJourney->takeATrip();
- $this->assertEquals(
- [
- 'Buy a flight ticket',
- 'Taking the plane',
- 'Eat, drink, take photos and sleep',
- 'Buy a gift',
- 'Taking the plane'
- ],
- $beachJourney->getThingsToDo()
- );
- }
- }