一、设计模式到底是什么?
先说说我的理解。设计模式,说白了就是「前人踩过的坑,总结出来的套路」。
我记得刚入行那会儿,写代码全靠直觉。一个类能塞几百行,一个函数能传七八个参数。后来项目越做越大,改一个功能要动十几个文件,改完还总出 bug。那时候我才意识到——写代码不能光靠蛮力。
设计模式就是一套「软件工程中的最佳实践」。它不是代码,不是库,而是一种思想。它告诉你:在某种场景下,用某种结构去组织代码,能让你的系统更灵活、更易维护。
GoF(Gang of Four,四人帮)在 1994 年出版了《设计模式:可复用面向对象软件的基础》,里面总结了 23 种经典模式。这 23 种模式,到今天依然是 C++ 开发者的必修课。
核心定义:设计模式是针对特定问题的、可复用的解决方案。它不是代码片段,而是代码组织方式的模板。
二、GoF 23 种设计模式分类
这 23 种模式,按用途分成三大类:创建型、结构型、行为型。我习惯用一句话记:「怎么造、怎么搭、怎么动」。
2.1 创建型模式(5 种)
解决「对象怎么创建」的问题。说白了,就是让你别到处 new。
- 单例模式:全局只有一个实例。我项目中用过一个日志管理器,整个进程只允许一个实例写文件。
- 工厂方法:定义一个创建对象的接口,让子类决定实例化哪个类。
- 抽象工厂:创建一组相关或依赖的对象。比如跨平台 UI 库,Windows 下创建 Windows 风格的按钮,Linux 下创建 Linux 风格的。
- 建造者模式:分步骤构建复杂对象。比如组装一台电脑,CPU、内存、硬盘可以自由搭配。
- 原型模式:通过拷贝已有对象来创建新对象。适合创建成本高的对象。
2.2 结构型模式(7 种)
解决「类和对象怎么组合」的问题。说白了,就是怎么搭积木。
- 适配器模式:让接口不兼容的类能一起工作。我曾经把一个旧系统的日志接口封装成新接口,用的就是适配器。
- 桥接模式:将抽象部分与实现部分分离。比如画图程序,形状和颜色可以独立变化。
- 组合模式:将对象组合成树形结构。文件系统就是典型例子。
- 装饰器模式:动态给对象添加新功能。比如给咖啡加糖、加奶。
- 外观模式:为复杂子系统提供统一接口。我经常用这个模式封装第三方库。
- 享元模式:共享细粒度对象,减少内存占用。比如游戏中的子弹、粒子效果。
- 代理模式:控制对对象的访问。比如懒加载、权限控制。
2.3 行为型模式(11 种)
解决「对象之间怎么通信、怎么分配职责」的问题。说白了,就是怎么动起来。
- 责任链模式:多个对象依次处理请求。比如审批流程,组长→经理→总监。
- 命令模式:将请求封装成对象。支持撤销、重做。
- 解释器模式:定义语言的文法,并解释执行。比如 SQL 解析器。
- 迭代器模式:提供统一方式遍历集合。C++ STL 的迭代器就是典型。
- 中介者模式:用一个中介对象封装对象间的交互。比如聊天室。
- 备忘录模式:保存对象状态,支持恢复。游戏存档就是。
- 观察者模式:一对多依赖,状态变化时通知所有观察者。我项目中用这个做事件驱动。
- 状态模式:对象行为随状态改变。比如 TCP 连接的状态机。
- 策略模式:定义一系列算法,可以互相替换。比如排序算法可以动态切换。
- 模板方法模式:定义算法骨架,子类实现具体步骤。
- 访问者模式:在不改变元素类的前提下,为元素添加新操作。
我的建议:别试图一次性记住所有模式。先掌握最常用的 5-6 种(单例、工厂、观察者、策略、适配器、装饰器),其他用到再查。
三、为什么 C++ 需要设计模式?
你可能会问:C++ 本身已经很灵活了,有模板、多态、RAII,为什么还要学设计模式?
嗯,这个问题我当年也想过。后来在项目中吃了亏才明白——语言特性是工具,设计模式是方法论。
3.1 C++ 的复杂性需要模式来约束
C++ 太灵活了。你可以用指针、引用、模板、虚函数、lambda……但灵活也意味着容易失控。设计模式给了一套「约定俗成」的写法,让团队协作时少踩坑。
我曾经接手过一个项目,里面全是裸指针和手动 new/delete,代码像蜘蛛网一样。后来用工厂模式 + RAII 重构,代码清晰多了。
3.2 设计模式弥补 C++ 的某些不足
比如 C++ 没有内置的「观察者」机制,但你可以用观察者模式自己实现。再比如 C++ 的虚函数可以实现策略模式,但如果你用模板 + 策略模式,性能还能更高。
3.3 设计模式让代码「可读、可测、可维护」
说白了,设计模式不是为了炫技。它是为了让你的代码:
- 可读:别人一看就知道你在干什么
- 可测:每个模块可以独立测试
- 可维护:改一个功能不会牵一发动全身
避坑指南:我曾经见过有人为了用设计模式而用设计模式,一个简单的功能硬套了三个模式,代码反而更复杂了。记住:设计模式是工具,不是目的。过度设计比没有设计更可怕。
四、知识体系总览
下面这张图,是我自己整理的「设计模式知识体系」。你可以把它当作学习地图。
五、一个简单的例子:为什么不用设计模式会痛苦?
假设你要写一个日志系统。一开始只输出到控制台,很简单:
class Logger {
public:
void log(const std::string& msg) {
std::cout << msg << std::endl;
}
};
后来需求变了:还要写文件。你加了个参数:
class Logger {
public:
void log(const std::string& msg, bool toFile = false) {
if (toFile) {
// 写文件...
} else {
std::cout << msg << std::endl;
}
}
};
再后来,要支持网络日志、数据库日志、加密日志……这个类会变成什么样子?
嗯,我见过一个项目里的 Logger 类,光 log 方法就有 200 行,if-else 嵌套了 5 层。每次加新功能,都要改这个类,改完还要回归测试所有功能。
如果用设计模式呢?
- 策略模式:把日志输出方式抽成策略,可以动态切换
- 装饰器模式:给日志加加密、加缓冲,一层套一层
- 单例模式:确保全局只有一个日志实例
你看,设计模式不是让你多写代码,而是让你少改代码。
一句话总结:设计模式是经验的结晶,是代码的「交通规则」。遵守它,你的代码就能跑得更远、更稳。
公众号:蓝海资料掘金营,微信 deep3321