Python 策略模式在业务逻辑中的应用

7次阅读

策略类必须实现统一接口,否则运行时抛错;应使用ABC定义抽象基类、初始化时检查方法存在性、单元测试覆盖;策略对象不可持业务状态,动态数据须通过execute()参数传入;推荐注册表模式管理策略;第三方依赖应由上下文注入而非策略内部硬编码

Python 策略模式在业务逻辑中的应用

策略类必须实现统一接口,否则 runtime 报错

python 没有接口语法强制约束,但策略模式依赖「同一组方法签名」才能被上下文安全调用。如果某个策略类漏了 execute() 方法,或参数名不一致(比如一个用 data,另一个用 payload),运行时就会抛 AttributeErrorTypeError

实操建议:

  • ABC@abstractmethod 显式定义抽象基类,哪怕只是做文档和 ide 提示
  • 在上下文初始化时加一层检查:hasattr(strategy, 'execute') and callable(getattr(strategy, 'execute'))
  • 单元测试里对每个策略实例调用 execute(),传空字典或默认值,验证不崩

策略对象不该持有业务状态,否则切换时出问题

常见错误是把用户 ID、订单号、缓存引用等塞进策略实例的 self.user_idself.cache 里。一旦上下文复用该策略对象处理不同请求,状态就会串。

正确做法是把所有动态数据通过 execute() 参数传入:

立即学习Python免费学习笔记(深入)”;

class DiscountStrategy:     def execute(self, order_amount: float, user_tier: str) -> float:         # 不读 self.user_tier,只读参数         if user_tier == "vip":             return order_amount * 0.9         return order_amount

这样策略对象可安全地被多个线程/请求共享,也方便做依赖注入或工厂管理。

策略注册表用字典比 if-elif 链更易维护

当策略超过 3 个,硬编码 if strategy_type == "promo": return PromoStrategy() 很快失控。新增策略要改两处:类定义 + 分支逻辑。

推荐用注册表模式:

_STRATEGIES = {} 

def register_strategy(name): def decorator(cls): _STRATEGIES[name] = cls return cls return decorator

@register_strategy("cashback") class CashbackStrategy: ...

使用时

strategy = _STRATEGIES.get("cashback")()

好处:

  • 新增策略只需加 @register_strategy("xxx"),不碰调度逻辑
  • 策略列表可从配置文件加载(如 jsON 中的 "payment_strategy": "alipay"
  • 运行时能用 _STRATEGIES.keys() 做校验或调试输出

策略内部别直接调第三方 API,应由上下文注入 client

策略类里写 requests.post("https://api.xx.com/verify") 看似简单,但带来三个问题:无法 mock 测试、超时/重试逻辑重复、HTTP client 实例生命周期难管理。

应该把依赖显式传入:

class RiskCheckStrategy:     def __init__(self, http_client):         self.http_client = http_client  # 如 requests.session 或 httpx.AsyncClient 
def execute(self, user_id):     resp = self.http_client.post("/risk", json={"uid": user_id})     return resp.json().get("pass", False)

这样上层能统一配置 timeout、headers、mock client,也能按环境切换真实/沙箱 client。

真正容易被忽略的是策略组合场景——比如先走风控策略,再走折扣策略,两个策略都需要同一个 cache_client 实例。这时候靠构造函数注入比靠全局变量或单例更可控,也更容易做单元隔离。

text=ZqhQzanResources