# 工厂模式（Factory）

URL: https://caijiao.org/ddd/chapter2/factory
Source: docs/ddd/chapter2/factory.md
Description: 介绍工厂模式在领域驱动设计中的作用，以及如何使用工厂模式创建复杂领域对象，保持系统的一致性与解耦。

在领域驱动设计（DDD）中，**工厂模式（Factory）** 是一个常用的设计模式，主要用于创建复杂对象。工厂模式允许系统将对象的创建过程从业务逻辑中解耦，提供更好的灵活性和可扩展性。

工厂模式对于创建聚合根、复杂的实体、或具有特定业务逻辑的值对象尤其重要。通过将这些复杂对象的创建过程委托给工厂类，系统能够更容易地进行单元测试、代码维护，并在需要时对对象的创建逻辑进行修改。

## 工厂模式的概念

工厂模式是一种创建对象的设计模式，它通过定义一个创建对象的接口，让子类决定实例化哪一个类。工厂方法将对象的创建委托给工厂类，而不是直接在代码中实例化对象。工厂模式通常用于以下场景：

- 对象的创建过程比较复杂，涉及多个步骤。
- 对象的创建需要根据不同条件选择不同的实例。
- 需要避免在客户端代码中暴露对象的具体创建过程。

## 工厂模式的类型

1. **简单工厂模式**：通过一个工厂方法根据条件返回不同类型的对象。
2. **工厂方法模式**：定义一个接口来创建对象，由子类决定具体创建哪个对象。
3. **抽象工厂模式**：提供一个接口，用于创建一组相关或相互依赖的对象。

## 工厂模式在 DDD 中的应用

在 DDD 中，工厂模式常常用于创建复杂的聚合根或值对象。由于这些对象通常包含领域逻辑，直接在代码中创建这些对象会使得业务代码与领域模型紧密耦合，从而降低了灵活性和可维护性。工厂模式帮助我们将对象的创建和业务逻辑分离，从而提高了代码的可测试性和可扩展性。

### 示例：创建订单聚合根

在电商系统中，`Order` 是一个聚合根，它可能会包含多个订单项，并且在创建时需要进行一些复杂的业务逻辑处理。我们可以通过工厂模式来封装订单的创建过程。

```java
public class OrderFactory {

    public Order createOrder(String customerId, List<OrderItem> items) {
        // 校验订单项是否合法
        if (items == null || items.isEmpty()) {
            throw new IllegalArgumentException("订单项不能为空");
        }

        // 创建订单对象
        Order order = new Order(customerId);

        // 添加订单项
        for (OrderItem item : items) {
            order.addItem(item);
        }

        // 其他业务逻辑，例如生成订单编号等
        order.generateOrderNumber();

        return order;
    }
}
```

在这个例子中，`OrderFactory` 负责创建订单对象，并将订单项添加到订单中。通过工厂方法，我们将订单的创建过程封装起来，避免了直接在业务逻辑中创建复杂对象的风险。

### 示例：创建值对象

除了聚合根，值对象也常常需要通过工厂模式进行创建，尤其是在构造值对象时需要进行复杂验证时。比如，创建一个包含地址信息的 `Address` 值对象。

```java
public class AddressFactory {

    public Address createAddress(String street, String city, String zipCode) {
        // 校验地址是否合法
        if (street == null || city == null || zipCode == null) {
            throw new IllegalArgumentException("地址字段不能为空");
        }

        // 创建并返回 Address 值对象
        return new Address(street, city, zipCode);
    }
}
```

在这个例子中，`AddressFactory` 用于创建 `Address` 值对象，确保传入的参数合法。

## 工厂模式的优点

1. **解耦对象创建过程**：通过工厂模式，可以将对象的创建过程从业务逻辑中解耦出来，使得业务逻辑更加专注于领域行为，而不需要关心对象的具体创建细节。
2. **增强代码可维护性**：对象创建逻辑集中在工厂中，修改创建逻辑时只需修改工厂类，而不需要在代码的各个地方进行修改。
3. **提高代码可测试性**：使用工厂模式可以方便地替换对象的创建逻辑，尤其是在单元测试中，可以使用模拟对象（mock）来替代实际的对象创建。
4. **支持对象的复杂创建过程**：当对象的创建过程涉及多个步骤、条件判断或复杂逻辑时，工厂模式可以很好地封装这些细节。

## 工厂模式的最佳实践

1. **将复杂创建逻辑集中在工厂类中**：尽量避免在其他地方（如服务类或控制器中）直接创建复杂对象。工厂类应该负责处理所有与对象创建相关的逻辑。
2. **避免工厂类过于庞大**：工厂类的职责是创建对象，但如果工厂类承担了过多的责任（例如，执行业务逻辑），会违背单一职责原则。应确保工厂类的职责清晰、简洁。
3. **使用工厂方法或抽象工厂模式**：如果系统中存在多种不同的对象创建方式，可以考虑使用工厂方法模式或抽象工厂模式，进一步提升系统的扩展性和灵活性。

## 总结

工厂模式在领域驱动设计中是一个重要的设计模式，它有助于将对象的创建与业务逻辑解耦，简化代码结构，提升代码的可维护性和可测试性。通过使用工厂模式，我们能够更好地管理复杂的对象创建过程，确保系统能够灵活应对变化。
