(2024.05.01 Wed @KLN)
策略模式是行為模式的一種,定義了一族算法/策略,每種策略封裝(encapsulate)在其自身的類中。這些策略實現(xiàn)相似的功能,但存在一定程度的差別。如果不使用設(shè)計模式,這些策略往往導(dǎo)致if-else的大量使用。策略模式允許所有算法和策略可互相取代(interchangeable),使用戶可以在運行代碼時根據(jù)需求不同選擇不同的策略并且不需要更改代碼。
(2024.04.21 Sun @KLN)
Case 1: Crypto
Crypto的自動交易系統(tǒng)中,設(shè)置一個應(yīng)用類對接客戶端,根據(jù)不同的情況使用不同的銷售策略。銷售接口類包含方法sell_crypto
,該案例中實現(xiàn)三種不同的銷售策略。
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
運行結(jié)果
>>> 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
第二步,實現(xiàn)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.")
第三部,創(chuàng)建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維護(hù)了一個對策略的引用,允許用戶通過設(shè)定在不同的策略中做切換與選擇
- strategy/策略:strategy interface/abstract class聲明了一個或多個方法,用于定義被使用的算法
- concrete strategies/具體策略:具體策略實現(xiàn)了strategy interface,提供了算法的具體實現(xiàn)
優(yōu)缺點、適用場景
優(yōu)點
- 符合Open/Close principle:不改變客戶端/context class代碼,就可引入新策略
- 符合Isolation:不同算法的具體實現(xiàn)細(xì)節(jié)保持獨立,不影響彼此
- Encapsulation:不同算法的具體實現(xiàn)內(nèi)容被完整封裝在策略類中,修改時不會影響context class。
- run-time switching/運行時切換:應(yīng)用或用戶可在運行時切換策略
缺點
- 創(chuàng)建額外對象:多數(shù)情況下需要使用策略對象設(shè)置context對象,所以需要創(chuàng)建和維護(hù)兩個對象
- awareness among clients:用戶需要知道不同策略的差別方可選擇最佳策略
- 提升復(fù)雜度:隨著應(yīng)用場景越來越復(fù)雜,會有越來越多的策略需要創(chuàng)建和維護(hù)
場景
- 相似案例:相似的類,僅僅在執(zhí)行方式上有差別
- 需要隔離:需要在業(yè)務(wù)邏輯與算法實現(xiàn)之間實現(xiàn)隔離
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