← Назад к вопросам

Какие плюсы и минусы у паттерна стратегия?

1.7 Middle🔥 151 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Strategy Pattern: Плюсы и минусы

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

Структура паттерна Strategy

from abc import ABC, abstractmethod

# Интерфейс стратегии
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount: float) -> bool:
        pass

# Конкретные стратегии
class CreditCardPayment(PaymentStrategy):
    def __init__(self, card_number, cvv):
        self.card_number = card_number
        self.cvv = cvv
    
    def pay(self, amount: float) -> bool:
        print(f"Processing credit card payment: ${amount}")
        return True

class PayPalPayment(PaymentStrategy):
    def __init__(self, email):
        self.email = email
    
    def pay(self, amount: float) -> bool:
        print(f"Processing PayPal payment: ${amount}")
        return True

class CryptoPayment(PaymentStrategy):
    def __init__(self, wallet_address):
        self.wallet_address = wallet_address
    
    def pay(self, amount: float) -> bool:
        print(f"Processing crypto payment: ${amount}")
        return True

# Контекст, использующий стратегию
class ShoppingCart:
    def __init__(self):
        self.items = []
        self.payment_strategy = None
    
    def add_item(self, item, price):
        self.items.append({"item": item, "price": price})
    
    def set_payment_strategy(self, strategy: PaymentStrategy):
        self.payment_strategy = strategy
    
    def checkout(self):
        total = sum(item["price"] for item in self.items)
        if self.payment_strategy:
            return self.payment_strategy.pay(total)
        return False

# Использование
cart = ShoppingCart()
cart.add_item("Laptop", 1000)
cart.add_item("Mouse", 30)

# Выбираем стратегию во время выполнения
cart.set_payment_strategy(CreditCardPayment("1234-5678-9012", "123"))
cart.checkout()  # Processing credit card payment: $1030

# Меняем стратегию
cart.set_payment_strategy(PayPalPayment("user@example.com"))
cart.checkout()  # Processing PayPal payment: $1030

Плюсы паттерна Strategy

1. Избегание множественных условных операторов (if/else)

Проблема без паттерна:

# Без Strategy
class PaymentProcessor:
    def process_payment(self, payment_type, amount, payment_data):
        if payment_type == "credit_card":
            # 50 строк кода обработки кредитной карты
            pass
        elif payment_type == "paypal":
            # 50 строк кода обработки PayPal
            pass
        elif payment_type == "crypto":
            # 50 строк кода обработки крипто
            pass
        elif payment_type == "bank_transfer":
            # 50 строк кода для банковского перевода
            pass
        # ... ещё 20 условий
        # Функция раздувается до 500+ строк!

Решение с Strategy:

# С Strategy
class PaymentProcessor:
    def process_payment(self, strategy: PaymentStrategy, amount):
        return strategy.pay(amount)  # Просто одна строка!

Результат: Код более читаемый и чистый

2. Открыт/Закрыт принцип (Open/Closed Principle)

Преимущество: Легко добавлять новые алгоритмы без изменения существующего кода

# Новая стратегия через 2 года
class ApplePayPayment(PaymentStrategy):
    def __init__(self, apple_id):
        self.apple_id = apple_id
    
    def pay(self, amount: float) -> bool:
        print(f"Processing Apple Pay: ${amount}")
        return True

# PaymentProcessor не требует изменений!
cart.set_payment_strategy(ApplePayPayment("user@icloud.com"))
cart.checkout()  # Работает сразу!

Результат: Проще поддерживать код и добавлять функционал

3. Динамический выбор алгоритма

Преимущество: Выбор алгоритма во время выполнения, а не на этапе компиляции

class PaymentProcessor:
    @staticmethod
    def get_strategy(payment_method: str) -> PaymentStrategy:
        strategies = {
            "credit_card": CreditCardPayment,
            "paypal": PayPalPayment,
            "crypto": CryptoPayment,
        }
        return strategies.get(payment_method)

# Выбор основан на пользовательском вводу
user_choice = input("Choose payment method: ")
strategy_class = PaymentProcessor.get_strategy(user_choice)
strategy = strategy_class(...)
cart.set_payment_strategy(strategy)

4. Логика алгоритма инкапсулирована

Преимущество: Каждый алгоритм находится в своем классе, отделён от других

# Каждая стратегия несёт только свою ответственность
class BitcoinPayment(PaymentStrategy):
    # Только логика Bitcoin платежей
    def pay(self, amount: float) -> bool:
        # Проверка адреса
        # Проверка комиссии
        # Отправка транзакции
        # Логирование
        pass

class EthereumPayment(PaymentStrategy):
    # Только логика Ethereum платежей
    def pay(self, amount: float) -> bool:
        # Другая логика
        pass

5. Упрощает тестирование

Преимущество: Легко тестировать каждую стратегию отдельно

import pytest
from unittest.mock import Mock

class TestPaymentStrategies:
    def test_credit_card_payment(self):
        strategy = CreditCardPayment("1234-5678-9012", "123")
        assert strategy.pay(100) == True
    
    def test_paypal_payment(self):
        strategy = PayPalPayment("user@example.com")
        assert strategy.pay(100) == True
    
    def test_crypto_payment(self):
        strategy = CryptoPayment("wallet123")
        assert strategy.pay(100) == True
    
    def test_cart_with_different_strategies(self):
        cart = ShoppingCart()
        cart.add_item("Item", 100)
        
        # Тестируем с разными стратегиями
        for strategy in [CreditCardPayment(...), PayPalPayment(...)]:
            cart.set_payment_strategy(strategy)
            assert cart.checkout() == True

6. Стратегии легко переиспользовать

Преимущество: Одну стратегию можно использовать в разных контекстах

# Стратегия работает не только с ShoppingCart
class Subscription:
    def __init__(self):
        self.payment_strategy = None
    
    def set_payment_strategy(self, strategy: PaymentStrategy):
        self.payment_strategy = strategy
    
    def renew(self, amount):
        return self.payment_strategy.pay(amount)

class DonationPlatform:
    def __init__(self):
        self.payment_strategy = None
    
    def set_payment_strategy(self, strategy: PaymentStrategy):
        self.payment_strategy = strategy
    
    def donate(self, amount):
        return self.payment_strategy.pay(amount)

# Одна стратегия используется везде
strategy = CreditCardPayment("1234", "123")
cart = ShoppingCart()
subscription = Subscription()
donation = DonationPlatform()

cart.set_payment_strategy(strategy)
subscription.set_payment_strategy(strategy)
donation.set_payment_strategy(strategy)

Минусы паттерна Strategy

1. Усложнение кода для простых случаев

Проблема: Для простых алгоритмов паттерн может быть оверкилл

# Если только 2 способа платежа
class PaymentProcessor:
    def __init__(self):
        self.payment_strategy = None
    
    def set_strategy(self, strategy):
        self.payment_strategy = strategy
    
    def pay(self, amount):
        return self.payment_strategy.pay(amount)

# Много кода для простой функциональности
# Может быть проще:
if payment_type == "credit_card":
    # обработка
else:
    # обработка

Результат: Излишняя сложность для простых задач

2. Увеличение количества классов

Проблема: Каждая стратегия требует отдельного класса

# Если есть 10 способов платежа, нужно 10 классов
class Strategy1: pass
class Strategy2: pass
class Strategy3: pass
class Strategy4: pass
class Strategy5: pass
class Strategy6: pass
class Strategy7: pass
class Strategy8: pass
class Strategy9: pass
class Strategy10: pass

# Может быть сложнее ориентироваться в коде

Результат: Много файлов, сложнее ориентироваться

3. Overhead памяти

Проблема: Каждая стратегия — объект, требующий память

# Без Strategy (экономнее)
def pay_with_credit_card(amount):
    return True

def pay_with_paypal(amount):
    return True

# С Strategy (больше памяти)
strategy = CreditCardPayment("card", "cvv")  # Объект в памяти
# Для простой функции это излишне

4. Сложность выбора правильной стратегии

Проблема: Нужно правильно выбрать и сконфигурировать стратегию

# Если выбрать неправильную стратегию
strategy = CreditCardPayment("invalid_card", "invalid_cvv")
cart.set_payment_strategy(strategy)
cart.checkout()  # Ошибка во время выполнения!

# Без Strategy это было бы очевидной ошибкой уже на этапе вызова
process_payment("credit_card", amount, "invalid_card")

5. Проблемы с сериализацией

Проблема: Сложно сериализовать стратегию (сохранить/загрузить)

import json

cart = ShoppingCart()
cart.set_payment_strategy(CreditCardPayment("1234", "123"))

# Как сохранить стратегию в JSON?
json.dumps(cart)  # TypeError: Object is not JSON serializable

# Нужно вручную реализовать
class CreditCardPayment(PaymentStrategy):
    def to_dict(self):
        return {"type": "credit_card", "card": self.card_number}
    
    @staticmethod
    def from_dict(data):
        return CreditCardPayment(data["card"], "123")

6. Скрытая зависимость контекста от стратегии

Проблема: Контекст зависит от какие-то параметры стратегии

# Контекст неявно зависит от деталей стратегии
class ShoppingCart:
    def checkout(self):
        # Контекст не знает, что нужно вызвать pay()
        # Хорошо, если интерфейс четко определен
        total = sum(...)
        return self.payment_strategy.pay(total)  # Это предполагает наличие pay()
        # Но что если добавить новый метод validate()?

7. Difficuly с отладкой

Проблема: Сложнее следить, какая стратегия используется

# Где определяется выбор стратегии?
cart.set_payment_strategy(strategy)  # Откуда это взялось?
# Нужно искать в коде, где set_payment_strategy() вызывается

# Без Strategy это было бы явным условием
if payment_type == "credit_card":
    # Ясно, что происходит

Когда использовать Strategy

Используй Strategy для:

  • Множество похожих алгоритмов (3+)
  • Алгоритмы, которые часто меняются
  • Выбор алгоритма во время выполнения
  • Тестирование разных реализаций
  • Open/Closed принцип важен

Не используй Strategy для:

  • 1-2 способа выполнения
  • Простые условные операторы
  • Когда нет необходимости в расширении
  • Прототип или MVP

Альтернативы Strategy

1. Условные операторы (для простых случаев)

def process_payment(method: str, amount: float) -> bool:
    if method == "credit_card":
        return process_credit_card(amount)
    elif method == "paypal":
        return process_paypal(amount)
    else:
        raise ValueError(f"Unknown method: {method}")

2. Dictionary с функциями

payment_methods = {
    "credit_card": process_credit_card,
    "paypal": process_paypal,
    "crypto": process_crypto,
}

payment_methods[method](amount)

3. Полиморфизм через наследование (Strategy это делает)

Практический пример: Сортировка

from abc import ABC, abstractmethod

class SortingStrategy(ABC):
    @abstractmethod
    def sort(self, data: list) -> list:
        pass

class BubbleSort(SortingStrategy):
    def sort(self, data: list) -> list:
        # Bubble sort implementation
        return sorted(data)

class QuickSort(SortingStrategy):
    def sort(self, data: list) -> list:
        # Quick sort implementation
        return sorted(data)

class MergeSort(SortingStrategy):
    def sort(self, data: list) -> list:
        # Merge sort implementation
        return sorted(data)

class Sorter:
    def __init__(self, strategy: SortingStrategy):
        self.strategy = strategy
    
    def sort(self, data: list) -> list:
        return self.strategy.sort(data)

# Использование
data = [5, 2, 8, 1, 9]
sorter = Sorter(QuickSort())
print(sorter.sort(data))  # [1, 2, 5, 8, 9]

# Смена алгоритма во время выполнения
sorter.strategy = MergeSort()
print(sorter.sort(data))  # [1, 2, 5, 8, 9]

Вывод

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

Какие плюсы и минусы у паттерна стратегия? | PrepBro