Template Method

Паттерн «скелет алгоритма»: общая последовательность шагов фиксируется в базовом классе, а отдельные шаги переопределяют подклассы.

Template Method определяет скелет алгоритма в методе базового класса, оставляя реализацию отдельных шагов подклассам.

Какую задачу решает

Несколько алгоритмов отличаются лишь парой шагов, а каркас одинаков: «загрузить данные → обработать → сохранить». Копировать каркас в каждый класс — дублирование. Template Method фиксирует порядок в базовом классе (template-метод), а изменчивые шаги объявляет абстрактными — их заполняют наследники.

Идея и реализация

from abc import ABC, abstractmethod


class Report(ABC):
    def generate(self):          # template-метод: скелет фиксирован
        self.load()
        body = self.format()
        self.save(body)

    def load(self):
        print("Загружаю данные")

    @abstractmethod
    def format(self): ...

    def save(self, body):
        print("Сохраняю:", body)


class HtmlReport(Report):
    def format(self):
        return "<h1>Отчёт</h1>"


class TextReport(Report):
    def format(self):
        return "ОТЧЁТ"


HtmlReport().generate()
TextReport().generate()

Вывод:

Загружаю данные
Сохраняю: <h1>Отчёт</h1>
Загружаю данные
Сохраняю: ОТЧЁТ

Метод generate задаёт неизменный порядок шагов. Подклассы переопределяют только format — изменчивую часть. Шаги load/save общие и не дублируются. Это «инверсия управления»: базовый класс вызывает методы подкласса, а не наоборот (принцип «не звоните нам, мы позвоним вам»).

Полезный приём — «хуки» (hooks): необязательные шаги с пустой реализацией по умолчанию, которые подкласс может переопределить, а может проигнорировать. Например, добавим в базовый класс пустой метод before_save() и вызовем его в шаблоне перед сохранением. Подклассу, которому нужна предобработка, достаточно его переопределить; остальные ничего не замечают. Так каркас остаётся жёстким там, где важна структура, и гибким там, где нужна вариативность.

Главный риск Template Method — жёсткость наследования. Подкласс намертво привязан к базовому классу и его порядку шагов; поменять алгоритм целиком в рантайме нельзя, а глубокие иерархии быстро становятся хрупкими. Поэтому, если изменчивых шагов много и они независимы, часто выгоднее собрать поведение из стратегий (композиция), а Template Method приберечь для случаев с одним-двумя варьируемыми шагами и стабильным каркасом.

Template Method против Strategy

Template MethodStrategy
Через наследованиеЧерез композицию
Меняет шаги алгоритмаМеняет алгоритм целиком
Фиксируется в подклассеПодменяется в рантайме

Где встречается

Базовые классы фреймворков (жизненный цикл View/компонента с хуками), пайплайны обработки данных, методы тестов (setUp/tearDown вокруг вашего теста), алгоритмы сортировки с переопределяемым сравнением.

Итог

  • Template Method фиксирует скелет алгоритма, отдавая шаги подклассам.
  • Убирает дублирование общего каркаса; реализует инверсию управления.
  • В отличие от Strategy, работает через наследование, а не композицию.
Проверьте себя
1. Что фиксирует Template Method?
AОдин экземпляр
BСкелет (порядок шагов) алгоритма в базовом классе
CСписок наблюдателей
DКопию объекта
2. Через какой механизм работает Template Method?
AКомпозицию
BНаследование (подклассы переопределяют шаги)
CГлобальные функции
DМетаклассы
3. Какой принцип иллюстрирует Template Method?
ASingleton
BИнверсия управления: базовый класс вызывает методы подкласса
CЛенивую загрузку
DКеширование
Поддержать проект