本文目录导读:

在PHP设计模式中,静态工厂和抽象工厂虽然都涉及对象创建,但它们的意图、结构和应用场景截然不同,很多初学者容易混淆,下面从核心区别到代码实战进行深度拆解。
核心概念对比(一句话总结)
- 静态工厂:“快捷方式”,用一个静态方法来封装
new关键字,侧重于代码整洁和命名语义化,它创建的通常是同一个类或同一族类(通过参数判断)。 - 抽象工厂:“工厂的工厂”,定义了一个接口,让子类决定实例化哪一个具体工厂,进而创建一组相关或依赖的对象,侧重于产品族的一致性。
深度解析与代码示例
1 静态工厂(Static Factory / 简单工厂)
特点:
- 使用
public static function创建对象。 - 返回值类型通常是父类或接口,但内部通过
if/switch判断返回具体子类。 - 没有抽象接口或继承体系(通常只有一个类)。
适用场景:替代冗长的构造函数,或者当创建逻辑不复杂、产品种类较少时使用。
代码示例(电商订单类型工厂):
<?php
// 1. 定义产品接口
interface OrderInterface {
public function calculate();
}
// 2. 具体产品
class NormalOrder implements OrderInterface {
public function calculate() {
return "普通订单计算:原价";
}
}
class DiscountOrder implements OrderInterface {
public function calculate() {
return "折扣订单计算:打8折";
}
}
// 3. 静态工厂(唯一入口)
class OrderFactory {
public static function createOrder($type): OrderInterface {
switch ($type) {
case 'normal':
return new NormalOrder();
case 'discount':
return new DiscountOrder();
default:
throw new \InvalidArgumentException("未知订单类型:{$type}");
}
}
}
// 客户端使用(非常简洁)
$order = OrderFactory::createOrder('discount');
echo $order->calculate(); // 输出:折扣订单计算:打8折
关键点:如果新增订单类型(如 GiftOrder),你需要修改 OrderFactory 里的 switch,这违反了开闭原则,但胜在简单直接。
2 抽象工厂(Abstract Factory)
特点:
- 有一个抽象工厂类(接口),里面声明了多个工厂方法(每个方法负责创建一个产品)。
- 有多个具体工厂类,分别实现抽象工厂的方法。
- 核心目标:确保同一个工厂生成的产品之间是配套的(产品族)。
适用场景:系统需要跨平台(如不同的数据库连接、不同的UI控件),且必须保证所有对象属于同一风格/同一平台。
代码示例(跨平台UI组件):
<?php
// ---- 1. 产品接口:按钮和弹窗 ----
interface Button {
public function render();
}
interface Dialog {
public function show();
}
// ---- 2. 产品族1:Windows风格 ----
class WindowsButton implements Button {
public function render() { return "渲染 [Windows] 按钮"; }
}
class WindowsDialog implements Dialog {
public function show() { return "显示 [Windows] 弹窗"; }
}
// ---- 3. 产品族2:Mac风格 ----
class MacButton implements Button {
public function render() { return "渲染 [Mac] 按钮"; }
}
class MacDialog implements Dialog {
public function show() { return "显示 [Mac] 弹窗"; }
}
// ---- 4. 抽象工厂接口(这是关键区别)----
interface UIFactory {
public function createButton(): Button;
public function createDialog(): Dialog;
}
// ---- 5. 具体工厂(每个工厂只产生一种风格的一套产品)----
class WindowsFactory implements UIFactory {
public function createButton(): Button {
return new WindowsButton();
}
public function createDialog(): Dialog {
return new WindowsDialog();
}
}
class MacFactory implements UIFactory {
public function createButton(): Button {
return new MacButton();
}
public function createDialog(): Dialog {
return new MacDialog();
}
}
// ---- 6. 客户端使用 ----
class App {
private $factory;
public function __construct(UIFactory $factory) {
$this->factory = $factory;
}
public function renderUI() {
$btn = $this->factory->createButton();
$dlg = $this->factory->createDialog();
return $btn->render() . " + " . $dlg->show();
}
}
// 选择平台(通常由配置文件/环境决定)
$app = new App(new WindowsFactory());
echo $app->renderUI(); // 输出:渲染 [Windows] 按钮 + 显示 [Windows] 弹窗
// 如果换成 Mac:
$app = new App(new MacFactory());
echo $app->renderUI(); // 输出:渲染 [Mac] 按钮 + 显示 [Mac] 弹窗
关键点:
- 新增一个 产品(如
Checkbox),需要修改UIFactory接口和 所有 具体工厂(WindowsFactory、MacFactory),比较麻烦。 - 新增一个 产品族(如
LinuxFactory),只需要新增一个工厂类即可,对现有代码零修改。
核心区别对照表
| 维度 | 静态工厂 (Static Factory) | 抽象工厂 (Abstract Factory) |
|---|---|---|
| 核心目的 | 简化对象创建(封装 new) |
创建 一组相关对象,并保证其风格一致 |
| 结构复杂度 | 简单,通常只有一个类 | 复杂,涉及多个工厂类和抽象产品接口 |
| 方法类型 | 使用 static 方法 |
普通实例方法(必须实例化具体工厂) |
| 对象产生方式 | 通过参数判断返回不同对象 | 通过调用不同工厂的方法返回对象 |
| 扩展性 | 差(新增产品需改 switch) |
良(新增产品族很方便,新增产品较繁琐) |
| 典型别名 | 有时被称为“简单工厂” | 属于 GoF 23 种设计模式之一 |
什么时候用哪个?(来自实战的建议)
- 当你的类有多个构造函数参数,或者名字不好记时:用 静态工厂(
User::createAdmin()、DB::connect('mysql')),这是最常见的日常写法。 - 当你的系统需要支持多套主题/多数据库类型,且需要保证配套时:用 抽象工厂,Laravel 的
DatabaseManager就是典型的抽象工厂思路,它让你用'mysql'或'pgsql'创建一整组Connection、Grammar、Processor组件。
最后提醒:在 PHP 中,静态工厂非常实用且常见(各大框架的 Facade 都是静态代理),而抽象工厂虽然更“高大上”,但它带来的代码量较大。如果只是创建单个独立对象,绝不要用抽象工厂,用静态工厂就够了。