设计模式小记

今天学习一些设计模式的内容,这个其实也没有啥可参考的内容,主要就是问AI,等我看完相关的书籍再来补充,这里主要结合实际的代码来讲解思想和应用场景。

所谓的设计模式,是指我们如何去设计代码,当我们面对大体量的代码时,设计模式会非常重要。设计模式本身不是代码模板,它是业界沉淀下来、针对高频软件难题的标准解决方案,本质是一套场景化的设计思路工具箱。遇到特定耦合、对象创建、流程管控、接口兼容等工程问题时,我们可以选用对应的模式规范代码结构,规避混乱的 if-else、无限继承、紧耦合、难以扩展等坏味道,它不是强制语法,而是权衡取舍后的架构经验。

在我们开始介绍设计模式前,我们需要先对设计原则有一些基本的了解:

原则简称 全称 核心定义 通俗理解 典型反面案例 相关设计模式
SRP 单一职责原则Single Responsibility Principle 一个类仅有一个引起它变化的原因 一个类只负责一件事;职责尽量收敛 用户类同时实现用户 CRUD、文件存储、日志打印 几乎所有模式底层遵循;命令模式、职责链模式
OCP 开闭原则Open Closed Principle 对扩展开放,对修改关闭 新增功能尽量新增类 / 代码,少改动稳定旧代码 新增算法时,直接在原有函数堆大量 if-else 策略、装饰器、工厂方法、模板方法
LSP 里氏替换原则Liskov Substitution Principle 子类可以完全替代父类,不破坏原有程序行为 继承不能篡改父类语义;父类出现地方,子类能无缝顶替 正方形继承长方形,重写宽高方法破坏父类逻辑 模板方法、工厂方法(所有依赖继承多态的模式)
ISP 接口隔离原则Interface Segregation Principle 客户端不依赖不需要的接口;优先多个小接口,拒绝臃肿大接口 不要强迫类实现一堆无用空方法 一个巨量通用接口,包含文件、网络、数据库所有方法 适配器模式、外观模式
DIP 依赖倒置原则Dependency Inversion Principle 高层、低层模块都依赖抽象;抽象不依赖细节,细节依赖抽象 面向接口 / 抽象编程,不要直接 new 具体实现 业务逻辑直接硬编码依赖 MySQL 具体类,无法切换数据库 工厂系列模式、策略模式
LoD 迪米特法则(最少知识原则)Law of Demeter 对象只和 "直接朋友" 交互,尽可能少了解其他对象内部 尽量不要 a.b.c.doSomething ();减少耦合 业务代码层层访问对象内部成员,链式调用深入多层 外观模式、中介者模式
CRP 合成复用原则组合优于继承 优先使用对象组合 / 聚合实现复用,谨慎使用类继承 继承是静态强耦合;组合运行时可灵活替换依赖 疯狂多层继承实现功能扩展,引发类爆炸 装饰器、桥接、策略模式
DRY Don't Repeat Yourself 不要重复自己 重复逻辑抽取公共模块,杜绝复制粘贴代码 多处一模一样的校验、计算代码分散在各个函数 通用底层思想,不限特定模式
KISS Keep It Simple, Stupid 保持简单 能用简单方案就不要引入复杂抽象 简单需求强行上多层架构、各种设计模式 通用指导思想,避免过度设计
YAGNI You Aren't Gonna Need It 你并不需要它 不为未来假想需求提前做设计 预估以后会扩展,提前搭建大量抽象,结果需求从未到来 通用指导思想,对抗过度设计

设计模式总的来说有23种:

分类 模式名称 核心作用 典型适用场景 关键特点
创建型(5 种) 负责对象实例化,隔离创建与使用 单例模式 Singleton 保证一个类仅有一个实例,提供全局访问点 配置管理器、日志管理器、连接池 控制实例数量,注意多线程安全
工厂方法 Factory Method 定义创建对象接口,由子类决定实例化哪个类 同类产品有多种实现,需要扩展产品 一个工厂对应一种产品,遵循开闭原则
抽象工厂 Abstract Factory 创建一系列相关 / 相互依赖的对象家族 多套配套产品(GUI 跨平台控件:Windows 按钮 / Mac 按钮) 生产产品族,不单一产品
建造者 Builder 分步构造复杂对象,分离构建与表示 对象构造步骤固定,但内部表示多样(组装电脑、报文构造) 相同构建流程,产出不同成品
原型 Prototype 通过克隆已有对象创建新实例 对象创建成本高;大量相似对象 基于副本复制,无需重新 new
结构型(7 种) 组装类 / 对象形成更大结构,灵活组合 适配器 Adapter 将一个接口转换成客户端期望的另一个接口 兼容旧代码、第三方 SDK 接口不匹配 分为类适配器(继承)、对象适配器(组合)
桥接 Bridge 分离抽象与实现,两者可独立扩展 存在两个变化维度(形状 + 颜色、文件 + 格式) 用组合代替多层继承,消灭类爆炸
组合 Composite 统一对待单个对象和对象容器(树形结构) 文件目录、菜单、树节点、UI 容器 叶子节点与容器对外接口一致
装饰器 Decorator 动态给对象增加职责,不修改原有代码 IO 流、动态叠加技能 / 特效 多层嵌套包装,优于继承扩展
外观 Facade 提供统一高层接口,简化复杂子系统调用 复杂底层模块封装,对外暴露简易入口 降低调用方复杂度,解耦
享元 Flyweight 复用大量细粒度重复对象,节省内存 文字编辑器字符、游戏大量同类型粒子 区分内部状态 (共享)、外部状态 (外部传入)
代理 Proxy 控制对目标对象的访问,增加额外逻辑 远程代理、延迟加载、权限校验、缓存 和适配器区别:接口保持一致;适配器做接口转换
行为型(11 种) 管控对象间通信、算法、职责分配 责任链 Chain of Responsibility 请求沿着一条处理链依次传递,直到被处理 审批流、过滤器、日志级别处理 请求发送者与接收者解耦
命令 Command 将请求封装为对象,支持撤销、排队、日志 菜单按钮、宏命令、事务操作、撤销功能 请求本身变成可存储对象
迭代器 Iterator 顺序遍历容器元素,不暴露容器内部结构 List、Tree 自定义遍历器 分离容器和遍历逻辑
中介者 Mediator 使用中介对象封装一组对象交互,消除多对多依赖 聊天室、UI 组件联动 多个对象不再互相持有引用,统一找中介
备忘录 Memento 在不破坏封装前提下保存对象快照,支持恢复 编辑器撤销、游戏存档 外部不能访问对象内部状态
观察者 Observer 一对多订阅通知,状态变化自动通知所有订阅者 事件总线、消息订阅、GUI 事件 发布 - 订阅基础模型
状态 State 对象行为随内部状态改变,消除大量 if/switch 订单状态、游戏角色状态(待机 / 攻击 / 眩晕) 状态独立成类,状态切换驱动行为变化
策略 Strategy 定义一系列可互换算法,运行时动态替换 排序算法、支付方式、不同压缩算法 消除算法分支判断
模板方法 Template Method 父类定义算法骨架,子类实现可变步骤 框架固定流程,业务自定义部分逻辑 继承实现,固定流程,扩展可变节点
访问者 Visitor 在不修改类前提下,新增作用于对象的操作 语法树遍历、多类型元素执行多种运算 双分派;适合稳定的数据结构
解释器 Interpreter 定义语言文法,构建解释器解析表达式 正则表达式、简单脚本、规则引擎 使用较少,适合简单小型语法规则

下面我来分别用简单的代码示例展示其用途:

创建型模式

单例模式 Singleton

单例模式保证一个类全局仅有唯一实例,并提供统一访问入口,实现上私有化构造函数、使用静态局部实例实现懒加载,禁用拷贝防止实例复制,在游戏开发中常用于全局资源管理器、配置表管理器、输入管理器、音效管理器、游戏全局事件中心、玩家数据管理器,这类模块整个游戏运行期间只需要一份实例。

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

class AudioManager {
protected:
    AudioManager() = default;
public:
    static AudioManager& GetInstance() {
        static AudioManager instance;
        return instance;
    }
    void PlaySound(const string& name) {
        cout << "播放音效:" << name << endl;
    }
    AudioManager(const AudioManager&) = delete;
    AudioManager& operator=(const AudioManager&) = delete;
};

int main() {
    AudioManager::GetInstance().PlaySound("attack.wav");
    return 0;
}

工厂方法 Factory Method

工厂方法定义创建对象的抽象接口,将实例化具体产品的逻辑延迟到子类,每种产品对应专属工厂,新增对象种类只需要扩展产品类和工厂类,游戏里典型用途是怪物生成器、不同子弹类型创建、技能实体生成、道具实例生产,方便热更新扩展怪物、道具种类。

cpp 复制代码
#include <iostream>
using namespace std;

class Monster {
public:
    virtual ~Monster() = default;
    virtual void Show() = 0;
};
class Goblin : public Monster {
public:
    void Show() override { cout << "生成哥布林怪物" << endl; }
};
class Slime : public Monster {
public:
    void Show() override { cout << "生成史莱姆怪物" << endl; }
};

class MonsterFactory {
public:
    virtual ~MonsterFactory() = default;
    virtual Monster* CreateMonster() = 0;
};
class GoblinFactory : public MonsterFactory {
public:
    Monster* CreateMonster() override { return new Goblin(); }
};
class SlimeFactory : public MonsterFactory {
public:
    Monster* CreateMonster() override { return new Slime(); }
};

int main() {
    MonsterFactory* factory = new GoblinFactory();
    Monster* m = factory->CreateMonster();
    m->Show();
    delete m;
    delete factory;
    return 0;
}

抽象工厂 Abstract Factory

抽象工厂用于创建一组相互配套、属于同一个产品族的对象,保证成套资源统一创建,实现上抽象工厂定义整套对象创建接口,具体工厂产出完整一套相关实体,游戏开发中适用于不同阵营套装(人族装备 / 魔族装备)、不同风格 UI 套件、不同主题特效组、不同关卡配套怪物 + 道具组合。

cpp 复制代码
#include <iostream>
using namespace std;

class Weapon {
public:
    virtual ~Weapon() = default;
    virtual void ShowWeapon() = 0;
};
class Armor {
public:
    virtual ~Armor() = default;
    virtual void ShowArmor() = 0;
};

class HumanSword : public Weapon {
public:
    void ShowWeapon() override { cout << "人族长剑" << endl; }
};
class HumanArmor : public Armor {
public:
    void ShowArmor() override { cout << "人族板甲" << endl; }
};

class RaceFactory {
public:
    virtual ~RaceFactory() = default;
    virtual Weapon* CreateWeapon() = 0;
    virtual Armor* CreateArmor() = 0;
};
class HumanFactory : public RaceFactory {
public:
    Weapon* CreateWeapon() override { return new HumanSword(); }
    Armor* CreateArmor() override { return new HumanArmor(); }
};

int main() {
    RaceFactory* factory = new HumanFactory();
    Weapon* w = factory->CreateWeapon();
    Armor* a = factory->CreateArmor();
    w->ShowWeapon();
    a->ShowArmor();
    delete w; delete a; delete factory;
    return 0;
}

工厂方法与抽象工厂均依托虚函数将对象创建逻辑交由子类实现,核心差异在于生产范围:工厂方法的抽象工厂仅定义单一产品的创建接口,每个具体工厂只负责生成一类产品,适合单一品类下扩展不同实例;抽象工厂在顶层定义一组多个创建接口,要求具体工厂产出相互配套的整套产品族,以此保证产品之间的搭配一致性,新增产品种类时需要修改顶层抽象接口,维护成本更高,二者可以简单概括为一厂一物和一厂一套。

建造者模式 Builder

建造者将复杂对象的构建流程与对象最终表现分离,相同构造流程可以生成不同配置的实例,依靠建造者分步组装属性、指挥者统一调度构造流程,游戏中适合构造复杂角色(属性、外观、技能、装备分步拼装)、构建技能效果、组装战斗单位、生成复杂关卡数据。

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

class Hero {
public:
    string weapon;
    string armor;
    void ShowInfo() {
        cout << "武器:" << weapon << ",护甲:" << armor << endl;
    }
};

class HeroBuilder {
public:
    virtual ~HeroBuilder() = default;
    virtual void BuildWeapon() = 0;
    virtual void BuildArmor() = 0;
    virtual Hero GetResult() = 0;
};

class WarriorBuilder : public HeroBuilder {
    Hero hero;
public:
    void BuildWeapon() override { hero.weapon = "重剑"; }
    void BuildArmor() override { hero.armor = "重甲"; }
    Hero GetResult() override { return hero; }
};

class Director {
public:
    Hero Construct(HeroBuilder& builder) {
        builder.BuildWeapon();
        builder.BuildArmor();
        return builder.GetResult();
    }
};

int main() {
    Director dir;
    WarriorBuilder builder;
    Hero h = dir.Construct(builder);
    h.ShowInfo();
    return 0;
}

工厂模式侧重直接批量生成多个完整、相似的对象,调用后直接得到成品,不关注对象内部构造细节;建造者面向单个结构复杂的对象,把构建过程拆解成多步独立组装工序,在统一构建流程下灵活替换各个组成部件,自由调整对象内部组成与属性配置,用来打造差异化的复杂实例,而非单纯批量产出大量同类对象。

原型模式 Prototype

原型模式通过复制已有对象创建新实例,避开开销巨大的初始化、资源加载流程,基类提供 Clone 克隆接口,子类实现拷贝逻辑,游戏里高频使用在对象池:大量怪物、子弹、粒子、投射物频繁生成销毁,直接克隆原型模板,省去反复加载资源耗时。

cpp 复制代码
#include <iostream>
using namespace std;

class Bullet {
public:
    int damage;
    Bullet(int dmg) : damage(dmg) {}
    virtual ~Bullet() = default;
    virtual Bullet* Clone() = 0;
    void PrintInfo() { cout << "子弹伤害:" << damage << endl; }
};

class FireBullet : public Bullet {
public:
    FireBullet(int dmg) : Bullet(dmg) {}
    Bullet* Clone() override {
        return new FireBullet(*this);
    }
};

int main() {
    Bullet* templateBullet = new FireBullet(50);
    Bullet* bullet1 = templateBullet->Clone();
    bullet1->PrintInfo();
    delete templateBullet;
    delete bullet1;
    return 0;
}

对象池,无需多言,没有人不接触的原型模式。

结构型模式

适配器模式 Adapter

适配器转换现有类的接口,让接口不兼容的模块能够互通,采用组合方式包裹原有对象,对外暴露统一接口,游戏开发常用于对接第三方物理 SDK、兼容新旧两套特效接口、统一不同来源资源加载接口、适配老版本技能数据结构。

cpp 复制代码
#include <iostream>
using namespace std;

// 游戏期望统一技能接口
class ISkill {
public:
    virtual ~ISkill() = default;
    virtual void Cast() { cout << "释放技能" << endl; }
};
// 旧版特效类,接口不匹配
class OldEffect {
public:
    void OldPlay() { cout << "旧版特效播放" << endl; }
};
// 适配器包装旧特效
class SkillAdapter : public ISkill {
    OldEffect* effect;
public:
    SkillAdapter(OldEffect* e) : effect(e) {}
    void Cast() override {
        effect->OldPlay();
    }
};

int main() {
    OldEffect oldEf;
    ISkill* skill = new SkillAdapter(&oldEf);
    skill->Cast();
    delete skill;
    return 0;
}

适配器本质就是一段胶水代码,用来抹平两套互不兼容的接口,对外提供统一调用入口,不需要改动原有旧模块的源码,就能让新旧组件、第三方 SDK 顺利协同工作。

桥接模式 Bridge

桥接模式将两个独立变化维度拆分为抽象层与实现层,使用组合代替多层继承避免类爆炸,两个维度可以独立扩展,游戏典型场景:角色 + 技能、技能本体 + 不同特效、单位类型 + 移动方式、物体形状 + 材质。

cpp 复制代码
#include <iostream>
using namespace std;

// 实现层:移动方式
class MoveMode {
public:
    virtual ~MoveMode() = default;
    virtual void Move() = 0;
};
class Walk : public MoveMode {
public:
    void Move() override { cout << "步行移动" << endl; }
};
class Fly : public MoveMode {
public:
    void Move() override { cout << "飞行移动" << endl; }
};

// 抽象层:游戏单位
class Unit {
protected:
    MoveMode* move;
public:
    Unit(MoveMode* m) : move(m) {}
    virtual ~Unit() = default;
    virtual void Action() = 0;
};
class MonsterUnit : public Unit {
public:
    MonsterUnit(MoveMode* m) : Unit(m) {}
    void Action() override {
        cout << "怪物开始行动:";
        move->Move();
    }
};

int main() {
    MoveMode* fly = new Fly();
    Unit* monster = new MonsterUnit(fly);
    monster->Action();
    delete monster;
    delete fly;
    return 0;
}

假设游戏单位有怪物、玩家(第一层:抽象维度);移动方式有步行、飞行、传送(第二层:实现维度)。 如果只用继承实现:怪物步行、怪物飞行、玩家步行、玩家飞行...... 后续新增单位或者新增移动方式,子类会疯狂增多。

桥接的做法:把【单位类型】和【移动方式】拆开,移动方式单独做成一套类(实现层),单位内部持有一个移动方式对象,运行时传入任意移动实现。新增一种移动方式,不需要修改任何单位代码;新增单位类型,也不用新增一堆移动相关子类。抽象层负责对外业务接口,实现层负责底层能力,两者依靠组合连接,这道组合关系就是 "桥"。

组合模式 Composite

组合模式构建树形层级结构,统一处理叶子节点和容器节点,客户端不需要区分单个实体和实体集合,游戏大量用于 UI 层级节点、技能效果树、buff 树形结构、场景物体分组、任务目录结构、技能节点编辑器。

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class GameObject {
public:
    string name;
    GameObject(string n) : name(n) {}
    virtual ~GameObject() = default;
    virtual void Show(int depth = 0) = 0;
    virtual void Add(GameObject*) {}
};

// 叶子:独立物体
class Actor : public GameObject {
public:
    Actor(string n) : GameObject(n) {}
    void Show(int depth) override {
        cout << string(depth, '-') << name << " [角色实体]" << endl;
    }
};
// 容器:物体组
class Group : public GameObject {
    vector<GameObject*> children;
public:
    Group(string n) : GameObject(n) {}
    void Add(GameObject* obj) override { children.push_back(obj); }
    void Show(int depth = 0) override {
        cout << string(depth, '-') << name << " [物体分组]" << endl;
        for (auto c : children) c->Show(depth + 1);
    }
};

int main() {
    Group sceneGroup("场景根节点");
    Actor* player = new Actor("玩家");
    Group* monsterGroup = new Group("怪物组");
    monsterGroup->Add(new Actor("史莱姆"));
    sceneGroup.Add(player);
    sceneGroup.Add(monsterGroup);
    sceneGroup.Show();
    return 0;
}

装饰器模式 Decorator

装饰器可以动态叠加功能到对象上,无需修改原始类代码,灵活增删特性,游戏最经典场景:动态叠加 Buff、装备带来属性加成、技能附加特效、攻击叠加元素伤害,运行时自由组合多种效果。

cpp 复制代码
#include <iostream>
using namespace std;

class Attack {
public:
    virtual ~Attack() = default;
    virtual int GetDamage() = 0;
};
class BaseAttack : public Attack {
public:
    int GetDamage() override { return 100; }
};

class AttackDecorator : public Attack {
protected:
    Attack* innerAtk;
public:
    AttackDecorator(Attack* a) : innerAtk(a) {}
};
class FireBuff : public AttackDecorator {
public:
    FireBuff(Attack* a) : AttackDecorator(a) {}
    int GetDamage() override {
        return innerAtk->GetDamage() + 30;
    }
};

int main() {
    Attack* atk = new BaseAttack();
    atk = new FireBuff(atk);
    cout << "总攻击力:" << atk->GetDamage() << endl;
    delete atk;
    return 0;
}

装饰器模式遵循开闭原则,无需修改原有业务代码,通过对象包装的方式动态扩展对象功能,支持灵活组合多项附加能力,多用于给实体叠加持续性、可自由搭配的效果。

外观模式 Facade

外观模式为一堆复杂底层子系统提供简洁高层接口,隔离外部与多个底层模块的耦合,游戏开发常用:游戏启动流程管理器、战斗系统总入口、资源加载门面,外部只调用简单接口,不用管理资源、渲染、物理、音效多个底层模块协同。

cpp 复制代码
#include <iostream>
using namespace std;

class RenderSystem {
public:
    void Init() { cout << "渲染系统初始化" << endl; }
};
class PhysicsSystem {
public:
    void Init() { cout << "物理系统初始化" << endl; }
};

// 游戏启动门面
class GameStartupFacade {
    RenderSystem render;
    PhysicsSystem physics;
public:
    void StartGame() {
        render.Init();
        physics.Init();
        cout << "游戏启动完成" << endl;
    }
};

int main() {
    GameStartupFacade game;
    game.StartGame();
    return 0;
}

享元模式 Flyweight

享元模式区分对象内部不变共享数据与外部动态数据,复用大量细粒度对象减少内存占用,游戏典型场景:地图瓦片、大量相同粒子模板、字体字符、重复 UI 图标、大量同类型怪物共享静态配置,只在外部传入坐标、血量等动态数据。

cpp 复制代码
#include <iostream>
#include <unordered_map>
using namespace std;

// 瓦片(内部共享:瓦片资源ID)
class Tile {
    int resId;
public:
    Tile(int id) : resId(id) {}
    void Draw(int x, int y) {
        cout << "瓦片资源" << resId << " 绘制坐标(" << x << "," << y << ")\n";
    }
};

class TileFactory {
    unordered_map<int, Tile*> pool;
public:
    Tile* GetTile(int resId) {
        if (!pool.count(resId)) {
            pool[resId] = new Tile(resId);
        }
        return pool[resId];
    }
};

int main() {
    TileFactory factory;
    Tile* tile1 = factory.GetTile(1001);
    Tile* tile2 = factory.GetTile(1001);
    cout << boolalpha << (tile1 == tile2) << endl;
    tile1->Draw(0, 0);
    tile2->Draw(2, 3);
    return 0;
}

代理模式 Proxy

代理对象控制对真实对象的访问,可以增加延迟加载、权限校验、缓存逻辑,和装饰器侧重点不同,代理重在访问管控,游戏场景:资源异步懒加载代理、远程角色数据代理、技能冷却拦截代理、贴图延迟加载。

cpp 复制代码
#include <iostream>
using namespace std;

class ITexture {
public:
    virtual ~ITexture() = default;
    virtual void Render() = 0;
};
class RealTexture : public ITexture {
public:
    void Render() override { cout << "渲染真实贴图资源" << endl; }
};

// 贴图代理,延迟加载
class TextureProxy : public ITexture {
    RealTexture* tex = nullptr;
public:
    void Render() override {
        cout << "代理:检查资源,需要时加载贴图" << endl;
        if (!tex) tex = new RealTexture();
        tex->Render();
    }
};

int main() {
    ITexture* tex = new TextureProxy();
    tex->Render();
    delete tex;
    return 0;
}
相关推荐
卡牌RWA研究院2 小时前
Relique:一张精品卡牌如何从收藏品变成可交易的链上资产?
设计模式·金融·区块链·创业创新
剧中有戏1 天前
抽象工厂模式从入门到精通:一个多云存储案例的完整剖析(修订版)
设计模式
MC皮蛋侠客1 天前
Redis 系列(八):缓存设计模式与一致性——从 Cache Aside 到防雪崩
redis·缓存·设计模式
莫得感情 o1 天前
设计模式 18 · 状态模式
设计模式·状态模式
莫得感情 o1 天前
设计模式 17 · 责任链模式
设计模式·责任链模式
用户938515635072 天前
从零在浏览器里跑 DeepSeek-R1:WebGPU + Transformer.js 全链路实战
前端·设计模式·typescript
AI人工智能+电脑小能手2 天前
大白话说Java设计模式-04-工厂方法模式(业务实战篇)
java·设计模式·工厂方法模式·架构设计·代码解耦
今天的砖头有点烫手啊2 天前
AI Agent 开发实战(十):Agent 设计模式(ReAct / Plan-Execute / Reflection)
人工智能·react.js·设计模式
董员外2 天前
RAG 系统进化论(十):可运营 RAG,从原型到长期运行的知识系统
人工智能·后端·设计模式