PHP静态工厂与抽象工厂

wen PHP项目 1

本文目录导读:

PHP静态工厂与抽象工厂

  1. 核心概念对比(一句话总结)
  2. 深度解析与代码示例
  3. 核心区别对照表
  4. 什么时候用哪个?(来自实战的建议)

在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 种设计模式之一

什么时候用哪个?(来自实战的建议)

  1. 当你的类有多个构造函数参数,或者名字不好记时:用 静态工厂User::createAdmin()DB::connect('mysql')),这是最常见的日常写法。
  2. 当你的系统需要支持多套主题/多数据库类型,且需要保证配套时:用 抽象工厂,Laravel 的 DatabaseManager 就是典型的抽象工厂思路,它让你用 'mysql''pgsql' 创建一整组 ConnectionGrammarProcessor 组件。

最后提醒:在 PHP 中,静态工厂非常实用且常见(各大框架的 Facade 都是静态代理),而抽象工厂虽然更“高大上”,但它带来的代码量较大。如果只是创建单个独立对象,绝不要用抽象工厂,用静态工厂就够了。

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