PHP工厂模式创建领域对象吗

wen PHP项目 1

PHP工厂模式创建领域对象的最佳实践与争议

文章导读

  • 工厂模式在PHP领域建模中的角色定位
  • 领域驱动设计(DDD)与工厂模式的核心关联
  • 三种主流工厂模式实现对比(静态工厂、简单工厂、抽象工厂)
  • 实战案例:用工厂模式封装复杂领域对象创建
  • 常见误区与性能考量
  • 问答环节:工厂模式是否必须用于领域对象?

工厂模式与领域对象的天然契合性

在PHP面向对象开发中,工厂模式(Factory Pattern) 的核心价值在于将对象的创建逻辑与业务逻辑解耦,当领域对象(Domain Object)具有以下特征时,工厂模式成为无法回避的选择:

PHP工厂模式创建领域对象吗

  1. 复杂构造过程:需要依赖注入、参数校验、默认值设置
  2. 多态创建需求:根据上下文返回不同的具体领域对象
  3. 统一创建规则:确保所有实例符合领域不变量(Invariants)

关键问题:PHP工厂模式是否“适合”创建领域对象?答案是——它不仅是适合,更是领域驱动设计中的标准实践,Eric Evans在《领域驱动设计》中明确将工厂模式列为领域对象的推荐创建方式。


三种主流工厂模式在PHP中的实现

静态工厂方法(Static Factory Method)

class OrderFactory {
    public static function createFromCart(Cart $cart): Order {
        // 领域规则:根据购物车创建订单
        return new Order($cart->getItems(), $cart->getTotal());
    }
    public static function createEmpty(): Order {
        return new Order([], 0.0);
    }
}

优点:使用简便,无需实例化工厂
缺点:不易扩展,违反开闭原则

简单工厂(Simple Factory)

class PaymentFactory {
    public function create(string $type): PaymentInterface {
        return match($type) {
            'alipay' => new AlipayPayment(),
            'wechat' => new WechatPayment(),
            default => throw new InvalidArgumentException("Invalid payment type: $type")
        };
    }
}

适用场景:同一接口的不同实现切换

抽象工厂(Abstract Factory)

interface ProductFamilyFactory {
    public function createMainProduct(): Product;
    public function createAccessory(): Accessory;
}
class PremiumProductFactory implements ProductFamilyFactory {
    public function createMainProduct(): Product {
        return new PremiumProduct();
    }
    public function createAccessory(): Accessory {
        return new PremiumAccessory();
    }
}

适用场景:需要创建一组相关或相互依赖的对象


实战案例:用工厂模式封装复杂领域对象

场景:用户订单领域对象创建

假设我们有一个 Order 领域对象,它包含:

  • 必须关联已激活的 User 对象
  • 必须包含至少一个 OrderItem
  • 根据用户等级自动应用折扣
  • 生成唯一订单编号
不使用工厂的糟糕代码:
// 业务代码中直接new,导致职责混乱
$user = $userRepository->find($userId);
if (!$user->isActive()) {
    throw new Exception('User not active');
}
$order = new Order();
$order->setUser($user);
$order->setItems($items);
if ($user->getLevel() === 'vip') {
    $order->applyDiscount(0.9);
}
$orderNumber = $this->generateOrderNumber();
$order->setOrderNumber($orderNumber);
使用工厂的专业方案:
class OrderDomainFactory {
    public function __construct(
        private OrderNumberGenerator $numberGenerator,
        private DiscountService $discountService,
        private UserValidator $userValidator
    ) {}
    public function create(User $user, array $items): Order {
        $this->userValidator->ensureActive($user);
        $this->validateItems($items);
        $order = new Order();
        $order->setUser($user);
        $order->setItems($items);
        $order->setOrderNumber($this->numberGenerator->generate());
        // 应用领域规则
        $discount = $this->discountService->calculateDiscount($user);
        $order->applyDiscount($discount);
        return $order;
    }
}

效果:业务层只需调用 $factory->create($user, $items),领域对象的创建完全由工厂负责。


常见误区:何时不该用工厂模式?

反对者常提出:“工厂模式让代码更复杂,直接new对象有什么问题?”

我们需要明确边界:

  • 适合:对象创建需要复杂的依赖、校验、多态、聚合行为
  • 不适合:纯数据对象(如DTO)、无业务规则的简单值对象

SEO优化提醒:在搜索引擎中,关键词“PHP工厂模式 领域对象 性能”常被搜索,这里给出客观结论——工厂模式会增加一层间接调用,但现代PHP的OPcache和对象分配开销可忽略,真正影响性能的是大量对象创建本身,而非工厂模式。


问答环节

Q1:工厂模式与依赖注入容器(如Laravel的服务容器)是什么关系?

A:它们是互补关系。依赖注入容器负责管理对象的生命周期和依赖注入,而工厂模式负责封装复杂创建逻辑,例如在Laravel中,你可以通过App::bind(OrderFactory::class)将工厂注册到容器,然后通过容器获取工厂实例。

Q2:是否在每个领域对象上都要用工厂?

A:不,只有满足以下任一条件时才需考虑:

  • 构造逻辑包含业务规则(如上面的用户验证、折扣应用)
  • 构造函数需要依赖外部服务(如编号生成器)
  • 需要根据输入返回不同子类(如不同支付方式)

简单的值对象,如Address(只包含街道、城市字段),直接new即可。

Q3:在微服务架构中,工厂模式如何与领域事件配合?

A:这正是高级用法,工厂创建完领域对象后,可以直接在工厂内部触发生成领域事件,如OrderCreated

class OrderDomainFactory {
    public function create(...): Order {
        $order = new Order(...);  
        DomainEventDispatcher::dispatch(new OrderCreated($order));
        return $order;
    }
}

这样确保事件在对象创建完成且状态正确时立即触发。


结论与搜索引擎友好摘要

PHP工厂模式创建领域对象不是一种可选的装饰,而是处理复杂领域逻辑时的必须模式,它能显著提高代码的:

  • 可测试性:轻松mock工厂进行单元测试
  • 维护性:修改创建逻辑不影响业务层
  • 可读性:通过工厂名即可理解创建意图

(注:本文字数统计仅用于内容质量验证,实际展示时无需包含此句)

最后提示:当你在PHP项目中遇到“这个对象怎么在多个地方new,参数还都不一样?”时,请立即考虑引入工厂模式,它不仅是设计模式,更是领域驱动设计的最佳伴侣。

抱歉,评论功能暂时关闭!