第一部分:创建型模式 (Creational Patterns)
核心目的:将对象的"使用"和"创建"分离,解决"对象怎么产生"的问题。
1. 单例模式 (Singleton)
-
解决痛点 :系统中某些资源(如数据库连接池、日志句柄)非常重,或者逻辑上只允许存在一份,如果到处
new,会导致资源冲突或数据不一致。 -
特点:保证全局唯一,提供全局访问点。
-
使用场景:
-
日志系统:多线程写入同一个日志文件。
-
配置管理器 :读取
config.ini,全局共享配置。 -
C++代码 (Meyers Singleton,线程安全):
cpp
class Logger {
public:
static Logger& get() {
static Logger instance; // C++11保证局部静态变量初始化线程安全
return instance;
}
void log(const char* msg) { /*...*/ }
Logger(const Logger&) = delete; // 禁用拷贝
Logger& operator=(const Logger&) = delete; // 禁用赋值
private:
Logger() = default;
};
2. 工厂模式 (Factory Method)
-
解决痛点 :在编写代码时,不知道将来需要实例化哪个具体类。如果直接用
new,一旦换类就要改所有代码。解耦封装了对象创建。 -
特点:把"实例化"推迟到子类。
-
使用场景:
-
跨平台GUI :代码里只写
createButton(),在 Windows 下生成 WinButton,在 Linux 下生成 GTKButton。 -
日志库 :用户配置输出到文件还是数据库,工厂根据配置生成
FileLogger或DBLogger。 -
C++代码:
cpp
struct Product { virtual ~Product() = default; virtual void use() = 0; };
struct ConcreteProduct : Product { void use() override {} };
struct Creator {
virtual std::unique_ptr<Product> create() = 0; // 纯虚工厂
};
struct ConcreteCreator : Creator {
std::unique_ptr<Product> create() override { return std::make_unique<ConcreteProduct>(); }
};
3. 抽象工厂模式 (Abstract Factory)
-
解决痛点 :需要创建一系列相关的对象(产品族),且不能混用。比如:MacOS的按钮不能配Windows的边框。
-
特点:创建"全家桶",保证产品族的一致性。
-
使用场景:
-
游戏换肤:一套皮肤包含背景、按钮、音效,切换皮肤时整个家族一起换。
-
C++代码:
cpp
// 抽象工厂接口:能造一系列东西
struct GUIFactory {
virtual std::unique_ptr<Button> createBtn() = 0;
virtual std::unique_ptr<Window> createWin() = 0;
};
4. 构建者模式 (Builder)
-
解决痛点:对象太复杂,构造函数参数巨多(比如10个参数),容易传错位;或者构建过程需要分步骤(先造头,再造手)。
-
特点:链式调用,清晰易读,分离了"构建算法"和"具体部件"。
-
使用场景:
-
HTTP请求构建 :
.setUrl().setMethod("GET").setHeader().build()。 -
SQL查询生成器 :
.select().from().where().build()。 -
C++代码:
cpp
class Computer { /*...*/ };
class Builder {
Computer c;
public:
Builder& setCPU(string cpu) { c.cpu = cpu; return *this; }
Builder& setRAM(int ram) { c.ram = ram; return *this; }
Computer build() { return c; }
};
5. 原型模式 (Prototype)
-
解决痛点:创建一个对象成本太高(比如要查库、解析大文件),但我们需要一个跟它一模一样的新对象。
-
特点 :基于内存二进制流的拷贝(Clone),比
new快得多。 -
使用场景:
-
游戏怪物刷新:先加载一个"半兽人"模板,然后克隆100个,只修改坐标。
-
C++代码:
cpp
struct Prototype {
virtual std::unique_ptr<Prototype> clone() const = 0;
};
struct Concrete : Prototype {
std::unique_ptr<Prototype> clone() const override {
return std::make_unique<Concrete>(*this); // 调用拷贝构造
}
};
第二部分:结构型模式 (Structural Patterns)
核心目的:优化类和对象的结构,解决"如何组装代码"的问题。
6. 适配器模式 (Adapter)
-
解决痛点 :两个类功能合适但接口名不对口,就像两脚插头插不进三脚插座。我们在应用程序中已经设计好了接口,与这个第三方提供的接口不一致,为了使得这些接口不兼容的类,可以在一起工作了,适配器模式提供了将一个类的接口转化为客户希望的接口。
-
特点:包装一层,进行接口转换。
-
使用场景:
-
老系统改造 :旧接口叫
doWork(),新系统统一调用execute(),写个 Adapter 转换一下。 -
C++代码:
cpp
class OldSystem { public: void specificRequest() {} };
class Target { public: virtual void request() = 0; };
class Adapter : public Target {
OldSystem old;
public:
void request() override { old.specificRequest(); }
};
7. 桥接模式 (Bridge)
-
解决痛点:一个类在两个维度都在变化(例如:形状有圆/方,颜色有红/蓝)。如果用继承,会有 个类。维度多了类会爆炸。
-
特点:(把不同维度的做组合), 把"抽象"和"实现"分离,用组合代替继承。
-
使用场景:
-
图形引擎:Shape(圆、方)和 Renderer(OpenGL、DirectX)分离。
-
Pimpl惯用法:隐藏私有实现,减少头文件依赖,加速编译。
-
C++代码:
cpp
struct Renderer { virtual void render() = 0; }; // 实现维度
class Shape {
protected:
Renderer* renderer; // 桥梁
public:
Shape(Renderer* r) : renderer(r) {}
virtual void draw() = 0;
};
8. 组合模式 (Composite)
-
解决痛点:处理树形结构(如文件夹里有文件,也有文件夹)。希望客户端能统一对待"单个对象"和"组合对象"。
-
特点:(把相同维度的做组合) 递归组合。
-
使用场景:
-
文件系统 :
rm -rf既能删文件,也能删文件夹。 -
GUI容器:Panel 里面可以包含 Button,也可以包含另一个 Panel。
-
C++代码:
cpp
struct Component { virtual void operation() = 0; };
struct Composite : Component {
vector<Component*> children;
void operation() override { for(auto c : children) c->operation(); }
};
9. 装饰器模式 (Decorator)
-
解决痛点:想给对象增加功能,但不想用继承(继承会导致类爆炸,且静态死板)。
-
特点:像"俄罗斯套娃"一样,一层层包装,动态增强功能。
-
使用场景:
-
IO流 :
BufferedInputStream(FileInputStream("file"))。 -
Web中间件:给处理函数加日志、加压缩、加加密。
-
C++代码:
cpp
struct DataSource { virtual void write(string data) = 0; };
class EncryptDecorator : public DataSource {
DataSource* wrapped;
public:
EncryptDecorator(DataSource* s) : wrapped(s) {}
void write(string data) override {
string enc = encrypt(data);
wrapped->write(enc);
}
};
10. 外观模式 (Facade)
-
解决痛点:子系统极其复杂,外部调用者看着头大。
-
特点:提供一个"一键式"的高层接口。
-
使用场景:
-
视频转码器 :内部涉及解封装、解码、重采样、编码、封装。对外只暴露
convert(file, format)。 -
C++代码:
cpp
class Facade {
SubSysA a; SubSysB b;
public:
void easyOperation() { a.step1(); b.step2(); }
};
11. 享元模式 (Flyweight)
- 解决痛点:内存不够用了!因为创建了太多完全相同的细粒度对象。
- 特点:共享内部状态(不变的部分),外部状态由参数传入。
- 使用场景:
- 文字处理:一篇文章10万字,不需要10万个"字"对象,只需要共享26个字母对象的引用,坐标不同即可。
- 游戏地图:百万棵树,模型是一样的,只有位置不一样。
12. 代理模式 (Proxy)
-
解决痛点:不想或者不能直接访问一个对象(要在访问前做权限检查、或者是远程对象、或者对象很大需要延迟加载)。
-
特点:找个替身,控制对原对象的访问。
-
使用场景:
-
智能指针 :
std::shared_ptr是最典型的代理,控制对象的引用计数和销毁。 -
RPC:本地调用的其实是 Stub(代理),Stub 负责发网络包。
-
C++代码:
cpp
class ImageProxy : public Image {
RealImage* realImg = nullptr;
public:
void display() override {
if (!realImg) realImg = new RealImage(); // 延迟加载
realImg->display();
}
};
第三部分:行为型模式 (Behavioral Patterns)
核心目的:解决对象之间"怎么交互、怎么分工"的问题。
13. 策略模式 (Strategy)
-
解决痛点 :一个功能有多种算法(比如计算税率、排序),代码里充满了
if (type == A) ... else if (type == B)...。 -
特点:算法封装成类,可互换。
-
使用场景:
-
支付方式:支付宝、微信、银联。
-
导航路线:最短时间、最短路径、不走高速。
-
C++代码:
cpp
struct Strategy { virtual void execute() = 0; };
class Context {
Strategy* strategy;
public:
void setStrategy(Strategy* s) { strategy = s; }
void doWork() { strategy->execute(); }
};
14. 观察者模式 (Observer)
- 解决痛点:A对象变了,B、C、D对象需要立即知道,但A不想和B、C、D死死耦合。
- 特点:发布-订阅机制。
- 使用场景:
- Excel图表:数据变了,柱状图、饼图自动更新。
- MVC架构:Model变了,View自动更新。
15. 责任链模式 (Chain of Responsibility)
- 解决痛点:一个请求可能被多个人处理,但我不想把请求者和具体的处理者绑定。
- 特点:像踢皮球一样,一级级往下传,直到有人处理。
- 使用场景:
- 审批流:请假3天组长批,7天经理批,30天CEO批。
- Web过滤器:AuthFilter -> LogFilter -> CompressionFilter。
16. 命令模式 (Command)
-
解决痛点 :需要把"请求"存下来,以便将来撤销(Undo) 、重做(Redo) 、或者排队执行。
-
特点:把函数调用封装成对象。
-
使用场景:
-
遥控器:按键对应具体的 Command 对象。
-
数据库事务:记录操作日志以便回滚。
-
C++代码:
cpp
struct Command { virtual void execute() = 0; virtual void undo() = 0; };
class Invoker {
vector<Command*> history;
void call(Command* cmd) { cmd->execute(); history.push_back(cmd); }
void undo() { history.back()->undo(); history.pop_back(); }
};
17. 状态模式 (State)
- 解决痛点 :对象的行为依赖于它的状态,并且会有极其复杂的
switch-case逻辑(例如 TCP 协议栈的状态流转)。 - 特点:每个状态一个类,状态类决定了行为,并负责切换到下一个状态。
- 使用场景:
- 自动售货机:待机状态、投币状态、出货状态。
- 游戏角色:站立(可跳)、跳跃中(不可二段跳)、死亡(不可操作)。
18. 模板方法 (Template Method)
-
解决痛点:做一件事的步骤是固定的,但其中某一步的具体实现各不相同。
-
特点:父类定流程(骨架),子类填空。
-
使用场景:
-
单元测试框架 :
setup()->runTest()->teardown()。 -
C++代码:
cpp
class Base {
public:
void process() { step1(); step2(); } // 模板
protected:
virtual void step1() = 0; // 留给子类实现
void step2() { /* 通用实现 */ }
};
19. 迭代器模式 (Iterator)
- 解决痛点:遍历一个容器,不需要知道它内部是用数组还是链表实现的。
- 现状:C++ STL 已经完美实现了,手写场景很少。
20. 中介者模式 (Mediator)
- 解决痛点:一大堆对象互相乱调,形成网状结构(M对N耦合),维护困难。
- 特点:所有对象只跟中介者说话,星型结构。
- 使用场景:
- 聊天室:用户只发消息给服务器(中介),服务器转发给其他人。
- 机场调度:飞机不直接互通,只听塔台的。
21. 备忘录模式 (Memento)
- 解决痛点:需要保存对象的快照,以便恢复,但又不能暴露对象的私有成员破坏封装。
- 使用场景:游戏存档、编辑器 Ctrl+Z。
22. 访问者模式 (Visitor)
- 解决痛点:类结构(如AST语法树)很稳定,但要在这些类上增加的新操作(如代码检查、格式化)很多且经常变。
- 特点 :双分派技术。把操作逻辑从数据结构中抽离出来。
- 使用场景:
- 编译器:语法树节点类不变,但可以写不同的 Visitor 来做语法分析、优化、机器码生成。
23. 解释器模式 (Interpreter)
- 解决痛点:需要解析一种特定的语言或语法。
- 特点:为语法的规则定义类。
- 使用场景:正则引擎、SQL解析器。但如果是复杂语言,通常用 Lex/Yacc 而不是手写这个模式。
总结:如何避免"乱用"?
- 简单问题不要用模式 :如果你的策略只有两个,用
if-else就行,别搞 策略模式。 - 创建型模式 :当你觉得
new一个对象很麻烦,或者需要灵活替换类时再用。 - 结构型模式:当你觉得类太多、关系太乱、接口对不上时再用。
- 行为型模式 :当你觉得逻辑太复杂,
if太多,或者对象间通信太紧密时再用。