今天我们来聊一个在消除复杂业务分支、提升代码可扩展性方面立竿见影的经典行为型模式——策略模式(Strategy Pattern)。
Day 4:策略模式 (Strategy Pattern)
1. 它是怎么来的?
在日常开发中,我们经常需要根据不同的条件执行不同的业务逻辑。
比如你正在开发一个电商系统的购物车结算模块,最初只有“普通会员”不打折:
1 2 3
| def calculate_price(price, user_level): if user_level == "normal": return price
|
随着业务发展,运营同学提出了各种花式促销:黄金会员打 9 折,白金会员打 8 折,双十一满 300 减 50,618 还有限时秒杀……于是你的代码变成了这样:
1 2 3 4 5 6 7 8 9
| if user_level == "normal": return price elif user_level == "gold": return price * 0.9 elif user_level == "platinum": return price * 0.8 elif festival == "double_11" and price >= 300: return price - 50
|
策略模式就是为了终结这种 if-elif-else 泥潭而生的。
它主张:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。
策略模式让算法的变化独立于使用它的客户端。在运行时,你只需要根据不同的上下文(Context)为它注入对应的具体策略对象即可。
2. 适用场景
- 多分支算法切换:一个系统需要动态地在几种算法或业务规则中选择一种,且分支逻辑非常臃肿。
- 屏蔽算法底层差异:你想封装算法的实现细节,让客户端只关心“做什么”,而不关心“怎么做”。
- 消除条件分支语句:代码中出现了大量的 if-elif-else 或 switch-case,且这些分支唯一的区别仅仅是执行的具体行为不同。
3. 优缺点分析
| 优点 |
缺点 |
| 完美符合开闭原则:新增一种算法或策略时,只需增加一个新的策略类,无需修改原有的上下文逻辑。 |
客户端必须知晓所有策略:调用者需要知道有哪些策略可供选择,以便决定在什么情况下使用哪一个策略。 |
| 避免多重条件判断:用面向对象或动态组合的多态性,彻底消除臃肿的 if-elif-else 分支。 |
策略类数量可能激增:每个策略都需要独立封装,如果策略非常多,会导致项目中类的数量显著增加。 |
| 极高复用性:策略类与具体的上下文解耦,可以被应用到系统中的其他模块。 |
|
4. Python 代码实现
Python 支持“函数是一等公民”,这意味着我们不仅可以用传统的面向对象方式实现策略模式,还可以用直接传递函数的高级玩法,让代码更加轻量。
💡 简单实例:电商打折季
我们来实现上述的电商结算场景,首先使用标准的面向对象方式来理解它的基本结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| from abc import ABC, abstractmethod
class DiscountStrategy(ABC): @abstractmethod def calculate(self, price: float) -> float: pass
class NormalUserStrategy(DiscountStrategy): """普通用户:原价""" def calculate(self, price: float) -> float: return price
class GoldUserStrategy(DiscountStrategy): """黄金会员:9 折""" def calculate(self, price: float) -> float: return price * 0.9
class PlatinumUserStrategy(DiscountStrategy): """白金会员:8 折""" def calculate(self, price: float) -> float: return price * 0.8
class OrderContext: def __init__(self, price: float): self.price = price self._strategy = None
def set_strategy(self, strategy: DiscountStrategy): """动态注入或切换策略""" self._strategy = strategy
def get_final_price(self) -> float: if not self._strategy: raise ValueError("尚未配置计价策略!") return self._strategy.calculate(self.price)
if __name__ == "__main__": original_price = 1000.0 order = OrderContext(original_price) print(f"商品原价: ¥{original_price}")
order.set_strategy(NormalUserStrategy()) print(f"💰 [普通用户] 最终应付: ¥{order.get_final_price():.2f}")
order.set_strategy(GoldUserStrategy()) print(f"💰 [黄金会员] 最终应付: ¥{order.get_final_price():.2f}")
order.set_strategy(PlatinumUserStrategy()) print(f"💰 [白金会员] 最终应付: ¥{order.get_final_price():.2f}")
|
🚀 复杂实例:一键切换的轻量级支付路由 (结合 Python 函数特性)
在实际工业开发中,如果每个策略只有简单的几行逻辑,硬写一堆类会显得有些大炮打蚊子。
利用 Python 的函数式编程特性和装饰器字典映射,我们可以在运行时优雅地建立一个“策略路由表”,不仅不需要写任何 ABC 抽象基类,还能保持完美的开闭原则。
下面是一个支持动态扩展的第三方支付路由系统(支持微信、支付宝、贝宝、数字货币):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| from typing import Callable, Dict
class PaymentRouter: """ 通过装饰器注册机制实现的轻量级策略中心 """ _strategies: Dict[str, Callable[[float], str]] = {}
@classmethod def register(cls, pay_method: str) -> Callable: """ 装饰器:用于在运行时动态注册支付策略函数 """ def decorator(func: Callable) -> Callable: cls._strategies[pay_method.lower()] = func return func return decorator
@classmethod def execute_payment(cls, pay_method: str, amount: float) -> str: """ 上下文网关:动态获取策略并执行 """ strategy = cls._strategies.get(pay_method.lower()) if not strategy: raise ValueError(f"❌ 不支持的支付方式: {pay_method}") return strategy(amount)
@PaymentRouter.register("wechat") def pay_via_wechat(amount: float) -> str: return f"[微信支付] 成功拉取微信 SDK,已扣款 ¥{amount},手续费 0.1%"
@PaymentRouter.register("alipay") def pay_via_alipay(amount: float) -> str: return f"[支付宝] 成功调起手机支付宝,已扣款 ¥{amount},使用花呗分期服务"
@PaymentRouter.register("paypal") def pay_via_paypal(amount: float) -> str: usd_amount = round(amount / 8.0, 2) return f"[PayPal] 跨境网关认证成功,已扣除 ${usd_amount} (折合 ¥{amount})"
@PaymentRouter.register("bitcoin") def pay_via_bitcoin(amount: float) -> str: return f"[加密货币] 链上广播成功,已转账对应价值 ¥{amount} 的 BTC,等待区块确认"
if __name__ == "__main__": order_amount = 2400.00 print(f"🛒 订单总额: ¥{order_amount}\n")
for method in ["wechat", "alipay", "paypal", "bitcoin"]: result = PaymentRouter.execute_payment(method, order_amount) print(result)
try: PaymentRouter.execute_payment("visa", order_amount) except ValueError as e: print(f"\n捕获预期异常: {e}")
|