Как в Python реализовать паттерн проектирования ‘Стратегия’

«Как в Python реализовать паттерн проектирования ‘Стратегия’» — вопрос из категории Паттерны, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Компоненты паттерна:

  • Context (Контекст): Класс, который использует одну из стратегий. Он хранит ссылку на объект-стратегию и делегирует ему выполнение алгоритма.
  • Strategy (Стратегия): Абстрактный базовый класс или интерфейс, который определяет общий метод для всех конкретных стратегий.
  • ConcreteStrategy (Конкретная стратегия): Классы, реализующие интерфейс Strategy, каждый со своей версией алгоритма.

Пример реализации

Представим, что у нас есть контекст, который должен сортировать данные, но способ сортировки может меняться.

from abc import ABC, abstractmethod
from typing import List, Type

# 1. Интерфейс Стратегии
class SortStrategy(ABC):
    @abstractmethod
    def sort(self, data: List) -> List:
        pass

# 2. Конкретные стратегии
class AscendingSort(SortStrategy):
    """Сортировка по возрастанию."""
    def sort(self, data: List) -> List:
        return sorted(data)

class DescendingSort(SortStrategy):
    """Сортировка по убыванию."""
    def sort(self, data: List) -> List:
        return sorted(data, reverse=True)

# 3. Контекст
class Sorter:
    def __init__(self, strategy: SortStrategy):
        self._strategy = strategy

    def set_strategy(self, strategy: SortStrategy):
        """Позволяет сменить стратегию на лету."""
        self._strategy = strategy

    def execute_sort(self, data: List):
        print(f"Используется стратегия: {self._strategy.__class__.__name__}")
        result = self._strategy.sort(data)
        print(f"Результат: {result}")
        return result

# Использование
data_to_sort = [5, 2, 8, 1, 9, 4]

# Изначально используем сортировку по возрастанию
sorter = Sorter(AscendingSort())
sorter.execute_sort(data_to_sort)
# Вывод:
# Используется стратегия: AscendingSort
# Результат: [1, 2, 4, 5, 8, 9]

# Динамически меняем стратегию на сортировку по убыванию
sorter.set_strategy(DescendingSort())
sorter.execute_sort(data_to_sort)
# Вывод:
# Используется стратегия: DescendingSort
# Результат: [9, 8, 5, 4, 2, 1]

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

  • Гибкость: Позволяет легко добавлять новые алгоритмы, не изменяя код контекста.
  • Изоляция: Код, данные и зависимости алгоритмов изолированы в своих классах.
  • Следование принципу открытости/закрытости: Система открыта для расширения (новые стратегии), но закрыта для модификации (не нужно менять класс Sorter).