Ответ
Паттерн Стратегия (Strategy) — это поведенческий паттерн проектирования, который позволяет определять семейство алгоритмов, инкапсулировать каждый из них в отдельный класс и делать их взаимозаменяемыми. Он позволяет клиенту выбирать алгоритм во время выполнения, не изменяя код клиента, который его использует.
Назначение: Основная цель Стратегии — предоставить способ изменения поведения объекта (контекста) без изменения его структуры. Это достигается путем вынесения алгоритмов в отдельные классы, что позволяет легко добавлять новые алгоритмы и динамически переключаться между ними. Паттерн Стратегия является отличным примером применения Принципа Открытости/Закрытости (Open/Closed Principle): система открыта для расширения (добавления новых стратегий), но закрыта для модификации (клиентский код не меняется).
Основные компоненты:
- Контекст (Context): Класс, который содержит ссылку на объект стратегии и делегирует ему выполнение алгоритма. Он не знает конкретной реализации стратегии, только ее интерфейс.
- Интерфейс Стратегии (Strategy Interface): Абстрактный класс или интерфейс, который объявляет общий метод для всех конкретных стратегий.
- Конкретные Стратегии (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: Система открыта для расширения новыми стратегиями, но закрыта для модификации.
Когда использовать паттерн Стратегия?
- Когда объект должен вести себя по-разному в зависимости от условий, и эти условия могут меняться.
- Когда у вас есть много похожих классов, которые отличаются только своим поведением.
- Когда нужно изолировать сложную бизнес-логику или алгоритмы от основного класса.
Ответ 18+ 🔞
Давай я тебе про этот паттерн «Стратегия» так расскажу, чтобы ты не просто понял, а чтобы у тебя в голове, блядь, щёлкнуло, как у кассового аппарата, когда покупка проходит.
Представь себе, ты в магазине. Ты — это контекст, понимаешь? Просто корзина с товарами. А вот кассирша — это твоя стратегия, блядь. Она может быть разная.
Вот подходишь ты с корзиной, а там сидит бабка Зина «Без скидки». Она тупо складывает цифры на калькуляторе и говорит: «С тебя, петушок, 150 рублей, плати». Это стратегия NoDiscountStrategy. Тупая, прямолинейная, нихуя не интересная.
А в соседней кассе сидит Ленка «Процентница» (PercentageDiscountStrategy). Глаза подведённые, говорит: «Ой, дорогой, у нас сегодня скидочка 10%, будет 135». Она умножает на свой коэфициент и всё.
А в третьей — Васька «От бороды» (FixedDiscountStrategy). Мужик здоровый, бородатый. Говорит хрипло: «От 150 рублей 20 списываю. Держи 130 и проваливай».
Суть в чём, ёпта? Твоя корзина-то одна и та же! Товары те же! А итоговая цена — пиздец как разная! Потому что ты подставляешь под свою корзину разных кассиров (стратегий). И корзине (контексту) похуй, кто там считает. Её дело — передать сумму и получить ответ. А уж Зина, Ленка или Васька будут там выёбываться со своими формулами — это их проблемы.
Вот смотри, как это в коде выглядит, только не засыпай:
from abc import ABC, abstractmethod
# Это типа общий устав для всех кассиров. Все должны уметь одну операцию: применить скидку.
class DiscountStrategy(ABC):
@abstractmethod
def apply_discount(self, price: float) -> float:
pass
# А это сами кассиры. Каждый — отдельный класс, живет своей жизнью.
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("Ты чё, мудила, скидку больше 100% делать собрался?")
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) # Чтобы в ноль уйти, а не в минус, а то Васька не поймёт
# А вот это и есть наша корзина — КОНТЕКСТ.
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. Скукотища.
# А теперь — ВНИМАНИЕ! — меняем стратегию на лету!
cart.set_discount_strategy(PercentageDiscountStrategy(0.10)) "Зина, отдыхай! Ленка, работай!"
print(f"С Ленкой (10%): {cart.calculate_total()}") # 135.0. Уже веселее.
cart.set_discount_strategy(FixedDiscountStrategy(20)) # "Ленка, вали! Васька, давай, руби с плеча!"
print(f"С Васькой (-20): {cart.calculate_total()}") # 130.0. Во, агрессивно!
И вся магия, ёпта, в том, что корзина ShoppingCart — она тупая как пробка. Она не знает, как там проценты считаются или фиксированная сумма вычитается. Её дело — спросить: «Эй, стратегия, вот сумма, что делать-то?». А стратегия уже сама решает, по своим внутренним, ебейшим правилам.
Зачем этот цирк?
А затем, сука, чтобы не было в коде этих бесконечных if:
if user_type == "vip":
price = price * 0.8
elif day == "black_friday":
price = price - 50
elif coupon == "PIZDA":
price = 0
else:
price = price * 1.1 # Налог на дурака
Это пиздец, а не код. Добавишь новую акцию — придёшь весь этот блок переписывать, а там уже 25 условий. А со стратегией ты просто создашь новый класс BlackFridayDiscountStrategy и подсунешь его корзине. И ни одна старая строчка кода не дрогнет! Это и есть принцип «открыт для расширения, закрыт для изменений». Красота, блядь, а не паттерн.
Итог: Стратегия — это когда ты выносишь мозг разной логики в отдельные штуки и подсовываешь их основной программе, как сменные насадки на дрель. Хочешь сверлить — ставь сверло, хочешь мешать — ставь миксер. Дрель одна, а возможностей — овердохуища.