PHP项目工厂与建造者

wen PHP项目 2

本文目录导读:

PHP项目工厂与建造者

  1. 📑 目录导读
  2. 设计模式的前世今生
  3. 工厂模式:三步创建对象的“流水线”
  4. 建造者模式:复杂对象的“组装说明书”
  5. 工厂 vs 建造者:5个场景教你选
  6. 实战案例:用两者重构支付系统
  7. 高频问答精粹

PHP项目工厂与建造者:从菜鸟到架构师的模式实战指南


📑 目录导读

  1. 设计模式的前世今生 – 为何工厂与建造者在PHP中如此重要
  2. 工厂模式:三步创建对象的“流水线” – 简单工厂、工厂方法、抽象工厂深度对比
  3. 建造者模式:复杂对象的“组装说明书” – 当构造函数不够用时的优雅方案
  4. 工厂 vs 建造者:何时用谁? – 5个真实场景决策树
  5. 实战案例:用两者重构一个支付系统 – 带完整代码与架构图
  6. 高频问答精粹 – 解决开发者最常见的8个困惑

设计模式的前世今生

在PHP开发中,我们每天都要创建对象:new User()new Order()……但当对象变复杂、创建逻辑重复、或需要切换不同实现时,直接new就成了技术债务。工厂模式建造者模式正是解决这类问题的两大神器。

根据2024年PHP社区调查,超过72%的中大型项目至少使用了其中一种模式,它们不是炫技,而是让代码可测试、可扩展、可维护的基石。


工厂模式:三步创建对象的“流水线”

1 简单工厂(Simple Factory)

class ProductFactory {
    public static function create(string $type): Product {
        return match($type) {
            'book' => new BookProduct(),
            'video' => new VideoProduct(),
            default => throw new InvalidArgumentException()
        };
    }
}

适用场景:对象类型少、固定不变时。

2 工厂方法(Factory Method)

abstract class ProductCreator {
    abstract protected function createProduct(): Product;
    public function doSomething(): void {
        $product = $this->createProduct();
        $product->use();
    }
}
class BookCreator extends ProductCreator {
    protected function createProduct(): Product {
        return new BookProduct();
    }
}

核心价值:把“创建”延迟到子类,遵循开闭原则。

3 抽象工厂(Abstract Factory)

interface UIFactory {
    public function createButton(): Button;
    public function createCheckbox(): Checkbox;
}
class WinFactory implements UIFactory {
    public function createButton(): Button { return new WinButton(); }
    public function createCheckbox(): Checkbox { return new WinCheckbox(); }
}

杀手锏:创建一系列相关对象(如UI主题),保证产品族兼容性。

💡 面试官最爱问的坑

:抽象工厂和工厂方法有什么区别?
:工厂方法针对一个产品等级结构(如创建不同型号的汽车);抽象工厂针对一个产品族(如创建宝马奔驰各自的方向盘+轮胎+引擎)。


建造者模式:复杂对象的“组装说明书”

当对象构造函数需要10+参数时,代码就像意大利面:

// 反模式
$car = new Car('V8', 'red', 'leather', true, false, 'panoramic', 19, 'sports');

建造者模式的治疗方案:

$car = (new CarBuilder())
    ->setEngine('V8')
    ->setColor('red')
    ->setSeats('leather')
    ->addSunroof('panoramic')
    ->build(); // 返回完整Car对象

核心四要素:

组件 职责 类比
Builder接口 定义组装步骤 施工图纸
ConcreteBuilder 具体实现+追踪产品 施工队
Director 安排组装顺序 项目经理
Product 最终成品 大楼

高级技巧:流式接口 + 不可变对象

class QueryBuilder {
    private array $conditions = [];
    public function where(string $field, string $op, $value): self {
        $clone = clone $this; // 不可变模式
        $clone->conditions[] = [$field, $op, $value];
        return $clone;
    }
}

工厂 vs 建造者:5个场景教你选

场景 推荐模式 理由
创建同类不同子类对象 工厂方法 根据参数决定子类
创建多个步骤、可定制的复杂对象 建造者 步骤可跳过、有默认值
需要确保产品族兼容性 抽象工厂 捆绑创建相关对象
对象创建只需一行代码 简单工厂 轻量、无需扩展
创建过程有50+种参数组合 建造者 避免望远镜构造函数

决策树速查

graph TD
    A[要创建对象] --> B{对象复杂?}
    B -->|是| C{需要多种配置?}
    C -->|是| D[建造者]
    C -->|否| E[抽象工厂]
    B -->|否| F{有多种子类?}
    F -->|是| G[工厂方法]
    F -->|否| H[简单工厂]

实战案例:用两者重构支付系统

原始代码(反面教材)

class PaymentService {
    public function process(string $type, array $config) {
        if ($type === 'alipay') {
            $gateway = new AlipayGateway($config['app_id'], $config['private_key']);
        } elseif ($type === 'wechat') {
            $gateway = new WechatGateway($config['app_id'], $config['mch_id']);
        }
        // 20+个条件分支
    }
}

重构后:工厂+建造者双剑合璧

Step 1:工厂负责选择支付类型

class PaymentGatewayFactory {
    public function create(string $type): PaymentGatewayBuilder {
        return match($type) {
            'alipay' => new AlipayGatewayBuilder(),
            'wechat' => new WechatGatewayBuilder(),
        };
    }
}

Step 2:建造者负责组装具体参数

class AlipayGatewayBuilder extends PaymentGatewayBuilder {
    private array $params = [];
    public function setAppId(string $appId): self {
        $this->params['app_id'] = $appId;
        return $this;
    }
    public function build(): AlipayGateway {
        return new AlipayGateway(
            $this->params['app_id'],
            $this->params['private_key'] ?? 'default_key'
        );
    }
}

Step 3:客户端使用

$service = new PaymentService();
$gateway = (new PaymentGatewayFactory())
    ->create('alipay')
    ->setAppId('2024xxxx')
    ->setEnvironment('sandbox') // 可忽略的步骤
    ->build();
$service->process($gateway);

效果:新增银联支付时,只需新增UnionPayGatewayBuilder零改动已有代码。


高频问答精粹

Q1:建造者模式是不是就是setter方法?
A:不完全是,建造者有build()方法来创建最终不可变对象,而setter允许随时修改。关键区别:建造者控制创建过程的完整性(如必填项校验)。

Q2:工厂模式会不会导致类爆炸?
A:会!所以建议配合配置驱动注册树模式使用,例如用YAML/JSON定义工厂映射。

Q3:抽象工厂中的“产品族”如何理解?
A:看电商系统:跨境支付需要同时创建美国海关表单、Visa支付、当地物流单,抽象工厂就能保证这三个组件来自同一国家。

Q4:建造者模式和策略模式能一起用吗?
A:策略决定“如何建造”,建造者决定“建造什么”,建造者搭建订单对象,策略决定优惠计算方式。

Q5:现代PHP框架中哪些使用了这些模式?
A:Laravel的Queue(工厂方法创建不同驱动)、Symfony的FormBuilder(建造者)、Doctrine的QueryBuilder(建造者)。

Q6:测试时如何Mock工厂和建造者?
A:对工厂Mock其create()方法,对建造者Mock其build()方法,推荐使用PHPUnit的createMock() + Prophecy。

Q7:性能上有损失吗?
A:微乎其微(<0.1ms),但若在循环中频繁创建建造者,可考虑对象池复用。

Q8:能用枚举(Enum)替代简单工厂吗?
A:PHP 8.1+确实可以,但枚举不适合需要动态注册的场景,工厂模式更灵活。


工厂模式是对象的“生产车间”,建造者模式是“组装流水线”,两者配合,能让你的PHP项目从“硬编码”进化到“可配置”,记住一句口诀:“单对象找工厂,多组装找建造,要族谱就抽象”

现在就去重构你项目中最脏的那个new语句吧!如果你觉得文章对你有帮助,欢迎分享给更多开发者。

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