抽象工厂模式(Abstract Factory):一族配套产品的"换装开关"
更新时间:2026-08-25。本文按"先看不用模式的困境 → 再用模式重构 → 类图对比分析"的结构,论述抽象工厂模式。可运行代码:demos/design-patterns/abstract-factory。
一、问题场景:产品不是单个出现的,而是一"族"
跨平台 GUI(Win 按钮 + Win 文本框 + Win 弹窗)、数据库方言(MySQL SQL 生成器 + MySQL 连接器 + MySQL 事务器)、主题皮肤(暗色按钮 + 暗色卡片 + 暗色输入框)。族内产品必须配套使用,且整套替换(换平台 = 换整族)。
二、不使用模式:客户端散落 new,换族如拆家
cpp
// 客户端要构建一整套 UI(Win 风格)
auto* btn = new WinButton();
auto* text = new WinTextBox();
auto* dlg = new WinDialog();
// 换成 Mac 风格:这里每一行都要改| 缺陷 | 说明 |
|---|---|
| 换族=全局改动 | 客户端里散落着整族 new,换平台要逐行改 |
| 配套约束靠自觉 | 没人保证"Win 按钮 + Mac 弹窗"不混搭 |
| 违反 DIP | 客户端依赖所有具体类 |
| 创建逻辑重复 | 每个界面模块都重复写"new 这一族" |
不使用模式的类图

三、使用模式:抽象工厂 = 整族的创建接口
抽象工厂定义一个"创建整族产品"的接口集合,每个具体工厂实现一族;客户端只依赖抽象工厂,换族 = 换一个工厂对象,配套关系由工厂内部保证。
cpp
// gui_factory.h
class Button { public: virtual void draw() = 0; virtual ~Button() = default; };
class TextBox { public: virtual void draw() = 0; virtual ~TextBox() = default; };
class Dialog { public: virtual void draw() = 0; virtual ~Dialog() = default; };
class GUIFactory { // 抽象工厂:创建"一族"
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<TextBox> createTextBox() = 0;
virtual std::unique_ptr<Dialog> createDialog() = 0;
virtual ~GUIFactory() = default;
};
class WinFactory : public GUIFactory {
public:
auto createButton() -> std::unique_ptr<Button> { return std::make_unique<WinButton>(); }
auto createTextBox() -> std::unique_ptr<TextBox> { return std::make_unique<WinTextBox>(); }
auto createDialog() -> std::unique_ptr<Dialog> { return std::make_unique<WinDialog>(); }
};
class MacFactory : public GUIFactory { /* Mac 族 */ };
// 客户端:只依赖抽象工厂
void render(GUIFactory& f) {
auto b = f.createButton(); auto t = f.createTextBox(); auto d = f.createDialog();
b->draw(); t->draw(); d->draw(); // 换 f 即换整族
}使用模式的类图

四、类图对比与分析
| 维度 | 不使用(散落 new) | 使用抽象工厂 |
|---|---|---|
| 换整族 | 客户端逐行改 | 换一个工厂对象 |
| 配套约束 | 靠约定,可能混搭 | 工厂内部保证族内配套 |
| 客户端依赖 | 所有具体产品类 | 只有抽象工厂 + 抽象产品 |
| 新增一族 | 全量回归 | 新增一个 Factory + 族产品类 |
| OCP 符合度 | 违反 | 符合(扩展维度=新增工厂) |
分析结论:抽象工厂把"产品族"作为一个可整体替换的单元封装起来,是 OCP 在"族"这个粒度上的体现。代价是结构重(族产品每加一个成员,所有具体工厂都要加一个创建方法)——产品族扩展代价高,换族收益高,适合族稳定、族间整体切换的场景。
五、适用边界
| 适用 | 不适用 |
|---|---|
| 产品天然成族且族内必须配套 | 只有单个产品(用工厂方法即可) |
| 换族是常态(跨平台/多主题) | 族基本不变(抽象工厂是过度设计) |
| 需要保证配套一致性 | 产品成员频繁增加(每加一员所有工厂都要改) |
与工厂方法的区别:工厂方法 = 一个工厂一个产品(关注"产品类型");抽象工厂 = 一个工厂一族产品(关注"产品族")。抽象工厂常内部用工厂方法实现每个成员。
一句话总结
抽象工厂把一族配套产品的创建收敛到一个工厂接口,客户端换族只换工厂对象、配套约束由工厂保证——在"族"的粒度上实现 OCP;但族成员每增一员所有工厂都要同步扩展,产品族必须相对稳定才划算。