Skip to content

抽象工厂模式(Abstract Factory)

前言

抽象工厂模式是工厂方法模式的升级版,用于创建一组相关或相互依赖的对象。它提供一个创建一系列相关对象的接口,而无需指定具体类。本文将详细讲解抽象工厂模式的核心原理、实现方式及与工厂方法的区别。


一、核心概念

1.1 定义

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

1.2 核心角色

角色说明
抽象工厂(AbstractFactory)声明创建一系列产品的方法
具体工厂(ConcreteFactory)实现创建具体产品的方法
抽象产品(AbstractProduct)定义产品接口(多组)
具体产品(ConcreteProduct)实现产品接口的具体类

1.3 与工厂方法的区别

对比项工厂方法抽象工厂
创建产品数单个产品一组产品
关注点单一产品创建产品族创建
扩展方向新增产品子类新增产品族
接口方法一个工厂方法多个工厂方法

二、代码实现

2.1 基础实现

以跨平台 UI 组件为例,不同平台需要创建风格统一的按钮和复选框。

php
<?php
// 抽象产品:按钮
interface Button
{
    public function render(): string;
    public function onClick(): void;
}

// 抽象产品:复选框
interface Checkbox
{
    public function render(): string;
    public function toggle(): void;
}

// Windows 风格产品
class WindowsButton implements Button
{
    public function render(): string { return 'Windows Button'; }

    public function onClick(): void
    {
        echo 'Windows Button clicked' . PHP_EOL;
    }
}

class WindowsCheckbox implements Checkbox
{
    public function render(): string { return 'Windows Checkbox'; }

    public function toggle(): void
    {
        echo 'Windows Checkbox toggled' . PHP_EOL;
    }
}

// Mac 风格产品
class MacButton implements Button
{
    public function render(): string { return 'Mac Button'; }

    public function onClick(): void
    {
        echo 'Mac Button clicked' . PHP_EOL;
    }
}

class MacCheckbox implements Checkbox
{
    public function render(): string { return 'Mac Checkbox'; }

    public function toggle(): void
    {
        echo 'Mac Checkbox toggled' . PHP_EOL;
    }
}

// 抽象工厂
interface GUIFactory
{
    public function createButton(): Button;
    public function createCheckbox(): Checkbox;
}

// Windows 工厂
class WindowsFactory implements GUIFactory
{
    public function createButton(): Button
    {
        return new WindowsButton();
    }

    public function createCheckbox(): Checkbox
    {
        return new WindowsCheckbox();
    }
}

// Mac 工厂
class MacFactory implements GUIFactory
{
    public function createButton(): Button
    {
        return new MacButton();
    }

    public function createCheckbox(): Checkbox
    {
        return new MacCheckbox();
    }
}

// 客户端代码
class Application
{
    private Button $button;
    private Checkbox $checkbox;

    public function __construct(GUIFactory $factory)
    {
        $this->button = $factory->createButton();
        $this->checkbox = $factory->createCheckbox();
    }

    public function render(): void
    {
        echo $this->button->render() . PHP_EOL;
        echo $this->checkbox->render() . PHP_EOL;
    }
}

// 使用:根据平台选择工厂
$platform = PHP_OS_FAMILY === 'Darwin' ? 'mac' : 'windows';
$factory = $platform === 'mac' ? new MacFactory() : new WindowsFactory();

$app = new Application($factory);
$app->render();

2.2 实际案例:数据库访问层

php
<?php
// 抽象产品:连接和命令
interface DBConnection
{
    public function connect(): string;
}

interface DBCommand
{
    public function execute(string $query): string;
}

// MySQL 产品族
class MySQLConnection implements DBConnection
{
    public function connect(): string
    {
        return 'MySQL connected';
    }
}

class MySQLCommand implements DBCommand
{
    public function execute(string $query): string
    {
        return "MySQL executing: {$query}";
    }
}

// PostgreSQL 产品族
class PostgreSQLConnection implements DBConnection
{
    public function connect(): string
    {
        return 'PostgreSQL connected';
    }
}

class PostgreSQLCommand implements DBCommand
{
    public function execute(string $query): string
    {
        return "PostgreSQL executing: {$query}";
    }
}

// 抽象工厂
interface DBFactory
{
    public function createConnection(): DBConnection;
    public function createCommand(): DBCommand;
}

// MySQL 工厂
class MySQLFactory implements DBFactory
{
    public function createConnection(): DBConnection
    {
        return new MySQLConnection();
    }

    public function createCommand(): DBCommand
    {
        return new MySQLCommand();
    }
}

// PostgreSQL 工厂
class PostgreSQLFactory implements DBFactory
{
    public function createConnection(): DBConnection
    {
        return new PostgreSQLConnection();
    }

    public function createCommand(): DBCommand
    {
        return new PostgreSQLCommand();
    }
}

// 使用
function createDBService(DBFactory $factory): void
{
    $connection = $factory->createConnection();
    $command = $factory->createCommand();
    echo $connection->connect() . PHP_EOL;
    echo $command->execute('SELECT * FROM users') . PHP_EOL;
}

createDBService(new MySQLFactory());
// MySQL connected
// MySQL executing: SELECT * FROM users

三、适用场景

场景说明
跨平台应用不同平台需要统一风格组件
数据库访问层不同数据库使用对应驱动
主题切换不同主题创建对应 UI 组件
产品族创建需要创建一组相关对象
配置化系统根据配置选择不同实现族

四、优缺点分析

优点缺点
保证产品兼容性新增产品类型困难
解耦客户端与产品代码复杂度高
符合开闭原则(新增产品族)需要维护多个工厂类
易于切换产品族违反开闭原则(新增产品类型)

五、常见踩坑与问题排查

5.1 新增产品类型违反开闭原则

抽象工厂在新增产品族时符合开闭原则,但新增产品类型(如在 GUIFactory 中新增 createInput() 方法)需要修改所有工厂类。

解决方案:在设计初期充分考虑产品类型,或使用参数化工厂。

5.2 工厂选择逻辑分散

客户端直接选择具体工厂会导致条件判断分散。

解决方案:使用配置或工厂注册中心统一管理:

php
<?php
class FactoryRegistry
{
    private array $factories = [];

    public function register(string $name, GUIFactory $factory): void
    {
        $this->factories[$name] = $factory;
    }

    public function getFactory(string $name): GUIFactory
    {
        if (!isset($this->factories[$name])) {
            throw new InvalidArgumentException("Unknown factory: {$name}");
        }
        return $this->factories[$name];
    }
}

$registry = new FactoryRegistry();
$registry->register('windows', new WindowsFactory());
$registry->register('mac', new MacFactory());

$factory = $registry->getFactory($config['theme']);

六、优化方案与进阶

6.1 与工厂方法结合

php
<?php
// 抽象工厂中每个方法都是一个工厂方法
interface DBFactory
{
    public function createConnection(): DBConnection;
    public function createCommand(): DBCommand;
    public function createTransaction(): DBTransaction;
}

6.2 使用简单工厂优化

当产品族少且固定时,可用简单工厂替代抽象工厂:

php
<?php
class SimpleGUIFactory
{
    public static function create(string $theme): GUIFactory
    {
        switch ($theme) {
            case 'windows':
                return new WindowsFactory();
            case 'mac':
                return new MacFactory();
            default:
                throw new InvalidArgumentException("Unknown theme: {$theme}");
        }
    }
}

七、全文总结

抽象工厂模式的核心是 创建一组相关的产品对象,保证产品兼容性

核心要点

  1. 用于创建产品族,保证同一工厂创建的产品兼容
  2. 新增产品族符合开闭原则,新增产品类型违反开闭原则
  3. 适用于跨平台、多主题、多数据库等场景
  4. 客户端只依赖抽象接口,与具体产品解耦
  5. 设计初期需要充分规划产品类型,避免后期频繁修改接口