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

Описание
Шаблонный метод — поведенческий шаблон проектирования, определяющий основу алгоритма и позволяющий наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.

Шаблонный метод определяет каркас выполнения определённого алгоритма, но реализацию самих этапов делегирует дочерним классам.
Когда использовать «Шаблонный метод»?
  • Когда необходимо определить общую последовательность шагов алгоритма, но конкретную реализацию шагов нужно оставить гибкой.

  • Когда в классах есть повторяющийся код, который можно вынести в базовый класс, оставив дочерним классам возможность адаптировать отдельные этапы.

  • Когда необходимо следовать принципу открытости/закрытости: алгоритм можно расширить, не изменяя его структуру.
Основные участники паттерна
AbstractClass (Абстрактный класс) — определяет каркас алгоритма и включает шаги, которые могут быть реализованы по-разному.

ConcreteClass (Конкретный класс) — реализация, в которой переопределены шаги, оставленные для дочерних классов.
Пример реализации
Представим, что у нас есть процесс приготовления напитка. Основные шаги для его приготовления включают нагрев воды, добавление основного ингредиента и добавление дополнительных компонентов. Паттерн «Шаблонный метод» поможет вынести общие этапы в базовый класс, позволяя каждому типу напитка (например, чаю и кофе) определять свои особенности.
Шаг 1: Определим базовый класс с каркасом алгоритма
from abc import ABC, abstractmethod

class Beverage(ABC):
    def prepare_recipe(self):
        self.boil_water()
        self.brew()
        self.pour_in_cup()
        self.add_condiments()
        
    def boil_water(self):
        print("Boiling water")
    
    def pour_in_cup(self):
        print("Pouring into cup")

    @abstractmethod
    def brew(self):
        pass

    @abstractmethod
    def add_condiments(self):
        pass
Здесь:
  • Beverage — абстрактный класс с методом prepare_recipe, который является шаблонным методом, определяющим последовательность шагов.
  • Методы boil_water и pour_in_cup — общие для всех напитков и реализованы в базовом классе.
  • Методы brew и add_condiments — абстрактные, и их реализацию оставляем дочерним классам.
Шаг 2: Создадим конкретные классы для чая и кофе
class Tea(Beverage):
    def brew(self):
        print("Steeping the tea")
    
    def add_condiments(self):
        print("Adding lemon")

class Coffee(Beverage):
    def brew(self):
        print("Dripping coffee through filter")
    
    def add_condiments(self):
        print("Adding sugar and milk")
В этих классах:
  • Tea и Coffee наследуются от Beverage и реализуют свои версии методов brew и add_condiments.
  • У каждого напитка своя реализация этапов приготовления.
Шаг 3: Использование шаблонного метода
def main():
    print("Making tea:")
    tea = Tea()
    tea.prepare_recipe()

    print("\nMaking coffee:")
    coffee = Coffee()
    coffee.prepare_recipe()

main()
Как работает код?
Метод prepare_recipe определяет порядок выполнения шагов, который остается неизменным для всех потомков Beverage.

Каждый конкретный класс, например Tea и Coffee, реализует свои версии методов brew и add_condiments, но основной алгоритм остаётся одинаковым.
Преимущества
  • Код повторного использования. Общие шаги вынесены в базовый класс, что сокращает дублирование кода.

  • Гибкость. Конкретные классы могут переопределять только нужные этапы, сохраняя основной алгоритм.

  • Следование принципу открытости/закрытости. Шаблонный метод позволяет добавлять новые шаги или изменять их реализацию, не изменяя общий алгоритм.
Недостатки
  • Ограничение гибкости. Паттерн накладывает жёсткую структуру на алгоритм, и это может быть ограничением в некоторых ситуациях.

  • Зависимость от наследования. Дочерние классы могут переопределять методы только через наследование, что делает паттерн менее гибким, чем использование композиций.