分类
技术

设计模式

在软件设计中,设计模式(Design Patterns) 是一套被反复验证的“架构经验总结”,它们帮助我们写出结构更清晰、可维护、可扩展的代码。

设计模式主要分为 三大类:

创建型模式(Creational Patterns)

关注「对象的创建方式」

模式名作用例子/比喻
Singleton(单例)确保一个类只有一个实例程序的“控制中心”或“音频引擎”
Factory Method(工厂方法)子类决定实例化哪种对象不同车厂生产不同车型
Abstract Factory(抽象工厂)创建一族相关对象“苹果工厂”生产 iPhone、Mac、iPad
Builder(建造者)按步骤构建复杂对象建房子:打地基 → 搭框架 → 装修
Prototype(原型)通过克隆现有对象创建新对象游戏里复制角色或武器模板

结构型模式(Structural Patterns)

关注「类与对象的组合关系」

模式名作用例子/比喻
Adapter(适配器)让不兼容的接口协同工作电源转换头(110V 转 220V)
Bridge(桥接)分离抽象与实现,使它们可独立扩展不同平台共用同一个渲染逻辑
Composite(组合)让单个对象和组合对象使用相同接口文件夹与文件系统结构
Decorator(装饰器)动态地为对象添加功能给图片加滤镜、加水印
Facade(外观)提供统一接口封装复杂系统一键“启动游戏”调用多个子系统
Flyweight(享元)共享对象,减少内存占用游戏中同种子弹或树的复用
Proxy(代理)控制对对象的访问网络代理、权限检查、懒加载

行为型模式(Behavioral Patterns)

关注「对象之间的通信与职责分配」

模式名作用例子/比喻
Observer(观察者)一变多通知机制订阅-发布系统、UI事件监听
Strategy(策略)动态切换算法或行为AI不同攻击策略
Command(命令)将请求封装成对象撤销/重做系统
State(状态)对象在不同状态下行为不同游戏角色:走、跑、攻击、死亡
Mediator(中介者)统一管理多个对象间的通信聊天室的消息转发器
Memento(备忘录)保存和恢复对象状态游戏存档系统
Iterator(迭代器)顺序访问集合,不暴露内部结构STL 的 begin() / end()
Chain of Responsibility(责任链)多个对象依次处理请求日志系统的不同级别过滤
Template Method(模板方法)定义算法框架,让子类实现具体步骤游戏 AI 的通用决策流程
Visitor(访问者)对对象结构的统一访问编辑器中“批量操作”不同对象

一、单例模式(Singleton Pattern)

概念

单例模式的目的是:

确保一个类在整个程序中只有一个实例,并且能在任何地方全局访问它。

“只造一个对象,全局都用这一个。”

程序例子:日志系统

#include <iostream>
#include <string>
using namespace std;

class Logger {
private:
    static Logger* instance; // 静态指针保存唯一实例
    Logger() {} // 私有构造函数,外部不能 new
public:
    static Logger* getInstance() {
        if (instance == nullptr)
            instance = new Logger();
        return instance;
    }

    void log(string message) {
        cout << "[Log]: " << message << endl;
    }
};

Logger* Logger::instance = nullptr;

使用方式:

int main() {
    Logger::getInstance()->log("Program started.");
    Logger::getInstance()->log("All systems running.");
}

输出

[Log]: Program started. 
[Log]: All systems running.

你不需要 new;

  • 永远只会存在一个 Logger
  • 全局任何地方都能访问。

核心特征:

特征说明
构造函数是私有的防止外部 new
拥有静态成员指针存放唯一实例
拥有静态访问函数统一获取实例入口

用途:

  • 日志系统(LogManager::Instance()
  • 配置管理器
  • 全局资源管理(如声音、窗口、输入系统)
  • 游戏中的场景控制器、系统管理器

单例 = 全局唯一 + 延迟创建 + 全局访问。


二、工厂模式(Factory Pattern)

概念

工厂模式的目的:

让子类决定创建哪种对象,而不是在代码里直接 new。

想要一个“产品”,你不用亲自去造;
你告诉“工厂”你要什么型号,它帮你造好给你。

举例说明

假设我们有一个基类:

class Shape {
public:
    virtual void draw() = 0;
};

派生类:

class Circle : public Shape {
public:
    void draw() override { cout << "Draw Circle\n"; }
};

class Square : public Shape {
public:
    void draw() override { cout << "Draw Square\n"; }
};

工厂类:

class ShapeFactory {
public:
    static Shape* createShape(const std::string& type) {
        if (type == "circle")
            return new Circle();
        else if (type == "square")
            return new Square();
        else
            return nullptr;
    }
};

使用:

Shape* s1 = ShapeFactory::createShape("circle");
Shape* s2 = ShapeFactory::createShape("square");

优点:

优点说明
解耦代码不需要知道对象怎么创建
可扩展新增类只需修改工厂
易维护管理创建逻辑集中在一处

工厂方法 = 子类决定造什么对象。


三、抽象工厂(Abstract Factory Pattern)

核心思想

创建一整组“相关对象”,而不是单个对象。

抽象工厂 = “一个工厂,能创建一整组相关的产品”。

假设我们要做一个“跨平台 UI 系统”:

  • Windows 系统用 WinButtonWinTextbox
  • macOS 系统用 MacButtonMacTextbox

这时就有一个问题:

怎么在程序里自动创建一整套“同风格”的组件,而不用到处 if 判断平台?

答案:用抽象工厂

举个例子(图形界面工厂)

我们定义一个抽象工厂接口:

// 抽象产品
class Button { public: virtual void click() = 0; };
class Textbox { public: virtual void draw() = 0; };

// 具体产品 - Windows 版本
class WinButton : public Button {
public: void click() override { cout << "Windows Button Clicked\n"; }
};
class WinTextbox : public Textbox {
public: void draw() override { cout << "Windows Textbox Drawn\n"; }
};

// 具体产品 - Mac 版本
class MacButton : public Button {
public: void click() override { cout << "Mac Button Clicked\n"; }
};
class MacTextbox : public Textbox {
public: void draw() override { cout << "Mac Textbox Drawn\n"; }
};

// 抽象工厂
class GUIFactory {
public:
    virtual Button* createButton() = 0;
    virtual Textbox* createTextbox() = 0;
};

// 具体工厂 - Windows
class WinFactory : public GUIFactory {
public:
    Button* createButton() override { return new WinButton(); }
    Textbox* createTextbox() override { return new WinTextbox(); }
};

// 具体工厂 - Mac
class MacFactory : public GUIFactory {
public:
    Button* createButton() override { return new MacButton(); }
    Textbox* createTextbox() override { return new MacTextbox(); }
};

使用方式

GUIFactory* factory = new MacFactory();  // 或 new WinFactory()
Button* btn = factory->createButton();
Textbox* box = factory->createTextbox();

btn->click();
box->draw();

优点总结

优点说明
产品族一致保证创建出的组件风格统一(不会混用 Mac 按钮和 Win 文本框)
易于扩展新增 LinuxFactory 只需实现一组新类
高层代码与具体平台解耦上层逻辑无需关心创建细节

图示思维

         [抽象工厂 GUIFactory]
           /             \
 [WinFactory]           [MacFactory]
    |   |                   |   |
 [WinButton][WinTextbox] [MacButton][MacTextbox]

抽象工厂能一次性生产一整套相关对象。


模式目的返回对象数量谁决定创建什么常见用途
单例全局唯一实例1类本身全局管理器、日志、配置
工厂延迟对象创建1子类UI 控件、武器工厂
抽象工厂创建产品族多个子类跨平台界面、主题样式

四、建造者模式(Builder Pattern)

核心思想

一步一步地构造复杂对象,而不是一次性 new 出来。

举个例子:

假设我们要建造一栋房子(House),
有地基、墙、屋顶、门窗、电线、水管……
不同风格的房子建造步骤相似,但细节不同。

解决方案:用建造者模式来分离“构建步骤”与“具体细节”。

举个例子(建造房子)

// 产品
class House {
public:
    void setFoundation(const string& f) { foundation = f; }
    void setStructure(const string& s) { structure = s; }
    void setRoof(const string& r) { roof = r; }

    void show() {
        cout << "House with " << foundation << ", " << structure << ", " << roof << endl;
    }
private:
    string foundation, structure, roof;
};

// 抽象建造者
class HouseBuilder {
public:
    virtual void buildFoundation() = 0;
    virtual void buildStructure() = 0;
    virtual void buildRoof() = 0;
    virtual House* getHouse() = 0;
};

// 具体建造者 - 木屋
class WoodenHouseBuilder : public HouseBuilder {
private:
    House* house;
public:
    WoodenHouseBuilder() { house = new House(); }
    void buildFoundation() override { house->setFoundation("Wood Foundation"); }
    void buildStructure() override { house->setStructure("Wood Structure"); }
    void buildRoof() override { house->setRoof("Wood Roof"); }
    House* getHouse() override { return house; }
};

// 指挥者(Director)
class Engineer {
private:
    HouseBuilder* builder;
public:
    Engineer(HouseBuilder* b) : builder(b) {}
    void constructHouse() {
        builder->buildFoundation();
        builder->buildStructure();
        builder->buildRoof();
    }
};

使用方式

WoodenHouseBuilder* woodBuilder = new WoodenHouseBuilder();
Engineer engineer(woodBuilder);

engineer.constructHouse();
House* house = woodBuilder->getHouse();
house->show();

输出:

House with Wood Foundation, Wood Structure, Wood Roof

优点总结

优点说明
分离构建过程与表示结构复杂的对象也能灵活构建
可复用构建步骤不同房子共用建造流程,细节可变
指挥者可复用同样的流程可生成不同类型产品

图示思维

[Engineer Director]
       ↓
 [HouseBuilder 抽象建造者]
       ↓
[WoodenHouseBuilder / StoneHouseBuilder]
       ↓
        → 组装出 [House]

建造者模式更像“盖房子的工地流程表”——
流程是一样的,材料和细节由具体建造者决定。


五、适配器模式(Adapter Pattern)

核心思想

让两个接口不兼容的类可以一起工作。

你可以把它理解成“接口转换器”或“插头转换头”。
当你有一个旧系统或第三方库,接口和你需要的不同,就用适配器把它“转接”起来。

举个例子(现实比喻)

你有一个「笔记本电源插头」是三孔的,
但家里插座是两孔的——怎么办?
插上一个转换头(Adapter)

程序里也是同样的。

程序例子:音频播放器

假设我们有一个 MediaPlayer 接口,只能播放 MP3:

class MediaPlayer {
public:
    virtual void play(string audioType, string fileName) = 0;
};

而有一个现成的第三方类 AdvancedPlayer 能播放 MP4 和 VLC:

class AdvancedPlayer {
public:
    void playMP4(string file) { cout << "Playing MP4: " << file << endl; }
    void playVLC(string file) { cout << "Playing VLC: " << file << endl; }
};

但是!我们现有的系统只认识 MediaPlayer 接口。

解决:写一个适配器

class MediaAdapter : public MediaPlayer {
private:
    AdvancedPlayer* advancedPlayer;
public:
    MediaAdapter() { advancedPlayer = new AdvancedPlayer(); }

    void play(string audioType, string fileName) override {
        if (audioType == "mp4")
            advancedPlayer->playMP4(fileName);
        else if (audioType == "vlc")
            advancedPlayer->playVLC(fileName);
        else
            cout << "Unsupported format" << endl;
    }
};

客户端使用

int main() {
    MediaPlayer* player = new MediaAdapter();
    player->play("mp4", "video.mp4");
    player->play("vlc", "movie.vlc");
    player->play("mp3", "song.mp3");
}

输出:

Playing MP4: video.mp4
Playing VLC: movie.vlc
Unsupported format

优点总结

优点说明
不修改原有代码可以复用旧接口或第三方库
实现接口兼容不同系统可以无缝协作
开闭原则新的格式可通过新适配器扩展

图示理解

[Client]
   ↓ (使用统一接口)
[MediaPlayer]
   ↓
[MediaAdapter]
   ↓
[AdvancedPlayer]

适配器 = “让原本不兼容的类能协同工作”。


六、装饰器模式(Decorator Pattern)

核心思想

动态地给对象增加功能,而不修改它的原始类。

换句话说:

“不改源码,却能加新能力”。

现实比喻

你有一杯咖啡 ☕,
想加点牛奶、糖、巧克力——
你不重新煮一杯,而是往原来的咖啡里“装饰”新配料。

这就是装饰器

程序例子:咖啡系

class Coffee {
public:
    virtual string getDescription() = 0;
    virtual double cost() = 0;
    virtual ~Coffee() {}
};

基础咖啡类

class SimpleCoffee : public Coffee {
public:
    string getDescription() override { return "Simple Coffee"; }
    double cost() override { return 2.0; }
};

装饰器基类

class CoffeeDecorator : public Coffee {
protected:
    Coffee* coffee;  // 被装饰的对象
public:
    CoffeeDecorator(Coffee* c) : coffee(c) {}
};

具体装饰器:加牛奶

class MilkDecorator : public CoffeeDecorator {
public:
    MilkDecorator(Coffee* c) : CoffeeDecorator(c) {}
    string getDescription() override { return coffee->getDescription() + ", Milk"; }
    double cost() override { return coffee->cost() + 0.5; }
};

具体装饰器:加糖

class SugarDecorator : public CoffeeDecorator {
public:
    SugarDecorator(Coffee* c) : CoffeeDecorator(c) {}
    string getDescription() override { return coffee->getDescription() + ", Sugar"; }
    double cost() override { return coffee->cost() + 0.3; }
};

使用方法

int main() {
    Coffee* coffee = new SimpleCoffee();
    coffee = new MilkDecorator(coffee);
    coffee = new SugarDecorator(coffee);

    cout << coffee->getDescription() << endl;
    cout << "Total: $" << coffee->cost() << endl;
}

输出:

Simple Coffee, Milk, Sugar
Total: $2.8

优点总结

优点说明
不修改类就能添加功能满足开闭原则
可以自由组合功能类似“叠加 Buff”
更灵活比继承更灵活的扩展方式

图示理解

[Client]
   ↓
[Coffee] ← 抽象接口
   ↓
[SimpleCoffee] ← 被装饰对象
   ↓
[MilkDecorator]
   ↓
[SugarDecorator]

装饰器 = “动态叠加功能的包装器”。


七、观察者模式(Observer Pattern)

核心思想

一变多通知机制:当一个对象的状态变化时,自动通知所有依赖它的对象。

当一个对象(称为 Subject / 被观察者)发生变化时,它会自动通知所有“关心它”的对象(称为 Observer / 观察者)。
这样就可以实现对象之间的解耦,被观察者不需要知道观察者的具体实现。

程序例子(天气系统)

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 观察者接口
class Observer {
public:
    virtual void update(float temp, float humidity) = 0;
};

// 主题(被观察者)
class WeatherStation {
    vector<Observer*> observers;
    float temperature = 0.0, humidity = 0.0;
public:
    void addObserver(Observer* obs) { observers.push_back(obs); }
    void setWeather(float t, float h) {
        temperature = t; humidity = h;
        notifyAll();
    }
private:
    void notifyAll() {
        for (auto o : observers)
            o->update(temperature, humidity);
    }
};

// 具体观察者
class Display : public Observer {
    string name;
public:
    Display(string n): name(n) {}
    void update(float t, float h) override {
        cout << name << " - Temp: " << t << " Humidity: " << h << endl;
    }
};

使用方式

int main() {
    WeatherStation station;
    Display phone("Phone");
    Display tv("TV");

    station.addObserver(&phone);
    station.addObserver(&tv);

    station.setWeather(25.5, 60);
}

输出:

Phone - Temp: 25.5 Humidity: 60
TV - Temp: 25.5 Humidity: 60

python例子

想象你订阅了一个 YouTube 频道:

  • 当博主上传新视频时(Subject 状态变化);
  • 所有订阅者(Observers)都会收到通知。
class Subject:
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        self._observers.append(observer)

    def detach(self, observer):
        self._observers.remove(observer)

    def notify(self, message):
        for observer in self._observers:
            observer.update(message)


class Observer:
    def update(self, message):
        pass


class ConcreteObserver(Observer):
    def __init__(self, name):
        self._name = name

    def update(self, message):
        print(f"{self._name} 收到通知:{message}")


# 使用示例
if __name__ == "__main__":
    subject = Subject()

    user1 = ConcreteObserver("小明")
    user2 = ConcreteObserver("小红")

    subject.attach(user1)
    subject.attach(user2)

    subject.notify("新视频发布啦!")

输出:

小明 收到通知:新视频发布啦!
小红 收到通知:新视频发布啦!

优点

优点说明
自动通知机制数据变化→视图自动更新
松耦合发布者不依赖订阅者数量与类型
广泛应用GUI、事件系统、信号槽、股票行情等

图示理解

[WeatherStation]
   │    (通知)
   ▼
[DisplayA] [DisplayB] [DisplayC]

“我变了,就告诉所有订阅我的人。”


八、策略模式(Strategy Pattern)

核心思想

定义一组算法(策略),让它们可以互相替换,运行时灵活切换。

就像游戏角色攻击时可以选择不同策略:

  • 近战攻击
  • 远程射击
  • 魔法攻击
    角色不变,但攻击方式可以自由切换。

程序例子(AI 攻击策略)

// 策略接口
class AttackStrategy {
public:
    virtual void attack() = 0;
};

// 具体策略
class MeleeAttack : public AttackStrategy {
public:
    void attack() override { cout << "Performing melee attack!\n"; }
};
class RangedAttack : public AttackStrategy {
public:
    void attack() override { cout << "Performing ranged attack!\n"; }
};

// 上下文类
class Enemy {
private:
    AttackStrategy* strategy;
public:
    void setStrategy(AttackStrategy* s) { strategy = s; }
    void performAttack() { strategy->attack(); }
};

使用方式

int main() {
    Enemy enemy;
    MeleeAttack melee;
    RangedAttack ranged;

    enemy.setStrategy(&melee);
    enemy.performAttack();

    enemy.setStrategy(&ranged);
    enemy.performAttack();
}

输出:

Performing melee attack!
Performing ranged attack!

优点

优点说明
✅ 易于扩展算法新增策略不用改主类
✅ 运行时切换行为灵活多变
✅ 消除大量 if-else代码更清晰

图示理解

[Enemy]
   ↓
(调用)
[AttackStrategy]
   ├── MeleeAttack
   └── RangedAttack

“不同的算法,同样的接口,自由切换。”


九、命令模式(Command Pattern)

核心思想

将“请求”封装成对象,从而能记录、撤销、排队执行。

就像游戏里输入系统:

  • 每个按键动作(移动、攻击、跳跃)都是一个命令;
  • 可以记录命令历史;
  • 还能“撤销”或“重播”指令。

程序例子(游戏角色命令)

// 接收者(执行实际操作)
class GameActor {
public:
    void jump() { cout << "Actor jumps!\n"; }
    void shoot() { cout << "Actor shoots!\n"; }
};

// 命令接口
class Command {
public:
    virtual void execute() = 0;
};

// 具体命令
class JumpCommand : public Command {
    GameActor* actor;
public:
    JumpCommand(GameActor* a) : actor(a) {}
    void execute() override { actor->jump(); }
};
class ShootCommand : public Command {
    GameActor* actor;
public:
    ShootCommand(GameActor* a) : actor(a) {}
    void execute() override { actor->shoot(); }
};

// 调用者(Invoker)
class InputHandler {
    Command *jumpCmd, *shootCmd;
public:
    InputHandler(Command* j, Command* s) : jumpCmd(j), shootCmd(s) {}
    void pressJump() { jumpCmd->execute(); }
    void pressShoot() { shootCmd->execute(); }
};

使用方式

int main() {
    GameActor actor;
    JumpCommand jump(&actor);
    ShootCommand shoot(&actor);
    InputHandler handler(&jump, &shoot);

    handler.pressJump();
    handler.pressShoot();
}

输出:

Actor jumps!
Actor shoots!

优点

优点说明
可撤销、可重做保存命令历史
易于扩展新命令独立实现
解耦调用者与执行者完全分离

图示理解

[InputHandler]
   ↓
[Command]
   ↓
[GameActor]

“命令是把操作打包成对象,让你可以记录、撤销、重播它。”


十、状态模式(State Pattern)

核心思想

对象在不同状态下表现出不同的行为。

就像游戏角色在不同状态下(走路、跑步、攻击、死亡),
输入相同的指令,行为却完全不同。

程序例子(角色状态)

class Character; // 前置声明

// 状态接口
class State {
public:
    virtual void handleInput(Character* c, string input) = 0;
    virtual string getName() = 0;
};

// 角色类(上下文)
class Character {
private:
    State* currentState;
public:
    Character(State* s) : currentState(s) {}
    void setState(State* s) { currentState = s; }
    void handleInput(string input) { currentState->handleInput(this, input); }
    void showState() { cout << "Current State: " << currentState->getName() << endl; }
};

// 具体状态
class IdleState : public State {
public:
    void handleInput(Character* c, string input) override {
        if (input == "run") c->setState(new RunningState());
        else cout << "Idle: doing nothing.\n";
    }
    string getName() override { return "Idle"; }
};

class RunningState : public State {
public:
    void handleInput(Character* c, string input) override {
        if (input == "stop") c->setState(new IdleState());
        else cout << "Running fast!\n";
    }
    string getName() override { return "Running"; }
};

使用方式

int main() {
    Character hero(new IdleState());
    hero.showState();

    hero.handleInput("run");
    hero.showState();

    hero.handleInput("stop");
    hero.showState();
}

输出:

Current State: Idle
Current State: Running
Current State: Idle

优点

优点说明
清晰分离状态行为各状态独立、互不干扰
消除 if-else 状态判断由多态自动处理
动态切换状态更符合现实逻辑

图示理解

[Character]
   ↓(委托)
[State]
   ├── IdleState
   └── RunningState

“对象的行为取决于当前状态,不同状态有不同逻辑。”

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

在此处输入验证码 : *

Reload Image