Паттерн Strategy
Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 48 из 64.
Паттерн Стратегия определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Вы можете переключать алгоритмы во время выполнения без изменения клиентского кода.
Вот простые классы стратегий для различных способов оплаты:
class CreditCard:
def pay(self, amount):
return f"Paid ${amount} with Credit Card"
class PayPal:
def pay(self, amount):
return f"Paid ${amount} with PayPal"
class Bitcoin:
def pay(self, amount):
return f"Paid ${amount} with Bitcoin"Каждая стратегия реализует один и тот же метод (pay), но с различным поведением.
Создайте класс контекста, который использует стратегии:
class ShoppingCart:
def __init__(self):
self.total = 0
self.payment_strategy = None
def add_item(self, price):
self.total += price
def set_payment_strategy(self, strategy):
self.payment_strategy = strategy
def checkout(self):
return self.payment_strategy.pay(self.total)Класс контекста может переключаться между различными стратегиями оплаты.
Используйте паттерн «Стратегия»:
cart = ShoppingCart()
cart.add_item(50)
cart.add_item(30)
# Используем стратегию оплаты кредитной картой
cart.set_payment_strategy(CreditCard())
print(cart.checkout())
# Переключаемся на стратегию PayPal
cart.set_payment_strategy(PayPal())
print(cart.checkout())Создайте еще один пример со стратегиями сортировки:
class BubbleSort:
def sort(self, data):
return f"Bubble sorted: {sorted(data)}"
class QuickSort:
def sort(self, data):
return f"Quick sorted: {sorted(data)}"
class Sorter:
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def sort_data(self, data):
return self.strategy.sort(data)
# Используйте различные стратегии сортировки
numbers = [3, 1, 4, 1, 5]
sorter = Sorter(BubbleSort())
print(sorter.sort_data(numbers))
sorter.set_strategy(QuickSort())
print(sorter.sort_data(numbers))Вывод:
Paid $80 with Credit Card
Paid $80 with PayPal
Bubble sorted: [1, 1, 3, 4, 5]
Quick sorted: [1, 1, 3, 4, 5]Ключевой момент: Паттерн «Стратегия» (Strategy Pattern) позволяет менять алгоритмы во время выполнения программы. Определите различные стратегии с одним и тем же интерфейсом, а затем позвольте контекстному классу выбирать, какую из них использовать. Это делает ваш код гибким и позволяет легко расширять его новыми алгоритмами без изменения существующего кода.
Задание
СреднеРеализуйте новую стратегию BitcoinPayment для нашей системы покупок.
Ваша задача:
- Создать класс BitcoinPayment, который реализует PaymentStrategy
- Он должен принимать wallet_address в конструкторе
- Метод pay должен выводить ровно:
Paying $X using Bitcoin wallet: Y, где X — это сумма, а Y — адрес кошелька - Метод pay должен возвращать True после вывода сообщения
- Написать функцию main, которая:
- Создает корзину покупок (shopping cart)
- Добавляет в корзину ноутбук стоимостью $1200 и наушники стоимостью $100
- Создает BitcoinPayment с адресом кошелька "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
- Устанавливает эту стратегию оплаты для корзины
- Вызывает метод checkout
Начальный код уже включает if __name__ == "__main__": внизу — вам не нужно добавлять это самостоятельно. Это соглашение Python, которое гарантирует, что ваша функция main() будет запущена только при прямом выполнении файла (а не при его импорте другим модулем). Просто определите свою функцию main(), и предоставленный блок вызовет ее автоматически.
Следуйте той же структуре, которая была показана в примерах урока.
Шпаргалка
Паттерн «Стратегия» определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Вы можете переключать алгоритмы во время выполнения без изменения клиентского кода.
Создайте классы стратегий с одинаковым интерфейсом:
class CreditCard:
def pay(self, amount):
return f"Paid ${amount} with Credit Card"
class PayPal:
def pay(self, amount):
return f"Paid ${amount} with PayPal"
class Bitcoin:
def pay(self, amount):
return f"Paid ${amount} with Bitcoin"Создайте класс контекста, который использует стратегии:
class ShoppingCart:
def __init__(self):
self.total = 0
self.payment_strategy = None
def add_item(self, price):
self.total += price
def set_payment_strategy(self, strategy):
self.payment_strategy = strategy
def checkout(self):
return self.payment_strategy.pay(self.total)Используйте паттерн «Стратегия»:
cart = ShoppingCart()
cart.add_item(50)
cart.add_item(30)
# Используем стратегию оплаты кредитной картой
cart.set_payment_strategy(CreditCard())
print(cart.checkout())
# Переключаемся на стратегию PayPal
cart.set_payment_strategy(PayPal())
print(cart.checkout())Еще один пример со стратегиями сортировки:
class BubbleSort:
def sort(self, data):
return f"Bubble sorted: {sorted(data)}"
class QuickSort:
def sort(self, data):
return f"Quick sorted: {sorted(data)}"
class Sorter:
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def sort_data(self, data):
return self.strategy.sort(data)Попробуйте сами
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass
class CreditCardPayment(PaymentStrategy):
def __init__(self, card_number, expiry_date, cvv):
self.card_number = card_number
self.expiry_date = expiry_date
self.cvv = cvv
def pay(self, amount):
print(f"Paying ${amount} using Credit Card: {self.card_number}")
return True
class PayPalPayment(PaymentStrategy):
def __init__(self, email, password):
self.email = email
self.password = password
def pay(self, amount):
print(f"Paying ${amount} using PayPal account: {self.email}")
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, payment_strategy):
self.payment_strategy = payment_strategy
def checkout(self):
total = sum(item["price"] for item in self.items)
if self.payment_strategy:
return self.payment_strategy.pay(total)
else:
raise ValueError("No payment strategy set")
# Создайте свой класс стратегии BitcoinPayment здесь
if __name__ == "__main__":
main()В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — Простой калькулятор4Наследование
Базовое наследованиеФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовИтоги — Иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy5Полиморфизм
Снова о переопределении методовУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовИтоги — Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыИтоги — Менеджер банковского счета6Инкапсуляция
Public, Protected, Private члены классаМодификаторы доступаСокрытие данныхДекораторы property: продвинутый уровеньИтоги: Система учета студентов12Проект: Управление библиотекой
Обзор проектаКлассы Book и User