今天我们来聊一个在消除复杂业务分支、提升代码可扩展性方面立竿见影的经典行为型模式——策略模式(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

# ================= 1. 抽象策略 (Strategy Interface) =================
class DiscountStrategy(ABC):
@abstractmethod
def calculate(self, price: float) -> float:
pass

# ================= 2. 具体策略 (Concrete Strategies) =================
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


# ================= 3. 上下文环境 (Context) =================
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}")