策略模式Strategy Pattern, 2024-04-21

(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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容