Menu
Coddy logo textTech

Паттерн Шаблонный метод

Часть раздела Объектно-ориентированное программирование путешествия по 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 в шаблонном методе. Это предотвращает изменение структуры алгоритма подклассами. Данный паттерн идеален, когда у вас есть несколько классов, которые следуют одному и тому же рабочему процессу, но различаются в деталях реализации. Добавление нового типа документа означает создание нового подкласса без изменения существующего кода.

challenge icon

Задание

Легко

Давайте создадим систему генерации отчетов, используя паттерн «Шаблонный метод» (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() и выведите полный отчет

?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование