简单工厂模式应优先用直白的if/else或dict映射,因其在Python中更可靠、易调试;避免注册表引发初始化顺序、循环引用等问题,所有分支须有明确else或异常兜底,参数需标准化处理。

为什么直接用 if/else 分支比注册表更合适
简单工厂模式的核心是「根据参数返回不同类实例」,不是为了炫技或提前铺架构。Python 动态类型 + 函数即对象的特性,让最直白的 if/else 或 dict 映射反而最可靠、最易调试。强行套用“注册+装饰器”容易在初始化顺序、模块循环引用、测试 mock 时翻车。
常见错误现象:NameError: name 'ConcreteProductA' is not defined(类定义在工厂之后)、AttributeError: 'NoneType' object has no attribute 'execute'(参数没匹配上任何分支但没兜底)。
- 优先用函数实现工厂逻辑,避免类状态干扰
- 所有分支必须有明确的
else或raise ValueError,不静默返回None - 参数校验放在工厂入口,比如统一转小写、strip 空格,别让调用方承担格式责任
用 dict 映射替代长 if/else 的实际写法
当产品类不多(≤5 个)、且类名稳定可预测时,dict 映射比嵌套 if 更紧凑,也方便后续加日志或缓存。但注意:字典值必须是**未调用的类或可调用对象**,不是实例。
示例:
立即学习“Python免费学习笔记(深入)”;
class ProductA:
def run(self): return "A"
<p>class ProductB:
def run(self): return "B"</p><h1>✅ 正确:存类本身</h1><p>PRODUCT_MAP = {
"a": ProductA,
"b": ProductB,
}</p><p>def create_product(type_name: str):
cls = PRODUCT_MAP.get(type_name.lower())
if not cls:
raise ValueError(f"Unknown product type: {type_name}")
return cls() # 这里才实例化
容易踩的坑:PRODUCT_MAP = {"a": ProductA()} —— 模块加载时就执行实例化,可能触发副作用或依赖未就绪。
什么时候该把工厂做成类而不是函数
只有当你需要共享状态(如连接池、配置上下文)或复用初始化逻辑时,才值得封装成类。否则函数更轻量、更易单元测试。
- 类工厂适合场景:构造实例需传入相同 config 对象、需预热缓存、需记录创建次数
- 函数工厂适合场景:纯参数驱动、无外部依赖、各实例完全独立
- 如果用了类工厂,别把
__init__做成重操作;初始化耗时逻辑应延迟到create()中
示例中若需传 config:
class ConfigurableFactory:
def __init__(self, timeout: int):
self.timeout = timeout
<pre class="brush:php;toolbar:false;">def create(self, type_name: str):
cls = PRODUCT_MAP.get(type_name.lower())
if not cls:
raise ValueError(...)
return cls(timeout=self.timeout) # 把 config 透传给具体类
绕过 isinstance 的类型判断陷阱
不要在工厂内部用 isinstance(obj, ProductA) 反向推导类型——这违背了工厂职责,也导致耦合。工厂只负责“按输入造实例”,类型判断是调用方的事。
真正要警惕的是:用户传入非法字符串(如空格、大小写混杂、特殊字符),而你的映射没覆盖。解决方案:
- 参数标准化:用
type_name.strip().lower()统一处理 - 预留扩展位:比如支持
"product_a"和"a"同时映射到ProductA - 拒绝模糊匹配:不自动截断、不尝试拼音转换,避免歧义
复杂点在于,一旦业务增长,简单工厂会迅速变成维护负担。这时候不是换“抽象工厂”或“建造者”,而是该问:是否真需要运行时动态决定类型?能不能用配置文件 + importlib 加载来解耦?


















