策略类必须继承统一接口(如abc.abc),用@abstractmethod声明核心方法(如apply()),确保类型安全、ide提示和调用方依赖抽象;注册应使用显式键名的字典,工厂负责初始化并注入依赖,状态交由外部组件管理。

策略类必须继承统一接口,否则类型检查和运行时都容易出错
Python没有强制接口,但不定义契约会导致后续策略替换时行为不一致。建议用 abc.ABC 和 @abstractmethod 声明核心方法,比如 execute() 或 calculate()。这样 IDE 能提示缺失实现,mypy 也能校验,更重要的是调用方能依赖抽象而非具体类。
常见错误是直接写一堆独立函数或普通类,结果发现新增策略后要改调度逻辑、漏了参数校验、返回值类型不一致——这些全是因为缺少顶层约束。
- 接口方法名保持一致(如统一叫
apply()),避免不同策略暴露不同入口 - 所有策略类构造函数参数尽量对齐,或用
**kwargs收口但文档注明必需字段 - 如果策略需访问上下文数据,通过构造函数注入,而不是在
apply()里传一堆参数
用字典注册策略比 if-else 更易维护,但键名必须可预测且唯一
把策略类按标识符存进字典,比如 {'discount_vip': VIPDiscount, 'discount_promo': PromoDiscount},调用时直接 strategies[rule_type].apply(...)。这比一长串 if rule == 'vip': ... elif rule == 'promo': ... 容易扩展也方便单元测试。
容易踩的坑是键名来自外部输入(如用户提交的字符串),没做白名单校验就直接查字典,会触发 KeyError。更糟的是用策略类名当 key(cls.__name__),一旦重命名就 silently 失效。
- 注册时显式声明 key,例如
register_strategy('vip', VIPDiscount) - 初始化策略容器时预加载全部策略,避免运行时动态 import 引发导入错误
- 提供默认策略兜底(如
strategies.get(rule_type, DefaultStrategy)()),而不是裸奔get()
策略工厂类应负责实例化,而非仅返回类对象
很多实现只返回类本身,导致每次调用都要手动 VIPDiscount(),既重复又难统一传参(比如所有策略都需要 logger 或 config)。工厂应该返回已初始化的实例,或至少封装好初始化逻辑。
另一个问题是把策略配置硬编码在工厂里,比如写死数据库连接或 API 密钥。这会让测试困难,也违反依赖倒置原则。
- 工厂接收依赖项(如
db_client,cache)作为参数,传递给各策略构造函数 - 支持从配置文件或环境变量读取策略启用开关,禁用某策略时直接跳过注册,而不是留着空壳
- 避免在工厂里做业务判断(如 “如果用户等级 > 5 就用 A 策略”),那是上下文的事,工厂只管创建
策略间共享状态需谨慎,全局变量或单例最容易引发并发问题
有些场景下多个策略需要共用缓存、计数器或连接池。直接用模块级变量或 staticmethod 持有状态,在多线程或多协程环境下极易出错:比如两个请求同时修改同一个计数器,结果少加一次。
更隐蔽的问题是策略类意外持有可变默认参数(如 def __init__(self, cache={}):),导致所有实例共享同一字典。
- 状态交给外部组件管理(如 Redis 缓存、thread-local 存储),策略只负责读写接口
- 若必须内部维护状态,用
__slots__+ 实例属性,杜绝可变默认参数 - 异步场景下尤其注意:
asyncio.Lock或contextvars.ContextVar才是安全选择,不是靠加await就万事大吉
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











