23种设计模式

第一部分:创建型模式 (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。

  • 日志库 :用户配置输出到文件还是数据库,工厂根据配置生成 FileLoggerDBLogger

  • 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 而不是手写这个模式。

总结:如何避免"乱用"?

  1. 简单问题不要用模式 :如果你的策略只有两个,用 if-else 就行,别搞 策略模式
  2. 创建型模式 :当你觉得 new 一个对象很麻烦,或者需要灵活替换类时再用。
  3. 结构型模式:当你觉得类太多、关系太乱、接口对不上时再用。
  4. 行为型模式 :当你觉得逻辑太复杂,if 太多,或者对象间通信太紧密时再用。
相关推荐
m0_748233172 小时前
30秒掌握C++核心精髓
开发语言·c++
Fleshy数模2 小时前
从数据获取到突破限制:Python爬虫进阶实战全攻略
java·开发语言
Duang007_2 小时前
【LeetCodeHot100 超详细Agent启发版本】字母异位词分组 (Group Anagrams)
开发语言·javascript·人工智能·python
froginwe113 小时前
Redis 管道技术
开发语言
风清扬_jd3 小时前
libtorrent-rasterbar-2.0.11编译说明
c++·windows·p2p
u0109272713 小时前
C++中的RAII技术深入
开发语言·c++·算法
彷徨而立3 小时前
【C/C++】strerror、GetLastError 和 errno 的含义和区别?
c语言·c++
superman超哥3 小时前
Serde 性能优化的终极武器
开发语言·rust·编程语言·rust serde·serde性能优化·rust开发工具
琹箐4 小时前
设计模式——观察者模式
观察者模式·设计模式
誰能久伴不乏4 小时前
【Qt实战】工业级多线程串口通信:从底层协议设计到完美收发闭环
linux·c++·qt