在软件设计中,设计模式(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 系统用 WinButton、WinTextbox
- macOS 系统用 MacButton、MacTextbox
这时就有一个问题:
怎么在程序里自动创建一整套“同风格”的组件,而不用到处 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
“对象的行为取决于当前状态,不同状态有不同逻辑。”
