抽象工厂模式(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}");
}
}
}七、全文总结
抽象工厂模式的核心是 创建一组相关的产品对象,保证产品兼容性。
核心要点:
- 用于创建产品族,保证同一工厂创建的产品兼容
- 新增产品族符合开闭原则,新增产品类型违反开闭原则
- 适用于跨平台、多主题、多数据库等场景
- 客户端只依赖抽象接口,与具体产品解耦
- 设计初期需要充分规划产品类型,避免后期频繁修改接口
