Шаблонный метод — это поведенческий паттерн, задающий скелет алгоритма в суперклассе и заставляющий подклассы реализовать конкретные шаги этого алгоритма.
Применимость: Шаблонные методы можно встретить во многих PHP-фреймворках. Разработчики создают такие методы, чтобы позволить клиентам легко и быстро расширять стандартный код при помощи наследования.
Признаки применения паттерна: Класс заставляет своих потомков реализовать методы-шаги, но самостоятельно реализует структуру алгоритма.
<?php namespace RefactoringGuru\TemplateMethod\Conceptual; /** * Абстрактный Класс определяет шаблонный метод, содержащий скелет некоторого * алгоритма, состоящего из вызовов (обычно) абстрактных примитивных операций. * * Конкретные подклассы должны реализовать эти операции, но оставить сам * шаблонный метод без изменений. */ abstract class AbstractClass { /** * Шаблонный метод определяет скелет алгоритма. */ final public function templateMethod(): void { $this->baseOperation1(); $this->requiredOperations1(); $this->baseOperation2(); $this->hook1(); $this->requiredOperation2(); $this->baseOperation3(); $this->hook2(); } /** * Эти операции уже имеют реализации. */ protected function baseOperation1(): void { echo "AbstractClass says: I am doing the bulk of the work\n"; } protected function baseOperation2(): void { echo "AbstractClass says: But I let subclasses override some operations\n"; } protected function baseOperation3(): void { echo "AbstractClass says: But I am doing the bulk of the work anyway\n"; } /** * А эти операции должны быть реализованы в подклассах. */ abstract protected function requiredOperations1(): void; abstract protected function requiredOperation2(): void; /** * Это «хуки». Подклассы могут переопределять их, но это не обязательно, * поскольку у хуков уже есть стандартная (но пустая) реализация. Хуки * предоставляют дополнительные точки расширения в некоторых критических * местах алгоритма. */ protected function hook1(): void { } protected function hook2(): void { } } /** * Конкретные классы должны реализовать все абстрактные операции базового * класса. Они также могут переопределить некоторые операции с реализацией по * умолчанию. */ class ConcreteClass1 extends AbstractClass { protected function requiredOperations1(): void { echo "ConcreteClass1 says: Implemented Operation1\n"; } protected function requiredOperation2(): void { echo "ConcreteClass1 says: Implemented Operation2\n"; } } /** * Обычно конкретные классы переопределяют только часть операций базового * класса. */ class ConcreteClass2 extends AbstractClass { protected function requiredOperations1(): void { echo "ConcreteClass2 says: Implemented Operation1\n"; } protected function requiredOperation2(): void { echo "ConcreteClass2 says: Implemented Operation2\n"; } protected function hook1(): void { echo "ConcreteClass2 says: Overridden Hook1\n"; } } /** * Клиентский код вызывает шаблонный метод для выполнения алгоритма. Клиентский * код не должен знать конкретный класс объекта, с которым работает, при * условии, что он работает с объектами через интерфейс их базового класса. */ function clientCode(AbstractClass $class) { // ... $class->templateMethod(); // ... } echo "Same client code can work with different subclasses:\n"; clientCode(new ConcreteClass1()); echo "\n"; echo "Same client code can work with different subclasses:\n"; clientCode(new ConcreteClass2());
Same client code can work with different subclasses: AbstractClass says: I am doing bulk of the work ConcreteClass1 says: Implemented Operation1 AbstractClass says: But I let subclasses to override some operations ConcreteClass1 says: Implemented Operation2 AbstractClass says: But I am doing bulk of the work anyway Same client code can work with different subclasses: AbstractClass says: I am doing bulk of the work ConcreteClass2 says: Implemented Operation1 AbstractClass says: But I let subclasses to override some operations ConcreteClass2 says: Overridden Hook1 ConcreteClass2 says: Implemented Operation2 AbstractClass says: But I am doing bulk of the work anyway