Паттерн Шаблонный метод
Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 78 из 91.
Шаблонный метод (Template Method Pattern) — это поведенческий шаблон проектирования, который определяет скелет алгоритма в базовом классе, позволяя подклассам переопределять конкретные шаги, не изменяя общую структуру. Базовый класс управляет рабочим процессом, в то время как подклассы настраивают отдельные его части.
Рассмотрим создание различных типов документов.
Каждый документ следует одному и тому же процессу: создание заголовка, добавление содержимого, создание нижнего колонтитула. Но особенности каждого шага зависят от типа документа. Шаблонный метод определяет эту последовательность, в то время как подклассы реализуют детали.
<?php
abstract class DocumentGenerator {
// Шаблонный метод - определяет структуру алгоритма
public final function generate(): string {
$output = $this->createHeader();
$output .= $this->createContent();
$output .= $this->createFooter();
return $output;
}
// Шаги, которые должны быть реализованы подклассами
abstract protected function createHeader(): string;
abstract protected function createContent(): string;
abstract protected function createFooter(): string;
}
class HtmlDocument extends DocumentGenerator {
protected function createHeader(): string {
return "<html><head></head><body>";
}
protected function createContent(): string {
return "<p>Hello World</p>";
}
protected function createFooter(): string {
return "</body></html>";
}
}
class TextDocument extends DocumentGenerator {
protected function createHeader(): string {
return "=== DOCUMENT ===\n";
}
protected function createContent(): string {
return "Hello World\n";
}
protected function createFooter(): string {
return "=== END ===";
}
}
$html = new HtmlDocument();
echo $html->generate() . "\n";
$text = new TextDocument();
echo $text->generate();
Вывод:
<html><head></head><body><p>Hello World</p></body></html>
=== DOCUMENT ===
Hello World
=== END ===Обратите внимание на ключевое слово final в шаблонном методе. Это предотвращает изменение структуры алгоритма подклассами. Данный паттерн идеален, когда у вас есть несколько классов, которые следуют одному и тому же рабочему процессу, но различаются в деталях реализации. Добавление нового типа документа означает создание нового подкласса без изменения существующего кода.
Задание
ЛегкоДавайте создадим систему генерации отчетов, используя паттерн «Шаблонный метод» (Template Method Pattern). Различные типы отчетов следуют одной и той же структуре — раздел заголовка, раздел данных и раздел сводки — но каждый тип отчета форматирует эти разделы по-разному. Шаблонный метод определит этот рабочий процесс, в то время как подклассы будут настраивать каждый шаг.
Вы распределите свой код по четырем файлам:
ReportGenerator.php— Создайте абстрактный классReportGenerator, который определяет скелет алгоритма генерации отчета. Ваш класс должен:- Принимать заголовок отчета в конструкторе (используйте продвижение свойств в конструкторе — constructor promotion)
- Иметь
finalпубличный методgenerate(): string, который последовательно вызывает три шага:createTitle(),createBody()иcreateSummary(), объединяя их результаты - Объявлять три абстрактных защищенных метода, которые подклассы должны реализовать:
createTitle(): string,createBody(): stringиcreateSummary(): string
HtmlReport.php— Подключите файл ReportGenerator и создайте классHtmlReport, который расширяет его. Этот отчет форматирует вывод в виде HTML:createTitle()возвращает"<h1>[title]</h1>"createBody()возвращает"<p>Report data goes here</p>"createSummary()возвращает"<footer>End of report</footer>"
PlainTextReport.php— Подключите файл ReportGenerator и создайте классPlainTextReport, который расширяет его. Этот отчет использует простое текстовое форматирование:createTitle()возвращает"### [title] ###\n"createBody()возвращает"Report data goes here\n"createSummary()возвращает"--- End of report ---"
main.php— Подключите оба файла отчетов. Вы получите два входных значения: тип отчета ("html"или"text") и заголовок отчета.В зависимости от типа отчета создайте соответствующий генератор отчетов с заданным заголовком. Вызовите
generate()и выведите готовый отчет.
Паттерн «Шаблонный метод» гарантирует, что все отчеты следуют одной и той же трехэтапной структуре, позволяя каждому типу отчета уникально форматировать свои разделы. Ключевое слово final для метода generate() предотвращает изменение последовательности алгоритма подклассами — они могут настраивать только отдельные шаги.
Шпаргалка
Паттерн Шаблонный метод (Template Method Pattern) определяет скелет алгоритма в базовом классе, позволяя подклассам переопределять отдельные шаги, не изменяя общую структуру.
Базовый класс содержит шаблонный метод, который управляет последовательностью рабочего процесса. Этот метод обычно помечается как final, чтобы подклассы не могли изменить структуру алгоритма.
Подклассы реализуют абстрактные методы для настройки отдельных шагов алгоритма.
<?php
abstract class DocumentGenerator {
// Шаблонный метод — определяет структуру алгоритма
public final function generate(): string {
$output = $this->createHeader();
$output .= $this->createContent();
$output .= $this->createFooter();
return $output;
}
// Абстрактные шаги, которые должны реализовать подклассы
abstract protected function createHeader(): string;
abstract protected function createContent(): string;
abstract protected function createFooter(): string;
}
class HtmlDocument extends DocumentGenerator {
protected function createHeader(): string {
return "<html><head></head><body>";
}
protected function createContent(): string {
return "<p>Hello World</p>";
}
protected function createFooter(): string {
return "</body></html>";
}
}
$html = new HtmlDocument();
echo $html->generate();
Основные характеристики:
- Шаблонный метод помечен как
final, чтобы предотвратить изменение структуры алгоритма - Абстрактные методы определяют точки настройки для подклассов
- Идеально подходит, когда несколько классов следуют одному и тому же рабочему процессу, но различаются деталями реализации
- Добавление новых вариантов требует только создания новых подклассов без изменения существующего кода
Попробуйте сами
<?php
require_once 'HtmlReport.php';
require_once 'PlainTextReport.php';
// Чтение входных данных
$reportType = trim(fgets(STDIN));
$reportTitle = trim(fgets(STDIN));
// TODO: На основе типа отчета ("html" или "text"),
// создайте соответствующий генератор отчетов с заданным заголовком
// TODO: Вызовите generate() и выведите полный отчет
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy