23 种设计模式 · C++ 版优缺点与应用场景
23 种设计模式 · C++ 版优缺点与应用场景
GoF《设计模式》23 种,按目的分三类:创建型(5)· 结构型(7)· 行为型(11)。本文给出每种模式的意图、C++ 关键代码、优缺点与典型应用场景。
总览
| 类别 | 模式 | 一句话 |
|---|---|---|
| 创建型 | 单例 Singleton | 全局唯一实例 |
| 创建型 | 工厂方法 Factory Method | 子类决定创建哪个类 |
| 创建型 | 抽象工厂 Abstract Factory | 创建一族相关对象 |
| 创建型 | 建造者 Builder | 分步构建复杂对象 |
| 创建型 | 原型 Prototype | 复制已有对象 |
| 结构型 | 适配器 Adapter | 接口转换 |
| 结构型 | 桥接 Bridge | 抽象与实现分离 |
| 结构型 | 组合 Composite | 树形整体-部分 |
| 结构型 | 装饰 Decorator | 动态附加职责 |
| 结构型 | 外观 Facade | 统一门面接口 |
| 结构型 | 享元 Flyweight | 共享细粒度对象 |
| 结构型 | 代理 Proxy | 替身控制访问 |
| 行为型 | 责任链 Chain of Responsibility | 请求沿链传递 |
| 行为型 | 命令 Command | 请求封装为对象 |
| 行为型 | 解释器 Interpreter | 文法解释 |
| 行为型 | 迭代器 Iterator | 顺序访问集合 |
| 行为型 | 中介者 Mediator | 对象间解耦通信 |
| 行为型 | 备忘录 Memento | 保存/恢复状态 |
| 行为型 | 观察者 Observer | 一对多通知 |
| 行为型 | 状态 State | 状态行为切换 |
| 行为型 | 策略 Strategy | 算法族可替换 |
| 行为型 | 模板方法 Template Method | 骨架固定、步骤可变 |
| 行为型 | 访问者 Visitor | 数据结构与操作分离 |
一、创建型模式
1. 单例 Singleton
意图:确保一个类只有一个实例,并提供全局访问点。
class Logger {
public:
static Logger& instance() {
static Logger inst; // C++11 线程安全
return inst;
}
private:
Logger() = default;
};| 优点 | 缺点 |
|---|---|
| 全局唯一、省资源 | 隐藏依赖,难测试 |
| 延迟初始化 | 多线程初始化需注意 |
场景:日志系统、配置中心、线程池、连接池。
2. 工厂方法 Factory Method
意图:定义一个创建对象的接口,让子类决定实例化哪个类。
struct Product { virtual ~Product() = default; };
struct ConcreteA : Product {};
class Factory {
public:
virtual std::unique_ptr<Product> create() = 0;
};
class FactoryA : public Factory {
std::unique_ptr<Product> create() override { return std::make_unique<ConcreteA>(); }
};| 优点 | 缺点 |
|---|---|
| 解耦创建与使用 | 每加一个产品需加子类 |
| 开闭原则友好 | 类数量膨胀 |
场景:框架中由子类决定对象类型(如游戏单位工厂)。
3. 抽象工厂 Abstract Factory
意图:提供创建一族相关对象的接口。
struct Button { virtual ~Button() = default; };
struct WinButton : Button {};
class UIFactory {
public:
virtual std::unique_ptr<Button> makeButton() = 0;
};
class WinFactory : public UIFactory {
std::unique_ptr<Button> makeButton() override { return std::make_unique<WinButton>(); }
};| 优点 | 缺点 |
|---|---|
| 保证产品族一致 | 新增产品族类型困难 |
| 隔离具体类 | 新增产品(方法)违反开闭 |
场景:跨平台 UI 组件、数据库访问层(不同方言)。
4. 建造者 Builder
意图:分步骤构建复杂对象,步骤可复用于不同表示。
class PizzaBuilder {
public:
PizzaBuilder& size(int s) { size_ = s; return *this; }
Pizza build() { return Pizza(size_); }
private:
int size_ = 0;
};
// 使用:PizzaBuilder{}.size(12).build();| 优点 | 缺点 |
|---|---|
| 构造参数可读、不可变 | 需要额外类 |
| 复用构建流程 | 简单对象反而过度设计 |
场景:复杂配置对象、文档/查询构建器。
5. 原型 Prototype
意图:通过克隆已有对象创建新对象。
struct Shape {
virtual ~Shape() = default;
virtual std::unique_ptr<Shape> clone() const = 0;
};
struct Circle : Shape {
std::unique_ptr<Shape> clone() const override {
return std::make_unique<Circle>(*this);
}
};| 优点 | 缺点 |
|---|---|
| 避免重复构造开销 | 深拷贝语义易出错 |
| 动态类型克隆 | 含资源成员需自定义拷贝 |
场景:编辑器复制图形、游戏单位克隆、配置模板。
二、结构型模式
6. 适配器 Adapter
意图:将不兼容接口转换为客户端期望的接口。
class Target { public: virtual void request() = 0; };
class Adaptee { public: void specific() {} };
class Adapter : public Target {
public:
void request() override { adaptee_.specific(); }
private:
Adaptee adaptee_;
};| 优点 | 缺点 |
|---|---|
| 复用已有类,不改源码 | 过度使用使架构混乱 |
| 符合开闭原则 | 有性能微开销 |
场景:接入第三方库、新旧接口桥接。
7. 桥接 Bridge
意图:将抽象部分与实现部分分离,各自独立变化。
class Renderer { public: virtual void draw() = 0; };
class Shape {
public:
explicit Shape(Renderer* r) : r_(r) {}
protected:
Renderer* r_;
};
class Circle : public Shape {
public:
using Shape::Shape;
void draw() { r_->draw(); }
};| 优点 | 缺点 |
|---|---|
| 抽象与实现可独立扩展 | 增加系统复杂度 |
| 符合开闭原则 | 需提前识别维度 |
场景:图形渲染后端(OpenGL/Vulkan)、跨平台设备驱动。
8. 组合 Composite
意图:将对象组织成树形结构,一致对待叶子与容器。
class Component {
public:
virtual void render() = 0;
virtual void add(Component*) {}
};
class Leaf : public Component { void render() override {} };
class Composite : public Component {
public:
void add(Component* c) override { children_.push_back(c); }
void render() override { for (auto* c : children_) c->render(); }
private:
std::vector<Component*> children_;
};| 优点 | 缺点 |
|---|---|
| 树形结构统一操作 | 叶子与容器接口耦合 |
| 易扩展新节点 | 违反接口隔离 |
场景:文件系统、UI 控件树、组织架构。
9. 装饰 Decorator
意图:动态给对象添加职责,替代继承。
class Stream { public: virtual void write(const std::string&) = 0; };
class FileStream : public Stream { void write(const std::string& s) override {} };
class EncryptedStream : public Stream {
public:
explicit EncryptedStream(Stream* s) : s_(s) {}
void write(const std::string& s) override { s_->write(encrypt(s)); }
private:
Stream* s_;
};| 优点 | 缺点 |
|---|---|
| 组合优于继承,灵活 | 层数多时难调试 |
| 运行时叠加能力 | 小对象过多 |
场景:IO 流加缓冲/加密/压缩、中间件管道。
10. 外观 Facade
意图:为复杂子系统提供统一简化的入口。
class VideoConverter {
public:
void convert(const std::string& file) {
codec_.load(file);
filter_.apply();
mux_.save();
}
private:
Codec codec_; Filter filter_; Muxer mux_;
};| 优点 | 缺点 |
|---|---|
| 客户端使用简单 | 可能变成“上帝对象“ |
| 隔离子系统变化 | 隐藏了灵活性 |
场景:编译模块入口、播放器/转换器统一 API。
11. 享元 Flyweight
意图:共享细粒度对象,减少内存占用。
struct CharStyle { std::string font; int size; };
class StyleCache {
public:
const CharStyle& get(const std::string& font, int size) {
auto key = font + std::to_string(size);
auto it = cache_.find(key);
if (it == cache_.end())
it = cache_.emplace(key, CharStyle{font, size}).first;
return it->second;
}
private:
std::unordered_map<std::string, CharStyle> cache_;
};| 优点 | 缺点 |
|---|---|
| 大幅减少内存 | 外部状态管理复杂 |
| 提高缓存命中 | 引入共享对象并发问题 |
场景:文本编辑器字符样式、游戏贴图/粒子模板、连接池。
12. 代理 Proxy
意图:提供替身控制对真实对象的访问(延迟/权限/缓存)。
class Image { public: virtual void show() = 0; };
class RealImage : public Image { void show() override {} };
class ImageProxy : public Image {
public:
void show() override {
if (!real_) real_ = std::make_unique<RealImage>();
real_->show(); // 延迟加载
}
private:
std::unique_ptr<RealImage> real_;
};| 优点 | 缺点 |
|---|---|
| 延迟加载、访问控制、日志 | 增加间接层 |
| 透明代理 | 过度代理增加延迟 |
场景:懒加载图片、智能指针、远程调用(RPC stub)。
三、行为型模式
13. 责任链 Chain of Responsibility
意图:多个处理者串成链,请求沿链传递直到被处理。
class Handler {
public:
Handler* next(Handler* n) { next_ = n; return this; }
virtual void handle(int level) {
if (canHandle(level)) process();
else if (next_) next_->handle(level);
}
private:
virtual bool canHandle(int) = 0;
virtual void process() = 0;
Handler* next_ = nullptr;
};| 优点 | 缺点 |
|---|---|
| 发送者与处理者解耦 | 请求可能无人处理 |
| 灵活增删处理者 | 调试链长时困难 |
场景:日志级别过滤、事件冒泡、中间件管道。
14. 命令 Command
意图:把请求封装成对象,支持参数化、队列、撤销。
class Command {
public:
virtual void execute() = 0;
virtual void undo() = 0;
};
class CopyCommand : public Command {
void execute() override {}
void undo() override {}
};| 优点 | 缺点 |
|---|---|
| 支持撤销/重做、任务队列 | 每个操作一个类 |
| 请求可参数化延迟执行 | 类数量增加 |
场景:编辑器撤销栈、事务、宏录制、线程任务。
15. 解释器 Interpreter
意图:为文法定义解释器,求值表达式。
struct Expr { virtual int eval() const = 0; };
struct Num : Expr { int v; int eval() const override { return v; } };
struct Add : Expr {
const Expr *l, *r;
int eval() const override { return l->eval() + r->eval(); }
};| 优点 | 缺点 |
|---|---|
| 文法易扩展 | 复杂文法维护困难 |
| 领域语言表达力强 | 性能较低 |
场景:SQL/正则/数学表达式引擎、脚本解析。
16. 迭代器 Iterator
意图:不暴露内部结构,顺序访问集合元素。
class Iter {
public:
virtual bool hasNext() = 0;
virtual int next() = 0;
};
class RangeIter : public Iter {
int cur_, end_;
public:
RangeIter(int a, int b) : cur_(a), end_(b) {}
bool hasNext() override { return cur_ <= end_; }
int next() override { return cur_++; }
};| 优点 | 缺点 |
|---|---|
| 集合遍历与结构解耦 | 简单集合反而多余 |
| 支持多种遍历策略 | 遍历中修改集合需约定 |
场景:STL 迭代器、分页遍历、树遍历。
17. 中介者 Mediator
意图:用一个中介对象协调多个对象的交互。
class Mediator {
public:
void notify(const std::string& from, const std::string& msg) {
for (auto* c : colleagues_) if (c->name() != from) c->receive(msg);
}
void add(Colleague* c) { colleagues_.push_back(c); }
private:
std::vector<Colleague*> colleagues_;
};| 优点 | 缺点 |
|---|---|
| 减少对象间网状依赖 | 中介者可能过于庞大 |
| 交互集中管理 | 单点故障 |
场景:聊天室、UI 控件协调、航班调度。
18. 备忘录 Memento
意图:在不破坏封装的前提下保存并恢复对象状态。
struct Memento { int state; };
class Originator {
public:
Memento save() const { return {state_}; }
void restore(const Memento& m) { state_ = m.state; }
private:
int state_ = 0;
};| 优点 | 缺点 |
|---|---|
| 状态快照与恢复简单 | 大对象快照开销高 |
| 不破坏封装 | 历史过多占内存 |
场景:编辑器撤销、游戏存档、事务回滚。
19. 观察者 Observer
意图:定义一对多依赖,状态变化自动通知。
class Observer { public: virtual void update() = 0; };
class Subject {
public:
void attach(Observer* o) { obs_.push_back(o); }
void notify() { for (auto* o : obs_) o->update(); }
private:
std::vector<Observer*> obs_;
};| 优点 | 缺点 |
|---|---|
| 主题与观察者解耦 | 通知顺序不确定 |
| 广播机制天然 | 级联更新难排查 |
场景:事件总线、MVC 视图刷新、发布订阅。
20. 状态 State
意图:对象状态改变时行为随之改变,替代大量 if-else。
class State { public: virtual void handle() = 0; };
class IdleState : public State { void handle() override {} };
class RunningState : public State { void handle() override {} };| 优点 | 缺点 |
|---|---|
| 状态行为内聚、可扩展 | 状态类数量多 |
| 消除巨型条件分支 | 状态转换逻辑分散 |
场景:播放器状态、订单状态机、网络连接状态。
21. 策略 Strategy
意图:定义算法族并封装,运行时切换。
class SortStrategy { public: virtual void sort(std::vector<int>&) = 0; };
class QuickSort : public SortStrategy { void sort(std::vector<int>& v) override {} };
class MergeSort : public SortStrategy { void sort(std::vector<int>& v) override {} };
class Sorter {
public:
void setStrategy(SortStrategy* s) { s_ = s; }
void run(std::vector<int>& v) { s_->sort(v); }
private:
SortStrategy* s_;
};| 优点 | 缺点 |
|---|---|
| 算法可替换、开闭原则 | 客户端需了解策略差异 |
| 消除条件分支 | 策略对象数量增加 |
场景:排序算法选择、压缩/加密算法、价格计算。
22. 模板方法 Template Method
意图:定义算法骨架,子类实现可变步骤。
class DataParser {
public:
void parse() { // 骨架固定
open(); read(); process(); close();
}
protected:
virtual void process() = 0; // 可变步骤
private:
void open() {} void read() {} void close() {}
};| 优点 | 缺点 |
|---|---|
| 复用公共流程 | 骨架修改影响全部子类 |
| 约束子类实现范围 | 违反 LSP 风险(子类改变骨架) |
场景:框架生命周期(初始化-运行-清理)、解析器流程。
23. 访问者 Visitor
意图:在不修改类的前提下,为对象结构增加新操作。
class Visitor;
class Node {
public:
virtual void accept(Visitor& v) = 0;
};
class TextNode : public Node {
public:
void accept(Visitor& v) override;
};
class Visitor {
public:
virtual void visit(TextNode&) = 0;
};| 优点 | 缺点 |
|---|---|
| 数据结构与操作分离,易加操作 | 新增节点类型需改所有访问者 |
| 操作内聚 | 破坏封装(需暴露内部) |
场景:AST 编译/解释、对象结构统计与序列化。
选型速查
| 问题 | 推荐模式 |
|---|---|
| 全局唯一 | 单例 |
| 创建复杂对象 | 建造者 / 工厂 / 抽象工厂 |
| 接口不兼容 | 适配器 |
| 动态加职责 | 装饰 |
| 树形结构 | 组合 |
| 延迟/控制访问 | 代理 |
| 一对多通知 | 观察者 |
| 运行时换算法 | 策略 |
| 状态行为切换 | 状态 |
| 撤销重做 | 命令 + 备忘录 |
| 为结构加新操作 | 访问者 |
原则提醒:优先组合而非继承、面向接口而非实现、对扩展开放对修改关闭。模式是工具,不要为模式而模式。