工厂函数应通过装饰器自动注册类到字典映射实现解耦,避免修改if-elif链;用abc约束统一接口,各业务类继承并实现抽象方法;注册需确保模块导入触发,校验继承关系,且遵守团队注册约定。

工厂函数怎么设计才支持新增业务类不改旧代码
核心是把类注册和实例化解耦,避免每次加新业务都要去修改工厂的 if-elif 链。用字典做映射 + 装饰器自动注册最稳妥。
- 定义一个空字典
registry = {}存类名到类的映射 - 写个装饰器
@register_handler("pay"),作用是把被修饰的类塞进registry - 工厂函数只依赖
registry.get(name),不存在的 key 直接抛ValueError - 新增业务类时,只需加装饰器、写类,完全不碰工厂逻辑
为什么不用 globals() 或 importlib 动态导入类
看似灵活,实则容易失控:模块路径写错、循环导入、命名冲突、IDE 无法跳转、单元测试难 mock。
-
globals().get("OrderHandler")要求类必须在当前模块全局命名空间,不适用于包结构 -
importlib.import_module("handlers.refund").RefundHandler需硬编码字符串路径,重构时极易断 - 装饰器注册在模块加载时就完成,IDE 和静态检查工具能识别依赖关系
- 测试时可直接 patch
registry字典,无需动 import 行为
如何让工厂返回的对象支持统一接口但行为不同
靠抽象基类(ABC)约束接口,每个业务类继承它并实现抽象方法,工厂只管返回实例,调用方只认接口。
- 定义抽象类
class BaseHandler(ABC),声明@abstractmethod def execute(self, data: dict) -> dict: - 各业务类如
PayHandler、RefundHandler继承BaseHandler并实现execute - 工厂函数返回前做类型检查:
if not isinstance(instance, BaseHandler): raise TypeError - 调用方只写
handler.execute(payload),完全不知道背后是哪个具体类
生产环境要注意注册时机和线程安全
模块导入时注册是默认行为,但如果业务类分散在多个子模块,得确保它们都被 import 过,否则 registry 里没条目。
- 在包的
__init__.py中显式 import 所有 handler 模块,触发注册 - 注册字典本身是模块级变量,CPython GIL 下多线程读安全,但禁止运行时动态删改
registry - 如果要用热加载(比如配置驱动新类),就得加锁 + copy-on-write,复杂度陡增,多数业务没必要
- 上线前加个校验脚本:遍历
registry.values(),确认每个类都继承BaseHandler
KeyError 才发现,这种错误不会在 CI 阶段暴露。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











