本文目录导读:

PHP项目工厂与建造者:从菜鸟到架构师的模式实战指南
📑 目录导读
- 设计模式的前世今生 – 为何工厂与建造者在PHP中如此重要
- 工厂模式:三步创建对象的“流水线” – 简单工厂、工厂方法、抽象工厂深度对比
- 建造者模式:复杂对象的“组装说明书” – 当构造函数不够用时的优雅方案
- 工厂 vs 建造者:何时用谁? – 5个真实场景决策树
- 实战案例:用两者重构一个支付系统 – 带完整代码与架构图
- 高频问答精粹 – 解决开发者最常见的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语句吧!如果你觉得文章对你有帮助,欢迎分享给更多开发者。