template-method Шаблонний метод (англ. Template method) — поведінковий шаблон проєктування, що визначає скелет алгоритму, перекладаючи відповідальність за деякі його кроки на підкласи. Дозволяє підкласам перевизначати кроки алгоритму, не змінюючи його загальну структуру.

Іншими словами, це каркас алгоритму, який добре підходить для бібліотек (у фреймворках, наприклад). Користувач просто реалізує методи уточнення, а суперклас робить всю основну роботу.

Це простий спосіб ізолювати логіку в конкретні класи та зменшити копіпаст, тому ви повсюдно зустрінете його у тому чи іншому вигляді.

  • BeachJourney.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\TemplateMethod;
    
    /**
     * Class BeachJourney
     * @package DesignPatterns\Behavioral\TemplateMethod
     */
    class BeachJourney extends Journey
    {
        /**
         * @return string
         */
        protected function enjoyVacation(): string
        {
            return "Swimming and sun-bathing";
        }
    }
    
                  
  • CityJourney.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\TemplateMethod;
    
    /**
     * Class CityJourney
     * @package DesignPatterns\Behavioral\TemplateMethod
     */
    class CityJourney extends Journey
    {
        /**
         * @return string
         */
        protected function enjoyVacation(): string
        {
            return "Eat, drink, take photos and sleep";
        }
    
        /**
         * @return string
         */
        protected function buyGift(): string
        {
            return "Buy a gift";
        }
    }
    
                  
  • Journey.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\TemplateMethod;
    
    /**
     * Class Journey
     * @package DesignPatterns\Behavioral\TemplateMethod
     */
    abstract class Journey
    {
        /**
         * @var string[]
         */
        private $thingsToDo = [];
    
        /**
         * @return void
         */
        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();
        }
    
        /**
         * @return string
         */
        abstract protected function enjoyVacation(): string;
    
        /**
         * @return string|null
         */
        protected function buyGift()
        {
            return null;
        }
    
        /**
         * @return string
         */
        private function buyAFlight(): string
        {
            return 'Buy a flight ticket';
        }
    
        /**
         * @return string
         */
        private function takePlane(): string
        {
            return 'Taking the plane';
        }
    
        /**
         * @return string[]
         */
        public function getThingsToDo(): array
        {
            return $this->thingsToDo;
        }
    }
    
                  
  • Test case:

    TemplateMethodTest.php
      
    <?php
    
    namespace DesignPatterns\Tests\Behavioral\TemplateMethod;
    
    use DesignPatterns\Behavioral\TemplateMethod\BeachJourney;
    use DesignPatterns\Behavioral\TemplateMethod\CityJourney;
    
    class TemplateMethodTest extends \PHPUnit_Framework_TestCase
    {
        public function testCanGetOnVacationOnTheBeach()
        {
            $beachJourney = new 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 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()
            );
        }
    }