(2024.05.01 Wed @KLN)
策略模式是行为模式的一种,定义了一族算法/策略,每种策略封装(encapsulate)在其自身的类中。这些策略实现相似的功能,但存在一定程度的差别。如果不使用设计模式,这些策略往往导致if-else的大量使用。策略模式允许所有算法和策略可互相取代(interchangeable),使用户可以在运行代码时根据需求不同选择不同的策略并且不需要更改代码。
(2024.04.21 Sun @KLN)
Case 1: Crypto
Crypto的自动交易系统中,设置一个应用类对接客户端,根据不同的情况使用不同的销售策略。销售接口类包含方法sell_crypto
,该案例中实现三种不同的销售策略。
from abc import ABC, abstractmethod
class SellStrategy(ABC):
""" it sells BTC and buys USDT """
@abstractmethod
def sell_crypto(self, balance: float, currency: float) -> dict:
"""sells crypto and returns new balance"""
class SellAll(SellStrategy):
def sell_crypto(self, balance: float, currency: float) -> dict:
"""critical!! Market doesn't look nice. Sell!"""
btc = 0
usdt = balance * currency
return {"btc":btc, "usdt": usdt}
class SellHalf(SellStrategy):
def sell_crypto(self, balance: float, currency: float) -> dict:
""" cautious! let's sell half and wait! """
btc = balance / 2
usdt = (balance / 2) * currency
return {"btc":btc, "usdt": usdt}
class SellLittle(SellStrategy):
def sell_crypto(self, balance: float, currency: float) -> dict:
""" HODL! """
btc = balance * 0.9
usdt = (balance * 0.1) * currency
return {"btc":btc, "usdt": usdt}
class TradingApp:
assets = {"btc":100,"usdt":0}
currency = 30000
def sell_order(self, sell_decision: SellStrategy):
self.assets = sell_decision.sell_crypto(self.assets["btc"], self.currency)
return self.assetsfrom abc import ABC, abstractmethod
运行结果
>>> A = TradingApp()
>>> assets = A.sell_order(SellLittle())
>>> print(assets)
{'btc': 90.0, 'usdt': 300000.0}
>>> assets = A.sell_order(SellHalf())
>>> print(assets)
{'btc': 45.0, 'usdt': 1350000.0}
Case 2: Payment Strategy
(2024.05.01 Wed @KLN)
使用不同支付方式支付。
首先定义strategy interface (abstract class)
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass
第二步,实现concrete strategies
class CreditCardPayment(PaymentStrategy):
def pay(self, amount):
print(f"Paid {amount} using Credit Card.")
class PayPalPayment(PaymentStrategy):
def pay(self, amount):
print(f"Paid {amount} using PayPal.")
class BankTransferPayment(PaymentStrategy):
def pay(self, amount):
print(f"Paid {amount} using PayPal.")
第三部,创建context class
class PaymentContext:
def __init__(self, payment_strategy):
self.payment_strategy = payment_strategy
def set_payment_strategy(self, payment_strategy):
self.payment_strategy = payment_strategy
def make_payment(self, amount):
self.payment_strategy.pay(amount)
第四步,使用strategy pattern
if __name__ == "__main__":
credit_card = CreditCardPayment()
paypal = PaPalPayment()
bank_transfer = BankTransferPayment()
payment_context = PaymentContext()
payment_context.set_payment_strategy(credit_card)
payment_context.make_payment(99) # 使用信用卡支付99
payment_context.set_payment_strategy(paypal)
payment_context.make_paymet(100) # 使用paypal支付100
payment_context.set_payment_strategy(bank_transfer)
payment_context.make_payment(101) # 使用bank transfer支付101
通过该案例回顾策略模式的基本元素
- context/前后文:context class维护了一个对策略的引用,允许用户通过设定在不同的策略中做切换与选择
- strategy/策略:strategy interface/abstract class声明了一个或多个方法,用于定义被使用的算法
- concrete strategies/具体策略:具体策略实现了strategy interface,提供了算法的具体实现
优缺点、适用场景
优点
- 符合Open/Close principle:不改变客户端/context class代码,就可引入新策略
- 符合Isolation:不同算法的具体实现细节保持独立,不影响彼此
- Encapsulation:不同算法的具体实现内容被完整封装在策略类中,修改时不会影响context class。
- run-time switching/运行时切换:应用或用户可在运行时切换策略
缺点
- 创建额外对象:多数情况下需要使用策略对象设置context对象,所以需要创建和维护两个对象
- awareness among clients:用户需要知道不同策略的差别方可选择最佳策略
- 提升复杂度:随着应用场景越来越复杂,会有越来越多的策略需要创建和维护
场景
- 相似案例:相似的类,仅仅在执行方式上有差别
- 需要隔离:需要在业务逻辑与算法实现之间实现隔离
Reference
1 levelup点gitconnected, Design Patterns in Python: Strategy Pattern, Okan Yenigun
2 medium, Exploring the Strategy Design Pattern in Python: A Guide with Examples, Nitesh Bhargav
3 geeksforgeeks