Skip to content

工厂方法 = 不变量建立器 ​

解决什么问题:复杂对象的创建逻辑容易泄露不变量—— 客户端需要知道 ID 生成、状态初始化、依赖校验、事件发布,才能"正确"创建一个对象。工厂把这一切封装为单一入口。

业务问题 ​

真实场景 ​

场景 1:电商订单创建的"5 步初始化"

python
# 反模式:客户端拼装
order = Order()
order.id = generate_id()           # 1. ID 生成
order.tenant_id = tenant.id         # 2. 租户绑定
order.customer_id = customer.id     # 3. 客户绑定
order.created_at = now()            # 4. 时间戳
order.status = "NEW"                # 5. 状态初始化
order.add_event(OrderCreated(...))  # 6. 事件发布

# 问题:
# - 漏一步 = 数据不一致
# - 多处复制 = 修改要改 N 处
# - 不变量(订单必须有客户)必须由客户端保证

Vernon Ch 11 引自 Evans 原话:

"Shift the responsibility for creating instances of complex objects and AGGREGATES to a separate object... Create entire AGGREGATES as a piece, enforcing their invariants."

失败模式 ​

失败模式后果
客户端拼装漏一步 = 数据不一致;客户端承担业务规则
构造函数复杂测试困难;子类化困难
工厂无业务规则工厂退化为"传话筒"
工厂依赖过多违反 SRP;难以测试

控制论视角 ​

Ch 4 §4.6:"没有反馈,就没有自动控制"——同样,没有自动校验,就没有一致的数据。

工厂 = 创建时的不变量自动建立:

创建前:状态不确定(参数可能错误)
        ↓
工厂方法:
  - 校验参数 → 不变量 1
  - 校验依赖 → 不变量 2
  - 初始化 → 不变量 3
  - 发布事件 → 状态变更通知
        ↓
创建后:状态确定(不变量已建立)

对位:

  • 工厂 = 控制律的初始化阶段(状态从任意 → 受控)
  • 不变量 = 受控状态的稳定条件(类比"设定点 setpoint")
  • 事件 = 状态变更的输出信号

核心概念 ​

工厂的 3 种形式 ​

形式位置适用Vernon 推荐度
Factory Method on Aggregate聚合根的方法简单场景★★★★★ 最常用
Factory on Domain Service领域服务跨聚合创建★★★
Standalone Factory独立类复杂组装★★

最常用:Factory Method on Aggregate。

Factory Method on Aggregate(标准模式) ​

python
class Order:
    def __init__(self, id, tenant_id, customer_id, status, created_at):
        # 构造函数是私有的(不对外暴露)
        self.id = id
        self.tenant_id = tenant_id
        self.customer_id = customer_id
        self.status = status
        self.created_at = created_at
        self._events = []
    
    @classmethod
    def create(cls, tenant_id, customer_id):
        # 1. 生成身份(不变量:必须有 ID)
        order_id = OrderId.generate()
        # 2. 校验不变量(不变量:必须有客户)
        if not customer_id:
            raise BusinessRuleViolation("Customer required")
        # 3. 初始化状态(不变量:状态必须是 NEW)
        order = cls(order_id, tenant_id, customer_id, "NEW", now())
        # 4. 发布事件(状态变更通知)
        order.add_event(OrderCreated(order_id, customer_id))
        return order

关键原则:

  • 工厂方法命名用业务语言(create, register, schedule)
  • 内部封装所有不变量检查
  • 返回的对象保证不变量已满足
  • 客户端不需要做任何校验

Factory as Domain Service(跨聚合场景) ​

python
class TenantRegistrationService:
    def register(self, tenant_info):
        # 跨聚合创建:Tenant + 初始用户 + 初始角色
        tenant = Tenant.create(tenant_info)
        admin_user = User.create_admin(tenant.id, tenant_info.admin_email)
        admin_role = Role.assign_admin(admin_user.id)
        
        # 全部原子操作
        self.tenant_repo.save(tenant)
        self.user_repo.save(admin_user)
        self.role_repo.save(admin_role)
        
        # 发布事件
        self.event_publisher.publish(TenantRegistered(tenant.id))

Standalone Factory(复杂组装) ​

python
class ProductFactory:
    """复杂产品创建:需要多个步骤、多种配置"""
    def create_bundle(self, bundle_spec):
        products = []
        for spec in bundle_spec.items:
            product = Product.create(spec)
            products.append(product)
        return ProductBundle(
            products=products,
            discount=bundle_spec.discount,
            valid_until=bundle_spec.valid_until
        )

工程实践 ​

决策 1:何时用工厂 ​

问 3 个问题:
  1. 创建过程 > 3 步?
     - 是 → 用工厂
  2. 创建需要保证不变量?
     - 是 → 用工厂
  3. 多个创建变体?
     - 是 → 用工厂(封装变体)

决策 2:选哪种工厂 ​

决策树:
  创建需要其他聚合数据?
    - 是 → 领域服务工厂
    - 否 → 聚合根工厂
  聚合根工厂的实现复杂?
    - 是 → 独立工厂类
    - 否 → 简单类方法

反模式 vs 正模式 ​

反模式正模式
简单对象也用工厂复杂对象(>3 步)才用
工厂只做"传话筒"工厂封装业务逻辑
构造函数公开(客户端可直接 new)构造函数私有/受保护
工厂依赖太多服务工厂依赖最小化

与已有 L 层的关系 ​

性能指标 ​

指标衡量健康值
工厂覆盖度复杂对象中工厂占比> 90%
工厂的复杂度工厂方法平均行数< 30 行
不变量在工厂的覆盖率不变量由工厂保证的比例> 95%
工厂重用度工厂被多少处调用> 1

边界判断 ​

何时用工厂(★ 必须) ​

  • 创建过程 > 3 步
  • 必须保证不变量
  • 多个创建变体
  • 跨聚合创建

何时用构造函数(★ 不要工厂) ​

  • 简单值对象(1-2 步)
  • 无业务规则
  • 单一变体

何时升级 ​

当前升级到触发条件
Factory MethodDomain Service Factory跨聚合
Domain Service FactoryStandalone Factory组装 > 50 行

关键洞察 ​

  1. 工厂 = 不变量建立器——这是工厂在 DDD 中的核心价值,不只是"创建对象"。
  2. 工厂方法 = 状态初始化——把"任意状态 → 受控状态"的转换封装为单一入口。
  3. 工厂应该薄——工厂只做"建立不变量",不做"业务规则"。
  4. 测试友好——客户端不需要构造完整参数,只需要传业务参数。
  5. 与 cybernetics 同构:控制律的"初始条件建立" + DDD 的"不变量建立" = 同一个数学原型。

从名家方法论与工程化思路中蒸馏出自己的工程体系。