Что такое паттерн Стратегия в программировании?

Ответ

Паттерн Стратегия (Strategy) — это поведенческий паттерн проектирования, который позволяет определять семейство алгоритмов, инкапсулировать каждый из них в отдельный класс и делать их взаимозаменяемыми. Он позволяет клиенту выбирать алгоритм во время выполнения, не изменяя код клиента, который его использует.

Назначение: Основная цель Стратегии — предоставить способ изменения поведения объекта (контекста) без изменения его структуры. Это достигается путем вынесения алгоритмов в отдельные классы, что позволяет легко добавлять новые алгоритмы и динамически переключаться между ними. Паттерн Стратегия является отличным примером применения Принципа Открытости/Закрытости (Open/Closed Principle): система открыта для расширения (добавления новых стратегий), но закрыта для модификации (клиентский код не меняется).

Основные компоненты:

  1. Контекст (Context): Класс, который содержит ссылку на объект стратегии и делегирует ему выполнение алгоритма. Он не знает конкретной реализации стратегии, только ее интерфейс.
  2. Интерфейс Стратегии (Strategy Interface): Абстрактный класс или интерфейс, который объявляет общий метод для всех конкретных стратегий.
  3. Конкретные Стратегии (Concrete Strategies): Классы, реализующие интерфейс стратегии и содержащие конкретную реализацию алгоритма.

Пример на Python (различные стратегии расчета скидок):

from abc import ABC, abstractmethod

# 2. Интерфейс Стратегии
class DiscountStrategy(ABC):
    @abstractmethod
    def apply_discount(self, price: float) -> float:
        pass

# 3. Конкретные Стратегии
class NoDiscountStrategy(DiscountStrategy):
    def apply_discount(self, price: float) -> float:
        return price

class PercentageDiscountStrategy(DiscountStrategy):
    def __init__(self, percentage: float):
        if not (0 <= percentage <= 1):
            raise ValueError("Процент скидки должен быть от 0 до 1")
        self._percentage = percentage

    def apply_discount(self, price: float) -> float:
        return price * (1 - self._percentage)

class FixedDiscountStrategy(DiscountStrategy):
    def __init__(self, amount: float):
        if amount < 0: 
            raise ValueError("Сумма скидки не может быть отрицательной")
        self._amount = amount

    def apply_discount(self, price: float) -> float:
        return max(0, price - self._amount)

# 1. Контекст
class ShoppingCart:
    def __init__(self, discount_strategy: DiscountStrategy):
        self._items = []
        self._discount_strategy = discount_strategy

    def add_item(self, item_price: float):
        self._items.append(item_price)

    def set_discount_strategy(self, strategy: DiscountStrategy):
        self._discount_strategy = strategy

    def calculate_total(self) -> float:
        total_price = sum(self._items)
        return self._discount_strategy.apply_discount(total_price)

# Использование:
cart = ShoppingCart(NoDiscountStrategy()) # Изначально без скидки
cart.add_item(100)
cart.add_item(50)
print(f"Общая сумма без скидки: {cart.calculate_total()}") # 150.0

# Применяем процентную скидку 10%
cart.set_discount_strategy(PercentageDiscountStrategy(0.10))
print(f"Общая сумма с 10% скидкой: {cart.calculate_total()}") # 135.0

# Применяем фиксированную скидку 20
cart.set_discount_strategy(FixedDiscountStrategy(20))
print(f"Общая сумма с фиксированной скидкой 20: {cart.calculate_total()}") # 130.0

Преимущества:

  • Гибкость и расширяемость: Легко добавлять новые алгоритмы без изменения существующего кода.
  • Избегание условных операторов: Устраняет длинные цепочки if/else или switch для выбора алгоритма.
  • Динамическая смена поведения: Позволяет изменять алгоритм, используемый объектом, во время выполнения.
  • Соответствие OCP: Система открыта для расширения новыми стратегиями, но закрыта для модификации.

Когда использовать паттерн Стратегия?

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