Template Method — Шаблонный метод

Последовательность шагов алгоритма фиксирована в базовом классе. Наследники переопределяют отдельные шаги. Основа Transaction Script.

Проблема

Несколько вариантов одной бизнес-операции (платёж: внутренний, международный, крипто). Каждый требует одинаковых шагов (валидация → авторизация → перевод → нотификация), но конкретная реализация шагов разная.

Не хочется дублировать последовательность во всех вариантах.

Решение

  • Базовый класс задаёт последовательность шагов в одном методе.
  • Каждый шаг — отдельный метод (validate, authorize, ...).
  • По умолчанию шаги бросают throw new Error('not implemented') или имеют дефолтную реализацию.
  • Наследники переопределяют нужные шаги, не трогая последовательность.

Пример в JS

// Базовый класс с фиксированной последовательностью
class MoneyTransfer {
  async process(request, connection) {
    await this.validate(request);
    await this.authorize(request);
    await this.transfer(request, connection);
    await this.notify(request);
  }
  validate(r) { throw new Error('not implemented'); }
  authorize(r) { throw new Error('not implemented'); }
  transfer(r, c) { throw new Error('not implemented'); }
  notify(r) { /* дефолт: ничего не делать */ }
}

class DomesticTransfer extends MoneyTransfer {
  validate(r) { /* ... */ }
  authorize(r) { /* ... */ }
  transfer(r, c) { /* SQL UPDATE accounts ... */ }
}

class InternationalTransfer extends MoneyTransfer {
  validate(r) { /* + проверка санкций */ }
  authorize(r) { /* + 3DS */ }
  transfer(r, c) { /* + SWIFT */ }
  notify(r) { /* отправить email */ }
}
// Функциональная альтернатива без наследования
const workflow = (steps) => async (request) => {
  for (const step of steps) await step(request);
};

const moneyTransfer = workflow([validate, authorize, transfer, notify]);
const internationalTransfer = workflow([validateIntl, authorize3DS, transferSwift, notifyEmail]);

Где используется в JS-экосистеме

  • Express middleware-цепочки — последовательность обработчиков (но это уже антипаттерн в 2026, по автору)
  • NestJS interceptors + pipes + guards — фиксированная последовательность по фазам
  • React lifecyclecomponentDidMount, componentDidUpdate (template method класса)
  • Hooks порядок — фиксирован, нарушать нельзя
  • Тестовые фреймворки: beforeEach, it, afterEach

Подводные камни

  • Template Method = жёсткая последовательность — если порядок меняется, паттерн ломается.
  • Глубокое наследование становится непрозрачным — что унаследовано, что переопределено.
  • Альтернатива в JS — функциональный pipe из шагов (без наследования).
  • Связь с транзакциями: «весь template-метод обернуть в транзакцию» — рекомендация автора.

Главные тезисы автора

  • «Определение шагов алгоритма».
  • «Последовательность задана в корне, её уже не можем трогать» — это и суть, и ограничение.
  • «Каждый шаг можно переопределить» в наследниках.
  • «Дефолтное поведение + переопределение» — шаги по умолчанию throw или логируют.
  • Template Method + persistent state — основа Transaction Script.
  • Транзакция: секунды, не часы — длительность блокировок.
  • В JS — функциональная альтернатива через массив функций без наследования.
  • Object pool коннекшенов прокидывается во все шаги.

🎓 Источники

См. также