创建型设计模式(GoF 5)
更新时间:2026-08-24。创建型模式关注对象如何创建,把"创建"与"使用"解耦,让系统不依赖具体类、不硬编码 new。
问题与主线
创建对象最朴素的方式是 new ConcreteClass()。这带来两个问题:
- 依赖具体类:写死了类名,换实现要改业务代码(违反开闭原则 OCP、依赖倒置 DIP)。
- 创建逻辑复杂:对象构建要 10 步初始化、参数可组合、全局唯一……业务代码被初始化代码淹没。
创建型模式的共同目标:把"怎么建"从"怎么用"中抽离,让客户端只面向抽象接口。
五种模式全景
| 模式 | 核心问题 | 关键思想 | 反例(何时别用) |
|---|---|---|---|
| 单例 Singleton | 全局唯一实例 | 私有构造 + 静态访问点 | 滥用变全局状态,难测试 |
| 工厂方法 Factory Method | 由子类决定实例化哪个类 | 延迟实例化到子类 | 一个类时过度设计 |
| 抽象工厂 Abstract Factory | 创建一族相关对象 | 一个接口造一族对象 | 产品族单一用不上 |
| 建造者 Builder | 复杂对象分步构建 | 分步 setter + 收尾 build | 参数少时冗余 |
| 原型 Prototype | 复制现成对象 | clone 代替 new | 深拷贝难维护时慎用 |
1. 单例 Singleton
问题:全局唯一实例(配置中心、日志器、连接池),且要线程安全。
朴素方案缺陷:static 全局变量初始化顺序不可控;手动判空 + 加锁容易写错双检锁。
模式方案(C++11 起最安全的写法):
// 线程安全的懒汉式单例(C++11 起,函数局部 static 初始化线程安全)
class Config {
public:
static Config& instance() {
static Config inst; // 首次调用时初始化,线程安全,之后零开销
return inst;
}
Config(const Config&) = delete;
Config& operator=(const Config&) = delete;
std::string get(const std::string& key) const { /* ... */ }
private:
Config() {} // 私有构造
};适用:真正"全局唯一"且无状态污染的场合。
边界:单例是全局可变状态,难做依赖注入、难单测。现代 C++ 更推荐"依赖注入 + 工厂管理生命周期"。
2. 工厂方法 Factory Method
问题:一个类根据配置/类型创建不同产品,但不想在业务代码里写 if/switch new。
朴素方案缺陷:业务类直接依赖所有具体产品类,新增产品要改业务类(违反 OCP)。
模式方案:
class Product {
public:
virtual ~Product() = default;
virtual void use() = 0;
};
class ConcreteA : public Product { public: void use() override { /* A */ } };
class ConcreteB : public Product { public: void use() override { /* B */ } };
class Factory {
public:
// 工厂方法:把"实例化哪个"交给具体子类覆盖
virtual std::unique_ptr<Product> create() = 0;
};
class FactoryA : public Factory {
public:
std::unique_ptr<Product> create() override { return std::make_unique<ConcreteA>(); }
};
// 客户端只面向 Product 抽象,不依赖具体类
void client(Factory& f) {
auto p = f.create();
p->use(); // 具体是 A 还是 B,客户端不关心
}适用:产品族稳定、具体类型在运行时才确定、要避免客户端依赖具体类。
3. 抽象工厂 Abstract Factory
问题:要创建一族相关对象(如"Windows 风格按钮 + 对话框" vs "Mac 风格按钮 + 对话框"),且这些对象要配套使用。
朴素方案缺陷:多个独立工厂容易配出"A 按钮 + B 对话框"的混搭,不配套。
模式方案:
// 抽象产品
class Button { public: virtual void render() = 0; };
class Dialog { public: virtual void show() = 0; };
// 抽象工厂:定义"一族"产品的创建契约
class UIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Dialog> createDialog() = 0;
};
// 具体工厂:保证整族配套
class WinFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() override { return std::make_unique<WinButton>(); }
std::unique_ptr<Dialog> createDialog() override { return std::make_unique<WinDialog>(); }
};
class MacFactory : public UIFactory {
// 同理,返回 Mac 一族
};
// 客户端拿到一个工厂,产出必然配套
void renderApp(UIFactory& f) {
auto b = f.createButton();
auto d = f.createDialog(); // 保证与 b 同族
}适用:有清晰的产品族(平台/主题/DB 驱动族),且要求整族一致性。
4. 建造者 Builder
问题:一个对象有大量可选参数、构建步骤复杂(如查询对象:过滤/排序/分页/字段选择),直接构造器参数爆炸。
朴素方案缺陷:构造器十几个参数、顺序易错、可读性差。
模式方案(链式调用):
class Query {
public:
Query& where(std::string cond) { where_ = cond; return *this; }
Query& orderBy(std::string col) { order_ = col; return *this; }
Query& limit(int n) { limit_ = n; return *this; }
std::string build() const { /* 拼 SQL */ return sql_; }
private:
std::string where_, order_, sql_;
int limit_ = -1;
};
// 用法:清晰的分步构建
auto q = Query{}.where("age > 18").orderBy("name").limit(50).build();适用:对象参数多、可选字段多、构建步骤需要清晰可读。
提示:C++ 中"命名构造器参数/流式 setter"常是 Builder 的轻量替代;配置复杂时再用独立 Builder 类。
5. 原型 Prototype
问题:创建对象代价高(数据库加载、复杂计算),想复制已有对象再微调。
朴素方案缺陷:手写拷贝要依赖具体类、且浅拷贝容易共享内部状态。
模式方案:让对象自己提供克隆(深拷贝)能力。
class Document {
public:
virtual ~Document() = default;
virtual std::unique_ptr<Document> clone() const = 0; // 原型方法
};
class Report : public Document {
public:
std::unique_ptr<Document> clone() const override {
return std::make_unique<Report>(*this); // 深拷贝(依赖正确拷贝构造)
}
};
// 用原型工厂创建新对象:不必知道具体类型
std::unique_ptr<Document> spawn(const Document& prototype) {
return prototype.clone();
}适用:对象构建昂贵、类型运行时多变、需要深拷贝隔离。
五种模式横向对比:到底好在哪里
创建型的五种模式都解耦了"创建"与"使用",但它们解耦的维度不同——这正是容易混淆的地方。用一个对比图看清各自的差异点:

对比表
| 维度 | Singleton | Factory Method | Abstract Factory | Builder | Prototype |
|---|---|---|---|---|---|
| 解耦什么 | 唯一性 | 单个产品的类型 | 一族产品的类型 | 复杂构建过程 | 昂贵的复制过程 |
| 替代的朴素写法 | 裸 static/全局 | if(type==A) new A | 多个独立 new | 长参数构造器 | 重新 new+初始化 |
| 新增一个产品 | — | 加一个工厂子类 | 加一族工厂 | — | 加一个 clone 实现 |
| 客户端依赖 | 无(静态访问) | Product 抽象 | UIFactory 抽象 | 具体 Builder | Document 抽象 |
| 何时最划算 | 全局唯一配置/日志 | 类型运行时才定 | 平台/主题/驱动族 | 对象参数多步骤杂 | 对象构建昂贵且多变 |
| 最易踩的坑 | 变成全局可变状态 | 产品单一却硬套 | 产品族单一却硬套 | 参数少却建 Builder | 浅拷贝共享内部状态 |
| 守护的原则 | — | OCP / DIP | OCP / DIP | — | — |
易混淆辨析
- 工厂方法 vs 抽象工厂:一个管单个产品(延迟到子类决定类型),一个管一族产品(保证整族配套)。抽象工厂常内部用工厂方法来创建族内每个成员,两者是"族 vs 成员"的关系,不是并列替代。
- 工厂 vs 建造者:工厂回答"创建谁"(哪个类型),建造者回答"怎么组装"(分几步、带什么参数)。同一类型对象的复杂分步构建用 Builder,类型可变用 Factory。
- 原型 vs 工厂:原型靠"复制现成实例"(clone),工厂靠"从零创建"(new)。当"已有模板实例"比"从头创建"更便宜时,原型胜出。
- 单例 vs 其他四种:单例隐藏了"个数",其余四种隐藏了"创建方式"。能用依赖注入+工厂管理生命周期时,尽量别用单例(可测试性差)。
- 一句话定位:Singleton 管"几个"、Factory Method 管"哪个"、Abstract Factory 管"一族"、Builder 管"怎么拼"、Prototype 管"怎么复制"——每个都只解耦一个维度的变化,选型时看你的变化点落在哪个维度。
决策速查

一句话总结:创建型模式把"new 具体类"从业务代码中剥离——单例管唯一、工厂管解耦、抽象工厂管配套、建造者管复杂度、原型管复制,核心是面向接口、对扩展开放。