GOF 23 种设计模式详解
引言
设计模式是解决软件设计中常见问题的典型解决方案。1994年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四位作者(业界称为 "四人组" 或 GoF)在《设计模式:可复用面向对象软件的基础》一书中系统性地总结了23种设计模式,它们就像能根据需求进行调整的预制蓝图,可用于解决代码中反复出现的设计问题。
与算法不同,算法提供达成目标的明确步骤,而模式更像是蓝图:你可以看到最终结果和模式功能,但需要自己确定实现步骤。
GoF提出的23种设计模式可以从两个维度进行分类:
按目的分类
- 创建型模式:关注对象的创建过程,旨在解耦对象的创建与使用。包括:单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。
- 结构型模式:关注类和对象的组合,旨在通过合理的结构组合形成更灵活的系统。包括:适配器模式、装饰器模式、代理模式、组合模式、桥接模式、外观模式和享元模式。
- 行为型模式:聚焦对象之间的职责分配与通信机制,优化系统行为协作。包括:观察者模式、策略模式、命令模式、状态模式、模板方法模式、迭代器模式、责任链模式、访问者模式、中介者模式、备忘录模式和解释器模式。
按作用范围分类
- 类模式:处理类与子类之间的关系,通过继承实现,在编译时确定。包括:工厂方法模式、(类)适配器模式、模板方法模式和解释器模式。
- 对象模式:处理对象之间的关系,通过组合或聚合实现,具备更高的动态性和灵活性。包括其余 19 种模式。
创建型模式详解
创建型模式通过解耦对象的创建过程,降低系统对具体类的依赖,提高扩展性和灵活性。以下是五种创建型模式的深度解析:
单例模式(Singleton)
单例模式确保一个类只有一个实例,并提供全局访问点。该模式通过私有化构造函数、提供静态获取实例方法来实现,需要考虑线程安全问题。
在后端游戏开发中,游戏配置管理、全局游戏状态记录、日志系统等功能往往需要确保仅有一个实例。单例模式通过限制类的实例化数量,提供全局访问点来实现这一需求。
#include <iostream>
#include <string>
#include <unordered_map>
#include <mutex>
// 游戏配置类,使用单例模式确保全局只有一个实例
class GameConfig {
private:
// 静态实例指针,指向唯一的实例
static GameConfig* instance;
// 互斥锁,用于线程安全的实例创建
static std::mutex mutex;
// 配置映射表,存储键值对形式的配置信息
std::unordered_map<std::string, std::string> configMap;
// 私有构造函数,防止外部创建实例
GameConfig() {}
public:
// 禁用拷贝构造函数,防止实例被复制
GameConfig(const GameConfig&) = delete;
// 禁用赋值运算符,防止实例被赋值
GameConfig& operator=(const GameConfig&) = delete;
// 静态方法,获取唯一的实例(使用双重检查锁定确保线程安全)
static GameConfig* getInstance() {
// 第一次检查,避免不必要的锁开销
if (instance == nullptr) {
// 使用互斥锁确保线程安全
std::lock_guard<std::mutex> guard(mutex);
// 第二次检查,确保只有一个实例被创建
if (instance == nullptr) {
instance = new GameConfig();
}
}
return instance;
}
// 设置配置项
void setConfig(const std::string& key, const std::string& value) {
configMap[key] = value;
std::cout << "Config set: " << key << " = " << value << std::endl;
}
// 获取配置项
std::string getConfig(const std::string& key) {
auto it = configMap.find(key);
if (it != configMap.end()) {
std::cout << "Config found: " << key << " = " << it->second << std::endl;
return it->second;
} else {
std::cout << "Config not found: " << key << std::endl;
return "";
}
}
};
// 初始化静态成员变量
GameConfig* GameConfig::instance = nullptr;
std::mutex GameConfig::mutex;
// 示例主函数:演示单例模式的使用
int main() {
// 获取游戏配置实例
GameConfig* config = GameConfig::getInstance();
// 设置一些配置项
config->setConfig("resolution", "1920x1080");
config->setConfig("volume", "75");
config->setConfig("difficulty", "medium");
// 获取并显示配置项
std::cout << "\nCurrent Game Configuration:\n";
config->getConfig("resolution");
config->getConfig("volume");
config->getConfig("difficulty");
config->getConfig("fps_limit"); // 不存在的配置项
// 尝试获取另一个实例(应该是同一个)
GameConfig* anotherConfig = GameConfig::getInstance();
anotherConfig->setConfig("fps_limit", "60");
// 确认两个实例是相同的
std::cout << "\nBoth instances are the same: "
<< (config == anotherConfig ? "Yes" : "No") << std::endl;
return 0;
}
工厂方法模式(Factory Method)
工厂方法模式定义一个创建对象的接口,由子类决定实例化哪个类。该模式将对象创建与使用分离,完美符合开闭原则——新增产品类型时不影响现有代码。
类比在游戏中,当需要根据不同条件创建不同类型的游戏角色(如战士、法师、刺客等)时,工厂方法模式能有效分离对象创建逻辑与使用逻辑,使系统更易扩展。
#include <iostream>
// 游戏角色抽象基类,定义所有角色的共同接口
class GameCharacter {
public:
// 纯虚函数,用于显示角色信息
virtual void display() = 0;
// 虚析构函数,确保正确释放派生类资源
virtual ~GameCharacter() = default;
};
// 战士角色类,继承自GameCharacter
class Warrior : public GameCharacter {
public:
// 实现display方法,显示战士角色信息
void display() override {
std::cout << "Warrior created" << std::endl;
}
};
// 法师角色类,继承自GameCharacter
class Mage : public GameCharacter {
public:
// 实现display方法,显示法师角色信息
void display() override {
std::cout << "Mage created" << std::endl;
}
};
// 角色工厂抽象基类,定义创建角色的接口
class CharacterFactory {
public:
// 纯虚函数,用于创建游戏角色
virtual GameCharacter* createCharacter() = 0;
// 虚析构函数,确保正确释放派生类资源
virtual ~CharacterFactory() = default;
};
// 战士工厂类,继承自CharacterFactory,负责创建战士角色
class WarriorFactory : public CharacterFactory {
public:
// 实现createCharacter方法,创建并返回战士角色对象
GameCharacter* createCharacter() override {
return new Warrior();
}
};
// 法师工厂类,继承自CharacterFactory,负责创建法师角色
class MageFactory : public CharacterFactory {
public:
// 实现createCharacter方法,创建并返回法师角色对象
GameCharacter* createCharacter() override {
return new Mage();
}
};
// 示例主函数:演示工厂模式的使用
int main() {
// 创建战士工厂实例
CharacterFactory* warriorFactory = new WarriorFactory();
// 使用战士工厂创建战士角色
GameCharacter* warrior = warriorFactory->createCharacter();
// 显示战士角色信息
warrior->display();
// 创建法师工厂实例
CharacterFactory* mageFactory = new MageFactory();
// 使用法师工厂创建法师角色
GameCharacter* mage = mageFactory->createCharacter();
// 显示法师角色信息
mage->display();
// 释放内存,防止内存泄漏
delete warrior;
delete warriorFactory;
delete mage;
delete mageFactory;
return 0;
}
抽象工厂模式(Abstract Factory)
抽象工厂模式是工厂方法模式的升级版,可以生产一个产品族的产品。该模式提供一个接口用于创建相关或依赖对象的家族,适用于需要同时创建一整套相关组件的场景。
好比若游戏开发中涉及创建多个相关的对象家族,例如不同阵营(人类、兽人)的角色及其专属装备,抽象工厂模式可用于统一创建管理,确保同一阵营的所有组件保持一致性,同时隔离具体实现。
#include <iostream>
// 武器抽象基类,定义所有武器的共同接口
class Weapon {
public:
// 纯虚函数,用于显示武器信息
virtual void display() = 0;
// 虚析构函数,确保正确释放派生类资源
virtual ~Weapon() = default;
};
// 护甲抽象基类,定义所有护甲的共同接口
class Armor {
public:
// 纯虚函数,用于显示护甲信息
virtual void display() = 0;
// 虚析构函数,确保正确释放派生类资源
virtual ~Armor() = default;
};
// 人类武器类,继承自Weapon
class HumanWeapon : public Weapon {
public:
// 实现display方法,显示人类武器信息
void display() override {
std::cout << "Human Weapon" << std::endl;
}
};
// 人类护甲类,继承自Armor
class HumanArmor : public Armor {
public:
// 实现display方法,显示人类护甲信息
void display() override {
std::cout << "Human Armor" << std::endl;
}
};
// 兽人武器类,继承自Weapon
class OrcWeapon : public Weapon {
public:
// 实现display方法,显示兽人武器信息
void display() override {
std::cout << "Orc Weapon" << std::endl;
}
};
// 兽人护甲类,继承自Armor
class OrcArmor : public Armor {
public:
// 实现display方法,显示兽人护甲信息
void display() override {
std::cout << "Orc Armor" << std::endl;
}
};
// 抽象工厂基类,定义创建武器和护甲的接口
class AbstractFactory {
public:
// 纯虚函数,用于创建武器
virtual Weapon* createWeapon() = 0;
// 纯虚函数,用于创建护甲
virtual Armor* createArmor() = 0;
// 虚析构函数,确保正确释放派生类资源
virtual ~AbstractFactory() = default;
};
// 人类工厂类,继承自AbstractFactory,负责创建人类的武器和护甲
class HumanFactory : public AbstractFactory {
public:
// 实现createWeapon方法,创建并返回人类武器对象
Weapon* createWeapon() override {
return new HumanWeapon();
}
// 实现createArmor方法,创建并返回人类护甲对象
Armor* createArmor() override {
return new HumanArmor();
}
};
// 兽人工厂类,继承自AbstractFactory,负责创建兽人的武器和护甲
class OrcFactory : public AbstractFactory {
public:
// 实现createWeapon方法,创建并返回兽人武器对象
Weapon* createWeapon() override {
return new OrcWeapon();
}
// 实现createArmor方法,创建并返回兽人护甲对象
Armor* createArmor() override {
return new OrcArmor();
}
};
// 示例主函数:演示抽象工厂模式的使用
int main() {
// 创建人类工厂实例
AbstractFactory* humanFactory = new HumanFactory();
// 使用人类工厂创建人类武器
Weapon* humanWeapon = humanFactory->createWeapon();
// 使用人类工厂创建人类护甲
Armor* humanArmor = humanFactory->createArmor();
// 显示人类武器和护甲信息
std::cout << "Human Character Equipment:" << std::endl;
humanWeapon->display();
humanArmor->display();
// 创建兽人工厂实例
AbstractFactory* orcFactory = new OrcFactory();
// 使用兽人工厂创建兽人武器
Weapon* orcWeapon = orcFactory->createWeapon();
// 使用兽人工厂创建兽人护甲
Armor* orcArmor = orcFactory->createArmor();
// 显示兽人武器和护甲信息
std::cout << "\nOrc Character Equipment:" << std::endl;
orcWeapon->display();
orcArmor->display();
// 释放内存,防止内存泄漏
delete humanWeapon;
delete humanArmor;
delete humanFactory;
delete orcWeapon;
delete orcArmor;
delete orcFactory;
return 0;
}
建造者模式(Builder)
建造者模式将复杂对象的构建与其表示分离,允许通过分步构建创建不同表现形式的对象。如同生产流水线代替手工组装,可以分步骤构建对象,控制构建过程,生成不同表现形式。该模式适用于构建复杂对象。
在构建包含多个组件的复杂游戏对象,如大型游戏场景(包含地形、建筑、NPC 等多种元素)时,建造者模式可将构建过程与对象表示分离,使构建步骤更清晰可控,同时支持同一构建过程创建不同的对象。
#include <iostream>
#include <string>
// 游戏场景类,表示最终要构建的对象
class GameScene {
private:
std::string terrain; // 场景地形
std::string buildings; // 场景建筑
std::string npcs; // 场景NPC
public:
// 设置地形
void setTerrain(const std::string& t) { terrain = t; }
// 设置建筑
void setBuildings(const std::string& b) { buildings = b; }
// 设置NPC
void setNPCs(const std::string& n) { npcs = n; }
// 显示场景信息
void display() {
std::cout << "Terrain: " << terrain << std::endl;
std::cout << "Buildings: " << buildings << std::endl;
std::cout << "NPCs: " << npcs << std::endl;
}
};
// 场景建造者抽象基类,定义构建场景各部分的接口
class SceneBuilder {
public:
// 纯虚函数,构建地形
virtual void buildTerrain() = 0;
// 纯虚函数,构建建筑
virtual void buildBuildings() = 0;
// 纯虚函数,构建NPC
virtual void buildNPCs() = 0;
// 纯虚函数,获取构建好的场景
virtual GameScene* getScene() = 0;
// 虚析构函数,确保正确释放派生类资源
virtual ~SceneBuilder() = default;
};
// 城市场景建造者类,继承自SceneBuilder,负责构建城市场景
class CitySceneBuilder : public SceneBuilder {
private:
GameScene* scene; // 指向要构建的场景对象
public:
// 构造函数,初始化场景对象
CitySceneBuilder() {
scene = new GameScene();
}
// 实现buildTerrain方法,构建城市地形
void buildTerrain() override {
scene->setTerrain("Paved Roads");
}
// 实现buildBuildings方法,构建城市建筑
void buildBuildings() override {
scene->setBuildings("Tall Buildings");
}
// 实现buildNPCs方法,构建城市NPC
void buildNPCs() override {
scene->setNPCs("Merchants and Guards");
}
// 实现getScene方法,返回构建好的场景
GameScene* getScene() override {
return scene;
}
};
// 导演类,负责指挥建造者按照一定的步骤构建场景
class Director {
private:
SceneBuilder* builder; // 指向场景建造者的指针
public:
// 构造函数,初始化建造者
Director(SceneBuilder* b) : builder(b) {}
// 构造场景,指挥建造者按照顺序构建各部分
void constructScene() {
builder->buildTerrain();
builder->buildBuildings();
builder->buildNPCs();
}
// 获取构建好的场景
GameScene* getScene() {
return builder->getScene();
}
};
// 示例主函数:演示建造者模式的使用
int main() {
// 创建城市场景建造者
SceneBuilder* cityBuilder = new CitySceneBuilder();
// 创建导演,并将建造者传递给导演
Director* director = new Director(cityBuilder);
// 指挥导演构建场景
director->constructScene();
// 获取构建好的场景
GameScene* cityScene = director->getScene();
// 显示场景信息
std::cout << "City Scene Information:" << std::endl;
cityScene->display();
// 释放资源
delete cityScene;
delete director;
delete cityBuilder;
return 0;
}
原型模式(Prototype)
原型模式通过复制现有对象来创建新对象,而非通过构造函数实例化。如同印刷术代替手工抄写,适用于对象初始化成本高时的复制操作。
也因此当游戏需要快速创建大量相似对象,如生成多个相同类型的怪物或场景元素时,原型模式通过复制现有对象来避免重复创建的开销,快速创建新实例的同时也保留了基本对象的配置信息。
#include <iostream>
// 怪物抽象基类,定义了所有怪物的共同接口
class Monster {
public:
// 纯虚函数,用于克隆怪物对象
virtual Monster* clone() = 0;
// 纯虚函数,用于显示怪物信息
virtual void display() = 0;
// 虚析构函数,确保正确释放派生类资源
virtual ~Monster() = default;
};
// 地精怪物类,继承自Monster
class Goblin : public Monster {
public:
// 实现clone方法,使用拷贝构造函数创建新的地精对象
Monster* clone() override {
return new Goblin(*this);
}
// 实现display方法,显示地精信息
void display() override {
std::cout << "Goblin" << std::endl;
}
};
// 示例主函数:演示原型模式的使用
int main() {
// 创建一个原始地精对象
Goblin* originalGoblin = new Goblin();
// 显示原始地精信息
std::cout << "Original Monster: ";
originalGoblin->display();
// 使用原型模式克隆地精对象
Monster* clonedGoblin = originalGoblin->clone();
// 显示克隆地精信息
std::cout << "Cloned Monster: ";
clonedGoblin->display();
// 释放内存,防止内存泄漏
delete originalGoblin;
delete clonedGoblin;
return 0;
}
结构型模式
结构型模式关注类和对象的组合关系,旨在通过合理的结构设计实现更灵活的系统架构。以下是七种结构型模式的解析:
代理模式(Proxy)
代理模式为其他对象提供一个代理以控制对原对象的访问。它如同 "中间人",可以在不修改原对象的情况下,添加额外功能如延迟加载、权限控制和远程调用等。
在游戏后端,代理模式可用于实现玩家与游戏服务器之间的权限控制、远程服务调用的封装,或对敏感操作的日志记录。
#include <iostream>
#include <memory> // 引入智能指针头文件
// 抽象接口:定义游戏服务的通用方法
class GameService {
public:
virtual void playGame() = 0; // 纯虚函数,子类必须实现
virtual ~GameService() = default; // 虚析构函数,确保正确释放子类资源
};
// 真实服务:实现具体的游戏功能
class RealGameService : public GameService {
public:
void playGame() override {
std::cout << "Playing game..." << std::endl;
}
};
// 代理服务:控制对真实服务的访问,并提供额外功能
class GameServiceProxy : public GameService {
private:
// 使用std::unique_ptr管理RealGameService对象的生命周期
std::unique_ptr<RealGameService> realService;
public:
// 构造函数:初始化智能指针
GameServiceProxy() : realService(std::make_unique<RealGameService>()) {}
void playGame() override {
// 权限验证逻辑(代理的额外功能)
std::cout << "Checking player permission..." << std::endl;
// 调用真实服务的方法
realService->playGame();
}
// 无需显式析构函数,std::unique_ptr会自动释放资源
};
// 示例主函数:演示代理模式的使用
int main() {
// 创建代理对象
std::unique_ptr<GameService> gameProxy = std::make_unique<GameServiceProxy>();
// 调用代理的方法,实际上会触发权限验证和真实游戏逻辑
gameProxy->playGame();
return 0;
}
适配器模式(Adapter)
适配器模式将一个类的接口转换成客户端期望的另一个接口。如同万能充电器或翻译官,适用于整合不兼容的接口和旧系统集成。实现方式包括类适配器(通过继承)和对象适配器(通过组合)两种。
当游戏需要整合不同的第三方库或旧系统时,适配器模式能将不兼容的接口转换为统一接口。比如,将旧版地图加载接口适配为新版游戏引擎所需的格式。
#include <iostream>
#include <string>
// 被适配者(Adaptee):旧系统的地图加载器,提供不兼容的旧接口
class OldMapLoader {
public:
void loadOldMap(const std::string& mapFile) { // 旧接口:加载旧格式地图
std::cout << "Loading old map: " << mapFile << std::endl;
}
};
// 目标接口(Target):新系统定义的标准接口,客户端依赖此接口
class NewMapLoaderInterface {
public:
virtual void loadMap(const std::string& mapFilePath) = 0; // 新接口:加载地图的抽象方法
virtual ~NewMapLoaderInterface() = default; // 虚析构函数:确保正确释放派生类资源
};
// 适配器(Adapter):连接新旧接口的桥梁,实现新接口并包装旧对象
class MapLoaderAdapter : public NewMapLoaderInterface {
private:
OldMapLoader oldLoader; // 组合旧系统对象(适配器模式的常见实现方式)
public:
void loadMap(const std::string& mapFilePath) override { // 实现新接口,委托给旧接口
oldLoader.loadOldMap(mapFilePath); // 将新接口调用转换为旧接口调用
}
};
// 客户端代码:依赖新接口,无需关心具体实现
void loadMapUsingNewInterface(NewMapLoaderInterface* loader, const std::string& mapPath) {
loader->loadMap(mapPath); // 客户端通过新接口调用地图加载功能
}
// 示例主函数:演示适配器模式的使用
int main() {
// 创建适配器实例(同时是新接口的实现和旧系统的包装)
MapLoaderAdapter adapter;
// 客户端代码:使用新接口加载地图(实际调用的是旧系统的功能)
loadMapUsingNewInterface(&adapter, "level1.map");
// 也可以直接通过适配器调用
// adapter.loadMap("level1.map");
return 0;
}
桥接模式(Bridge)
桥接模式的核心是将抽象部分与实现部分分离。如同约定优于配置,避免多层继承,提高系统可扩展性。适用于多维度变化的系统,如不同形状和颜色的图形。
在游戏开发中,若游戏特效(如火焰、冰霜特效)的类型和渲染平台(PC、移动端)存在独立变化维度,桥接模式可将特效类型与渲染实现分离。
#include <iostream>
// 实现部分(Implementor):定义渲染平台的抽象接口
class RenderPlatform {
public:
virtual void render() = 0; // 渲染方法,由具体平台实现
virtual ~RenderPlatform() = default; // 虚析构函数确保正确释放资源
};
// 具体实现部分(Concrete Implementor):PC平台的渲染实现
class PCPlatform : public RenderPlatform {
public:
void render() override {
std::cout << "Rendering on PC" << std::endl;
}
};
// 具体实现部分(Concrete Implementor):移动平台的渲染实现
class MobilePlatform : public RenderPlatform {
public:
void render() override {
std::cout << "Rendering on Mobile" << std::endl;
}
};
// 抽象部分(Abstraction):定义游戏特效的抽象接口
class GameEffect {
protected:
RenderPlatform* platform; // 持有一个渲染平台的指针,实现与平台的桥接
public:
GameEffect(RenderPlatform* p) : platform(p) {} // 通过构造函数注入具体平台
virtual void display() = 0; // 显示特效的抽象方法
virtual ~GameEffect() = default; // 虚析构函数
};
// 扩展抽象部分(Refined Abstraction):火焰特效的具体实现
class FireEffect : public GameEffect {
public:
FireEffect(RenderPlatform* p) : GameEffect(p) {} // 调用基类构造函数初始化平台
void display() override {
std::cout << "Fire Effect - "; // 特效特有逻辑
platform->render(); // 委托给具体平台实现渲染
}
};
// 扩展抽象部分(Refined Abstraction):冰冻特效的具体实现
class IceEffect : public GameEffect {
public:
IceEffect(RenderPlatform* p) : GameEffect(p) {} // 调用基类构造函数初始化平台
void display() override {
std::cout << "Ice Effect - "; // 特效特有逻辑
platform->render(); // 委托给具体平台实现渲染
}
};
// 示例主函数:演示桥接模式的使用
int main() {
// 创建具体的渲染平台实现
PCPlatform pcPlatform;
MobilePlatform mobilePlatform;
// 创建特效并关联到不同的平台
// 火焰特效在PC平台上显示
FireEffect fireOnPC(&pcPlatform);
// 火焰特效在移动平台上显示
FireEffect fireOnMobile(&mobilePlatform);
// 冰冻特效在PC平台上显示
IceEffect iceOnPC(&pcPlatform);
// 调用特效显示,实际渲染由关联的平台完成
std::cout << "=== 特效显示 ===" << std::endl;
fireOnPC.display(); // 输出: Fire Effect - Rendering on PC
fireOnMobile.display(); // 输出: Fire Effect - Rendering on Mobile
iceOnPC.display(); // 输出: Ice Effect - Rendering on PC
return 0;
}
装饰器模式(Decorator)
装饰器模式通过动态地为对象添加额外的职责,实现了功能的灵活扩展。如同给煎饼加鸡蛋、加香肠。相比继承,装饰器模式比继承更灵活,可以动态添加或撤销功能,且能避免类继承导致的层级膨胀。
在游戏中为角色动态添加属性或技能时,装饰器模式可在不修改原有角色类的基础上实现功能扩展。例如,为角色添加临时增益效果。
#include <iostream>
// 组件接口(Component):定义所有具体组件和装饰器的公共接口
class GameCharacter {
public:
virtual void display() = 0; // 显示角色信息的抽象方法
virtual ~GameCharacter() = default; // 虚析构函数确保正确释放资源
};
// 具体组件(Concrete Component):基础角色实现
class BasicCharacter : public GameCharacter {
public:
void display() override {
std::cout << "Basic Character"; // 基础角色的默认显示
}
};
// 装饰器基类(Decorator):维护对组件对象的引用,并定义与组件接口一致的接口
class CharacterDecorator : public GameCharacter {
protected:
GameCharacter* character; // 持有一个组件对象的指针,实现装饰器与组件的组合
public:
CharacterDecorator(GameCharacter* c) : character(c) {} // 通过构造函数注入被装饰的组件
void display() override {
character->display(); // 默认转发调用给被装饰的组件
}
};
// 具体装饰器(Concrete Decorator):力量增益装饰器
class StrengthBuffDecorator : public CharacterDecorator {
public:
StrengthBuffDecorator(GameCharacter* c) : CharacterDecorator(c) {} // 调用基类构造函数初始化组件
void display() override {
CharacterDecorator::display(); // 先调用被装饰组件的display()方法
std::cout << " with Strength Buff"; // 再添加力量增益的额外描述
}
};
// 具体装饰器(Concrete Decorator):敏捷增益装饰器
class AgilityBuffDecorator : public CharacterDecorator {
public:
AgilityBuffDecorator(GameCharacter* c) : CharacterDecorator(c) {} // 调用基类构造函数初始化组件
void display() override {
CharacterDecorator::display(); // 先调用被装饰组件的display()方法
std::cout << " with Agility Buff"; // 再添加敏捷增益的额外描述
}
};
// 示例主函数:演示装饰器模式的使用
int main() {
// 创建基础角色
BasicCharacter basicChar;
std::cout << "1. 基础角色:";
basicChar.display(); // 输出: Basic Character
std::cout << std::endl;
// 用力量增益装饰基础角色
StrengthBuffDecorator strengthChar(&basicChar);
std::cout << "2. 力量增益后的角色:";
strengthChar.display(); // 输出: Basic Character with Strength Buff
std::cout << std::endl;
// 用敏捷增益装饰基础角色
AgilityBuffDecorator agilityChar(&basicChar);
std::cout << "3. 敏捷增益后的角色:";
agilityChar.display(); // 输出: Basic Character with Agility Buff
std::cout << std::endl;
// 用力量增益和敏捷增益依次装饰基础角色(嵌套装饰)
AgilityBuffDecorator strengthAndAgilityChar(&strengthChar);
std::cout << "4. 力量和敏捷双重增益后的角色:";
strengthAndAgilityChar.display(); // 输出: Basic Character with Strength Buff with Agility Buff
std::cout << std::endl;
return 0;
}
外观模式(Facade)
外观模式为子系统中的一组复杂接口提供一个统一的高层接口。如同打开一扇门通向全世界,能降低系统复杂度,提供简单入口。适用于简化复杂系统的调用,如API网关等场景。
游戏中的复杂系统,如角色养成系统(包含升级、装备、技能等子系统),可通过外观模式提供统一的简单接口,方便外部调用。
#include <iostream>
#include <string>
// 子系统1:角色升级系统,负责处理角色等级提升相关逻辑
class LevelingSystem {
public:
void levelUp() {
std::cout << "Leveling up character..." << std::endl;
}
};
// 子系统2:装备系统,负责处理角色装备相关逻辑
class EquipmentSystem {
public:
void equipItem(const std::string& item) {
std::cout << "Equipping " << item << std::endl;
}
};
// 子系统3:技能系统,负责处理角色技能学习相关逻辑
class SkillSystem {
public:
void learnSkill(const std::string& skill) {
std::cout << "Learning " << skill << std::endl;
}
};
// 外观类(Facade):提供统一的接口,封装子系统的复杂交互
class CharacterGrowthFacade {
private:
// 持有各个子系统的实例
LevelingSystem leveling;
EquipmentSystem equipment;
SkillSystem skill;
public:
// 外观方法:简化的接口,封装了多个子系统的调用流程
void growCharacter(const std::string& item, const std::string& skill) {
std::cout << "\n=== Starting Character Growth Process ===" << std::endl;
// 按照预设顺序调用子系统方法
leveling.levelUp(); // 先升级
equipment.equipItem(item); // 再装备新物品
skill.learnSkill(skill); // 最后学习新技能
std::cout << "=== Character Growth Process Complete ===\n" << std::endl;
}
};
// 示例主函数:演示外观模式的使用
int main() {
// 创建外观类实例
CharacterGrowthFacade growthFacade;
// 客户端只需调用外观类的简化接口,无需了解子系统细节
std::cout << "Player requests character growth:" << std::endl;
growthFacade.growCharacter("Dragon Sword", "Fireball");
// 输出结果:
// Player requests character growth:
//
// === Starting Character Growth Process ===
// Leveling up character...
// Equipping Dragon Sword
// Learning Fireball
// === Character Growth Process Complete ===
return 0;
}
享元模式(Flyweight)
享元模式通过共享技术高效地支持大量细粒度对象。如同优化资源配置减少重复浪费,区分内部状态(共享)和外部状态(不共享)是关键。文本编辑器中的字符对象和棋牌游戏棋子是典型应用
在大型多人在线游戏中,大量相似的游戏对象(如场景中的树木、石头)可使用享元模式共享对象实例,减少内存占用。
#include <iostream>
#include <string>
#include <unordered_map>
// 享元接口(Flyweight):定义共享对象的操作
class GameObject {
public:
virtual void display() = 0; // 显示游戏对象的抽象方法
virtual ~GameObject() = default; // 虚析构函数确保正确释放资源
};
// 具体享元(Concrete Flyweight):实现享元接口,包含内部状态
class Tree : public GameObject {
private:
std::string texture; // 内部状态:纹理,在创建时确定且不可变
public:
Tree(const std::string& t) : texture(t) {} // 通过构造函数初始化内部状态
void display() override {
std::cout << "Tree with texture: " << texture << std::endl;
}
};
// 享元工厂(Flyweight Factory):创建和管理享元对象
class GameObjectFactory {
private:
// 享元池:存储已创建的享元对象,键为纹理名称
std::unordered_map<std::string, GameObject*> objectMap;
public:
// 获取享元对象的方法:如果池中存在则返回,否则创建新对象并添加到池中
GameObject* getGameObject(const std::string& key) {
// 检查池中是否已存在该纹理的树
if (objectMap.find(key) == objectMap.end()) {
// 若不存在,创建新的树对象并添加到池中
std::cout << "Creating new tree with texture: " << key << std::endl;
objectMap[key] = new Tree(key);
} else {
// 若存在,提示重用现有对象
std::cout << "Reusing existing tree with texture: " << key << std::endl;
}
return objectMap[key]; // 返回享元对象
}
// 析构函数:释放享元池中所有对象的内存,防止内存泄漏
~GameObjectFactory() {
for (auto& pair : objectMap) {
delete pair.second;
}
objectMap.clear();
}
};
// 示例主函数:演示享元模式的使用
int main() {
// 创建享元工厂
GameObjectFactory factory;
// 尝试获取相同纹理的树多次
std::cout << "=== Requesting trees with the same texture ===" << std::endl;
GameObject* oakTree1 = factory.getGameObject("Oak"); // 创建新对象
GameObject* oakTree2 = factory.getGameObject("Oak"); // 重用现有对象
GameObject* oakTree3 = factory.getGameObject("Oak"); // 重用现有对象
// 显示对象(验证内部状态相同)
std::cout << "\n=== Displaying trees ===" << std::endl;
oakTree1->display();
oakTree2->display();
oakTree3->display();
// 尝试获取不同纹理的树
std::cout << "\n=== Requesting trees with different textures ===" << std::endl;
GameObject* pineTree = factory.getGameObject("Pine"); // 创建新对象
GameObject* mapleTree = factory.getGameObject("Maple"); // 创建新对象
// 显示对象
std::cout << "\n=== Displaying additional trees ===" << std::endl;
pineTree->display();
mapleTree->display();
// 输出结果示例:
// === Requesting trees with the same texture ===
// Creating new tree with texture: Oak
// Reusing existing tree with texture: Oak
// Reusing existing tree with texture: Oak
//
// === Displaying trees ===
// Tree with texture: Oak
// Tree with texture: Oak
// Tree with texture: Oak
//
// === Requesting trees with different textures ===
// Creating new tree with texture: Pine
// Creating new tree with texture: Maple
//
// === Displaying additional trees ===
// Tree with texture: Pine
// Tree with texture: Maple
return 0;
}
组合模式(Composite)
组合模式将对象组合成树形结构以表示"部分-整体"关系。如同文件系统中 "文件夹"(组合对象)可以包含 "文件"(叶子对象),使得客户端可以统一处理单个对象和组合对象。文件系统、UI组件树和组织架构常使用此模式。
游戏中的场景层次结构,如主城包含多个区域,区域又包含多个建筑,适合使用组合模式进行管理,实现统一的操作接口。
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
// 组件接口(Component):定义组合中对象的通用接口
class GameSceneComponent {
public:
virtual void display() = 0; // 显示组件的抽象方法
virtual void add(GameSceneComponent* component) {} // 添加子组件的默认实现(叶节点不支持)
virtual void remove(GameSceneComponent* component) {} // 移除子组件的默认实现(叶节点不支持)
virtual ~GameSceneComponent() = default; // 虚析构函数确保正确释放资源
};
// 叶节点(Leaf):组合中的基本元素,没有子节点
class Building : public GameSceneComponent {
private:
std::string name; // 建筑物名称
public:
Building(const std::string& n) : name(n) {} // 构造函数初始化名称
void display() override {
std::cout << "Building: " << name << std::endl; // 显示建筑物信息
}
// 叶节点不支持添加/移除子组件,使用基类的空实现
};
// 组合节点(Composite):包含叶节点或其他组合节点的容器
class Area : public GameSceneComponent {
private:
std::vector<GameSceneComponent*> components; // 子组件集合
std::string name; // 区域名称
public:
Area(const std::string& n) : name(n) {} // 构造函数初始化名称
// 显示区域及其所有子组件的信息(递归实现)
void display() override {
std::cout << "Area: " << name << std::endl; // 显示区域名称
for (auto component : components) {
component->display(); // 递归显示每个子组件
}
}
// 添加子组件到区域中
void add(GameSceneComponent* component) override {
components.push_back(component);
}
// 从区域中移除子组件
void remove(GameSceneComponent* component) override {
auto it = std::find(components.begin(), components.end(), component);
if (it != components.end()) {
components.erase(it);
}
}
// 析构函数:释放所有子组件的内存,防止内存泄漏
~Area() override {
for (auto component : components) {
delete component;
}
components.clear();
}
};
// 示例主函数:演示组合模式的使用
int main() {
// 创建根区域
Area* gameWorld = new Area("Game World");
// 创建一级区域
Area* forestArea = new Area("Forest");
Area* cityArea = new Area("City");
// 创建建筑物(叶节点)
Building* tree1 = new Building("Oak Tree");
Building* tree2 = new Building("Pine Tree");
Building* house = new Building("Town House");
Building* castle = new Building("Castle");
// 构建组合结构:将建筑物添加到对应区域
forestArea->add(tree1);
forestArea->add(tree2);
cityArea->add(house);
cityArea->add(castle);
// 将一级区域添加到根区域
gameWorld->add(forestArea);
gameWorld->add(cityArea);
// 显示整个游戏世界(递归调用)
std::cout << "=== Displaying Game World ===" << std::endl;
gameWorld->display();
// 输出结果:
// === Displaying Game World ===
// Area: Game World
// Area: Forest
// Building: Oak Tree
// Building: Pine Tree
// Area: City
// Building: Town House
// Building: Castle
// 释放资源(析构函数会递归释放所有子组件)
delete gameWorld;
return 0;
}
行为型模式
行为型模式特别关注对象之间的通信。通过优化对象间的通信方式,提升系统的灵活性与可维护性。这类模式更关注 “对象如何协作”,而非 “对象如何创建或组合”,以下是十一种行为型模式的解析:
策略模式(Strategy)
策略模式定义一系列算法使它们可以互相替换。如同条条大道通罗马具体哪条你来定,适用于多种算法或策略的场景,如排序算法和支付方式选择。该模式能避免多重条件语句,便于算法扩展。
游戏中角色的攻击策略(如近战攻击、远程攻击、魔法攻击)、AI 决策逻辑(防守 / 进攻 / 逃跑)等场景可通过策略模式实现,方便动态切换攻击方式、新增策略时无需修改角色类。
#include <iostream>
// 策略接口(Strategy):定义所有具体策略的公共接口
class AttackStrategy {
public:
virtual void attack() = 0; // 攻击行为的抽象方法
virtual ~AttackStrategy() = default; // 虚析构函数确保正确释放资源
};
// 具体策略(Concrete Strategy):近战攻击策略
class MeleeAttack : public AttackStrategy {
public:
void attack() override {
std::cout << "Performing melee attack" << std::endl; // 实现近战攻击逻辑
}
};
// 具体策略(Concrete Strategy):远程攻击策略
class RangedAttack : public AttackStrategy {
public:
void attack() override {
std::cout << "Performing ranged attack" << std::endl; // 实现远程攻击逻辑
}
};
// 上下文(Context):持有策略对象并使用其执行操作
class GameCharacter {
private:
AttackStrategy* strategy; // 持有一个策略对象的指针
public:
// 构造函数:通过依赖注入初始化策略
GameCharacter(AttackStrategy* s) : strategy(s) {}
// 动态切换策略的方法
void setStrategy(AttackStrategy* s) {
strategy = s;
}
// 执行攻击行为的方法,委托给当前策略
void attack() {
strategy->attack();
}
// 析构函数:不负责释放策略对象(避免与客户端管理冲突)
// 注意:在实际应用中,可根据所有权语义使用智能指针管理策略
~GameCharacter() = default;
};
// 示例主函数:演示策略模式的使用
int main() {
// 创建具体策略对象
MeleeAttack meleeStrategy;
RangedAttack rangedStrategy;
// 创建游戏角色,初始使用近战策略
GameCharacter warrior(&meleeStrategy);
// 使用近战策略攻击
std::cout << "Warrior using melee strategy:" << std::endl;
warrior.attack(); // 输出: Performing melee attack
// 动态切换到远程策略
warrior.setStrategy(&rangedStrategy);
// 使用远程策略攻击
std::cout << "\nWarrior using ranged strategy:" << std::endl;
warrior.attack(); // 输出: Performing ranged attack
// 可以创建新角色并使用不同策略
GameCharacter archer(&rangedStrategy);
std::cout << "\nArcher using ranged strategy:" << std::endl;
archer.attack(); // 输出: Performing ranged attack
// 策略可以被多个角色共享
GameCharacter rogue(&meleeStrategy);
std::cout << "\nRogue using melee strategy:" << std::endl;
rogue.attack(); // 输出: Performing melee attack
// 策略还可以根据条件动态切换
bool enemyIsFar = true;
warrior.setStrategy(enemyIsFar ? &rangedStrategy : &meleeStrategy);
std::cout << "\nWarrior using strategy based on enemy distance:" << std::endl;
warrior.attack(); // 输出: Performing ranged attack
return 0;
}
模板方法模式(Template Method)
模板方法模式在抽象类中定义算法的骨架,将某些步骤延迟到子类中实现。如同流程全部标准化需要微调请覆盖,适用于框架中的钩子方法和业务流程标准化。实现方式是抽象类定义模板方法,具体子类实现特定步骤。
在游戏任务系统中,不同类型的任务(主线任务、支线任务)都包含接受任务、执行任务、完成任务等步骤,但具体实现不同,适合使用模板方法模式。
#include <iostream>
// 抽象类(Abstract Class):定义模板方法和基本操作
class GameTask {
public:
// 模板方法(Template Method):定义算法骨架,固定步骤顺序
void executeTask() {
acceptTask(); // 接受任务(抽象步骤,由子类实现)
performTask(); // 执行任务(抽象步骤,由子类实现)
completeTask(); // 完成任务(抽象步骤,由子类实现)
}
protected:
// 基本操作(Primitive Operations):抽象方法,由子类实现具体逻辑
virtual void acceptTask() = 0; // 接受任务的具体方式
virtual void performTask() = 0; // 执行任务的具体方式
virtual void completeTask() = 0; // 完成任务的具体方式
// 虚析构函数:确保正确释放子类资源
virtual ~GameTask() = default;
};
// 具体类(Concrete Class):实现抽象类中的抽象方法,完成特定任务
class MainQuest : public GameTask {
protected:
void acceptTask() override {
std::cout << "Accepting main quest from king's advisor" << std::endl;
}
void performTask() override {
std::cout << "Fighting dragon to save the kingdom" << std::endl;
}
void completeTask() override {
std::cout << "Received royal medal, kingdom reputation +1000" << std::endl;
}
};
// 具体类(Concrete Class):实现抽象类中的抽象方法,完成另一种特定任务
class SideQuest : public GameTask {
protected:
void acceptTask() override {
std::cout << "Accepting side quest from village elder" << std::endl;
}
void performTask() override {
std::cout << "Clearing wolves from village pasture" << std::endl;
}
void completeTask() override {
std::cout << "Received 50 gold coins, village favor +50" << std::endl;
}
};
// 示例主函数:演示模板方法模式的使用
int main() {
// 创建具体任务对象
MainQuest mainQuest;
SideQuest sideQuest;
// 执行主线任务(自动按照模板方法的步骤顺序执行)
std::cout << "=== Executing Main Quest ===" << std::endl;
mainQuest.executeTask(); // 调用模板方法,执行固定步骤
// 输出结果:
// Accepting main quest from king's advisor
// Fighting dragon to save the kingdom
// Received royal medal, kingdom reputation +1000
// 执行支线任务(自动按照模板方法的步骤顺序执行)
std::cout << "\n=== Executing Side Quest ===" << std::endl;
sideQuest.executeTask(); // 调用模板方法,执行固定步骤
// 输出结果:
// Accepting side quest from village elder
// Clearing wolves from village pasture
// Received 50 gold coins, village favor +50
return 0;
}
观察者模式(Observer)
观察者模式定义对象间的一对多依赖关系。如同到点就通知我,适用于事件驱动系统和GUI框架中的事件监听。实现方式是主题维护观察者列表,提供注册、注销方法,状态变化时通知所有观察者。
在后端游戏开发中,观察者模式常用于实现游戏内的事件通知功能。比如,当游戏角色的血量发生变化时,需要通知相关的 UI 模块进行血量条更新,或者当游戏内的活动时间改变时,通知所有订阅该活动的玩家。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm> // 用于std::find
// 观察者接口(Observer):定义更新方法,供被观察者通知时调用
class Observer {
public:
// 纯虚函数:接收被观察者发送的消息并更新自身状态
virtual void update(const std::string& message) = 0;
// 虚析构函数:确保子类析构函数能被正确调用
virtual ~Observer() = default;
};
// 具体观察者(Concrete Observer):游戏UI界面,接收角色状态变化并更新显示
class GameUI : public Observer {
public:
// 实现更新方法:打印UI收到的消息
void update(const std::string& message) override {
std::cout << "Game UI updated: " << message << std::endl;
}
};
// 具体观察者(Concrete Observer):新增角色日志系统,记录角色状态变化
class GameLog : public Observer {
public:
void update(const std::string& message) override {
std::cout << "Game Log recorded: " << message << std::endl;
}
};
// 被观察者(Subject):游戏角色,状态变化时通知所有观察者
class GameCharacter {
private:
int health; // 角色生命值(被观察的状态)
std::vector<Observer*> observers; // 存储所有观察者的列表
public:
// 构造函数:初始化角色生命值
GameCharacter(int h) : health(h) {}
// 注册观察者:将观察者添加到列表中
void attach(Observer* observer) {
observers.push_back(observer);
}
// 移除观察者:将观察者从列表中删除
void detach(Observer* observer) {
auto it = std::find(observers.begin(), observers.end(), observer);
if (it != observers.end()) {
observers.erase(it);
}
}
// 通知所有观察者:当状态变化时,调用所有观察者的update方法
void notify(const std::string& message) {
for (Observer* observer : observers) {
observer->update(message);
}
}
// 角色受伤害的方法:修改生命值并通知观察者
void takeDamage(int damage) {
health -= damage;
// 构造通知消息
std::string message = "Character took damage, current health: " + std::to_string(health);
// 通知所有观察者状态变化
notify(message);
}
};
// 示例主函数:演示观察者模式的使用
int main() {
// 创建被观察者:一个生命值为100的游戏角色
GameCharacter character(100);
// 创建观察者:游戏UI和日志系统
GameUI ui;
GameLog log;
// 注册观察者:让角色通知UI和日志系统
character.attach(&ui);
character.attach(&log);
// 触发角色状态变化:角色受到伤害
std::cout << "=== Character takes 30 damage ===" << std::endl;
character.takeDamage(30); // 生命值变为70,通知所有观察者
// 输出结果:
// Game UI updated: Character took damage, current health: 70
// Game Log recorded: Character took damage, current health: 70
// 再次触发状态变化
std::cout << "\n=== Character takes 20 damage ===" << std::endl;
character.takeDamage(20); // 生命值变为50,通知所有观察者
// 输出结果:
// Game UI updated: Character took damage, current health: 50
// Game Log recorded: Character took damage, current health: 50
// 移除一个观察者:让角色不再通知日志系统
character.detach(&log);
std::cout << "\n=== After removing GameLog observer ===" << std::endl;
std::cout << "=== Character takes 10 damage ===" << std::endl;
character.takeDamage(10); // 生命值变为40,仅通知UI
// 输出结果:
// Game UI updated: Character took damage, current health: 40
return 0;
}
迭代器模式(Iterator)
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素。如同流水线上坐一天每个包裹扫一遍,封装集合内部结构提供统一访问接口。适用于集合类的遍历和树形结构遍历。
当游戏中有自定义的数据集合,如玩家背包中的物品集合,需要以统一的方式进行遍历操作时,迭代器模式就派上了用场。它能将遍历逻辑与数据集合分离,方便扩展和维护。
#include <iostream>
#include <vector>
#include <string>
// 迭代器接口(Iterator):定义访问和遍历元素的操作
class Iterator {
public:
// 检查是否还有下一个元素
virtual bool hasNext() = 0;
// 返回当前元素并移动到下一个位置
virtual std::string next() = 0;
// 虚析构函数确保正确释放子类资源
virtual ~Iterator() = default;
};
// 具体迭代器(Concrete Iterator):实现背包物品的遍历逻辑
class BagIterator : public Iterator {
private:
std::vector<std::string>& items; // 引用背包中的物品集合
int currentIndex; // 当前遍历位置的索引
public:
// 构造函数:初始化物品引用和当前索引
BagIterator(std::vector<std::string>& i) : items(i), currentIndex(0) {}
// 实现hasNext方法:检查是否还有下一个元素
bool hasNext() override {
return currentIndex < items.size(); // 索引小于集合大小时表示还有元素
}
// 实现next方法:返回当前元素并将索引后移
std::string next() override {
if (hasNext()) {
return items[currentIndex++]; // 返回当前元素并增加索引
}
return ""; // 无元素时返回空字符串(实际应用中可能抛出异常)
}
};
// 集合接口(Aggregate):定义创建迭代器的方法
// 注:原代码中Bag类直接实现了创建迭代器的功能,这里可视为隐式的Aggregate接口
class Bag {
private:
std::vector<std::string> items; // 存储背包中的物品
public:
// 添加物品到背包
void addItem(const std::string& item) {
items.push_back(item);
}
// 创建并返回一个迭代器对象,用于遍历背包中的物品
Iterator* createIterator() {
return new BagIterator(items);
}
// 析构函数:释放迭代器资源(实际应用中可考虑使用智能指针避免内存泄漏)
~Bag() {
// 注意:此处简化处理,实际应确保所有创建的迭代器都被正确释放
// 例如:使用智能指针或维护已创建迭代器的列表进行统一管理
}
};
// 示例主函数:演示迭代器模式的使用
int main() {
// 创建背包并添加物品
Bag playerBag;
playerBag.addItem("Sword"); // 添加剑
playerBag.addItem("Potion"); // 添加药水
playerBag.addItem("Shield"); // 添加盾牌
playerBag.addItem("Key"); // 添加钥匙
// 创建迭代器遍历背包物品
std::cout << "=== Player's Bag Contents ===" << std::endl;
Iterator* iterator = playerBag.createIterator(); // 创建迭代器
// 使用迭代器遍历物品
while (iterator->hasNext()) { // 检查是否有下一个元素
std::string item = iterator->next(); // 获取当前元素并移动到下一个位置
std::cout << "- " << item << std::endl; // 打印物品名称
}
// 释放迭代器资源(避免内存泄漏)
delete iterator;
iterator = nullptr;
// 输出结果:
// === Player's Bag Contents ===
// - Sword
// - Potion
// - Shield
// - Key
return 0;
}
责任链模式(Chain of Responsibility)
责任链模式将请求的发送者和接收者解耦,通过构建接收者链,使请求沿链传递,直至被处理。。如同各人自扫门前雪莫管他们瓦上霜,适用于审批流程和异常处理链。实现要点是处理器接口,每个处理器保存对下一个的引用,请求沿链传递直到被处理。
在游戏的审批流程或事件处理流程中,一个请求可能需要经过多个处理环节。例如,游戏内的道具申请,可能需要先经过小队队长审批,再到公会会长审批,责任链模式可以很好地组织这种流程。
#include <iostream>
#include <string>
// 处理者抽象类(Handler):定义处理请求的接口和后继者引用
class Handler {
protected:
Handler* successor; // 后继处理者,用于传递无法处理的请求
public:
// 构造函数:初始化后继者为nullptr
Handler() : successor(nullptr) {}
// 设置后继处理者,构建责任链
void setSuccessor(Handler* s) {
successor = s;
}
// 处理请求的抽象方法,由具体处理者实现
virtual void handleRequest(const std::string& request) = 0;
// 虚析构函数确保子类资源正确释放
virtual ~Handler() = default;
};
// 具体处理者1(Concrete Handler):小队队长,处理与小队相关的请求
class TeamLeader : public Handler {
public:
// 实现处理请求的方法
void handleRequest(const std::string& request) override {
// 判断请求是否包含"team"关键词
if (request.find("team") != std::string::npos) {
// 若包含,则由小队队长处理并输出处理结果
std::cout << "Team Leader approved: " << request << std::endl;
}
// 若无法处理且存在后继者,则将请求传递给后继者
else if (successor != nullptr) {
std::cout << "Team Leader passed: " << request << std::endl;
successor->handleRequest(request);
}
// 若不存在后继者且无法处理,则请求被丢弃
else {
std::cout << "Team Leader couldn't handle: " << request << std::endl;
}
}
};
// 具体处理者2(Concrete Handler):公会会长,处理与公会相关的请求
class GuildMaster : public Handler {
public:
// 实现处理请求的方法
void handleRequest(const std::string& request) override {
// 判断请求是否包含"guild"关键词
if (request.find("guild") != std::string::npos) {
// 若包含,则由公会会长处理并输出处理结果
std::cout << "Guild Master approved: " << request << std::endl;
}
// 若无法处理且存在后继者,则将请求传递给后继者
else if (successor != nullptr) {
std::cout << "Guild Master passed: " << request << std::endl;
successor->handleRequest(request);
}
// 若不存在后继者且无法处理,则请求被丢弃
else {
std::cout << "Guild Master couldn't handle: " << request << std::endl;
}
}
};
// 具体处理者3(Concrete Handler):游戏管理员,处理与系统相关的请求
class GameAdmin : public Handler {
public:
// 实现处理请求的方法
void handleRequest(const std::string& request) override {
// 判断请求是否包含"system"关键词
if (request.find("system") != std::string::npos) {
// 若包含,则由游戏管理员处理并输出处理结果
std::cout << "Game Admin approved: " << request << std::endl;
}
// 若无法处理且存在后继者,则将请求传递给后继者
else if (successor != nullptr) {
std::cout << "Game Admin passed: " << request << std::endl;
successor->handleRequest(request);
}
// 若不存在后继者且无法处理,则请求被丢弃
else {
std::cout << "Game Admin couldn't handle: " << request << std::endl;
}
}
};
// 示例主函数:演示责任链模式的使用
int main() {
// 创建具体处理者对象
TeamLeader teamLeader;
GuildMaster guildMaster;
GameAdmin gameAdmin;
// 构建责任链:TeamLeader -> GuildMaster -> GameAdmin
// 小队队长无法处理的请求传递给公会会长,公会会长无法处理的传递给游戏管理员
teamLeader.setSuccessor(&guildMaster);
guildMaster.setSuccessor(&gameAdmin);
// 测试不同类型的请求在责任链中的处理流程
std::cout << "=== Testing Chain of Responsibility ===" << std::endl;
// 1. 小队相关请求(由TeamLeader直接处理)
std::cout << "\n--- Request 1: Team-related ---" << std::endl;
teamLeader.handleRequest("Add new member to team");
// 2. 公会相关请求(TeamLeader无法处理,传递给GuildMaster处理)
std::cout << "\n--- Request 2: Guild-related ---" << std::endl;
teamLeader.handleRequest("Upgrade guild hall");
// 3. 系统相关请求(TeamLeader和GuildMaster都无法处理,最终由GameAdmin处理)
std::cout << "\n--- Request 3: System-related ---" << std::endl;
teamLeader.handleRequest("Reset server maintenance schedule");
// 4. 无关请求(责任链中没有处理者能处理,最终被丢弃)
std::cout << "\n--- Request 4: Unrelated ---" << std::endl;
teamLeader.handleRequest("Buy in-game item from shop");
return 0;
}
命令模式(Command)
命令模式将请求封装为独立对象,使请求的发送者与执行者解耦。如同运筹帷幄之中决胜千里之外,适用于撤销操作、任务队列和遥控器。关键组件包括Command接口、ConcreteCommand、Invoker和Receiver。
在后端游戏开发中,命令模式可以用来管理玩家的各种操作,如角色的移动、技能释放等,方便实现操作的撤销、重做,以及记录操作日志。
#include <iostream>
#include <string>
#include <vector>
// 接收者类 - 代表命令的实际执行者
// 在这个例子中,接收者是一个游戏角色,它知道如何执行具体的操作
class GameCharacter {
public:
// 获取角色当前状态
std::string getState() const {
return state;
}
// 设置角色状态
void setState(const std::string& newState) {
state = newState;
}
// 角色移动操作
void move(const std::string& direction) {
state = "moved " + direction;
std::cout << "Character " << state << std::endl;
}
// 角色释放技能操作
void castSkill(const std::string& skillName) {
state = "cast " + skillName;
std::cout << "Character " << state << std::endl;
}
private:
std::string state = "idle"; // 角色初始状态为闲置
};
// 命令接口 - 定义了所有具体命令必须实现的方法
// 包括执行操作和撤销操作
class Command {
public:
virtual void execute() = 0; // 执行命令
virtual void undo() = 0; // 撤销命令
virtual ~Command() = default; // 虚析构函数确保正确释放派生类对象
};
// 具体命令类:角色移动命令
// 将角色移动操作封装为一个命令对象
class MoveCommand : public Command {
public:
// 构造函数:初始化接收者和移动方向,并保存当前状态用于撤销
MoveCommand(GameCharacter* receiver, const std::string& direction)
: receiver(receiver), direction(direction) {
prevState = receiver->getState();
}
// 执行命令:调用接收者的移动方法
void execute() override {
receiver->move(direction);
}
// 撤销命令:恢复接收者到执行前的状态
void undo() override {
receiver->setState(prevState);
std::cout << "Undo move, character state restored to: " << prevState << std::endl;
}
private:
GameCharacter* receiver; // 命令的接收者
std::string direction; // 移动方向
std::string prevState; // 执行命令前的状态,用于撤销
};
// 具体命令类:角色释放技能命令
// 将角色释放技能操作封装为一个命令对象
class CastSkillCommand : public Command {
public:
// 构造函数:初始化接收者和技能名称,并保存当前状态用于撤销
CastSkillCommand(GameCharacter* receiver, const std::string& skillName)
: receiver(receiver), skillName(skillName) {
prevState = receiver->getState();
}
// 执行命令:调用接收者的释放技能方法
void execute() override {
receiver->castSkill(skillName);
}
// 撤销命令:恢复接收者到执行前的状态
void undo() override {
receiver->setState(prevState);
std::cout << "Undo cast skill, character state restored to: " << prevState << std::endl;
}
private:
GameCharacter* receiver; // 命令的接收者
std::string skillName; // 技能名称
std::string prevState; // 执行命令前的状态,用于撤销
};
// 调用者类 - 负责调用命令的执行和撤销方法
// 它不直接执行操作,而是将请求委托给命令对象
class CommandInvoker {
public:
// 设置当前要执行的命令
void setCommand(Command* command) {
this->command = command;
}
// 执行当前命令并记录到历史记录中
void executeCommand() {
command->execute();
commandHistory.push_back(command);
}
// 撤销最后执行的命令
void undoLastCommand() {
if (!commandHistory.empty()) {
commandHistory.back()->undo();
commandHistory.pop_back();
} else {
std::cout << "No commands to undo." << std::endl;
}
}
private:
Command* command = nullptr; // 当前命令
std::vector<Command*> commandHistory; // 命令历史记录
};
// 示例主函数:演示命令模式的使用
int main() {
// 创建接收者(游戏角色)
GameCharacter character;
// 创建调用者
CommandInvoker invoker;
// 创建具体命令并关联接收者
Command* moveCommand = new MoveCommand(&character, "north");
Command* castSkillCommand = new CastSkillCommand(&character, "Fireball");
// 使用调用者执行命令
std::cout << "=== Executing Commands ===" << std::endl;
invoker.setCommand(moveCommand);
invoker.executeCommand();
invoker.setCommand(castSkillCommand);
invoker.executeCommand();
// 使用调用者撤销命令
std::cout << "\n=== Undoing Commands ===" << std::endl;
invoker.undoLastCommand(); // 撤销释放技能命令
invoker.undoLastCommand(); // 撤销移动命令
// 清理资源
delete moveCommand;
delete castSkillCommand;
return 0;
}
备忘录模式(Memento)
备忘录模式捕获并外部化一个对象的内部状态,使对象在不暴露实现细节的前提下,可恢复到之前的状态。如同失足不成千古恨想重来时就重来,适用于文档版本控制和游戏存档系统。关键角色包括Originator(需要保存状态的对象)、Memento(状态存储)和Caretaker(管理备忘录)。
在游戏中,备忘录模式可以用于实现存档功能,保存游戏角色的当前状态,以便玩家在需要时恢复到之前的游戏进度。
#include <iostream>
#include <string>
// 备忘录类 - 存储原发器的内部状态
// 备忘录对象是不可变的,一旦创建就不能修改
class Memento {
private:
std::string state; // 存储的状态数据
public:
// 构造函数:初始化备忘录的状态
Memento(const std::string& s) : state(s) {}
// 获取备忘录中存储的状态
std::string getState() const {
return state;
}
};
// 原发器类 - 创建备忘录并使用备忘录恢复其内部状态
// 在这个例子中,原发器是一个游戏角色
class GameCharacter {
private:
std::string state; // 角色的当前状态
public:
// 构造函数:初始化角色状态
GameCharacter(const std::string& s) : state(s) {}
// 设置角色状态
void setState(const std::string& newState) {
std::cout << "GameCharacter: Setting state to " << newState << std::endl;
state = newState;
}
// 获取角色当前状态
std::string getState() const {
return state;
}
// 创建备忘录,保存当前状态
Memento saveStateToMemento() {
std::cout << "GameCharacter: Saving state to Memento" << std::endl;
return Memento(state);
}
// 从备忘录恢复状态
void restoreStateFromMemento(const Memento& memento) {
state = memento.getState();
std::cout << "GameCharacter: State restored from Memento: " << state << std::endl;
}
};
// 负责人类 - 负责保存备忘录,但不检查或修改备忘录的内容
// 负责人可以保存多个备忘录,实现历史记录功能
class Caretaker {
private:
Memento memento; // 保存的备忘录
public:
// 获取保存的备忘录
Memento getMemento() const {
return memento;
}
// 设置备忘录
void setMemento(const Memento& m) {
memento = m;
}
};
// 示例主函数:演示备忘录模式的使用
int main() {
// 创建原发器(游戏角色),初始状态为"Healthy"
GameCharacter character("Healthy");
// 创建负责人,负责管理备忘录
Caretaker caretaker;
// 保存当前状态到备忘录
caretaker.setMemento(character.saveStateToMemento());
// 修改角色状态
character.setState("Wounded");
// 尝试其他操作...
character.setState("Critical");
// 从备忘录恢复状态
character.restoreStateFromMemento(caretaker.getMemento());
return 0;
}
状态模式(State)
状态模式允许对象在其内部状态改变时改变其行为,使对象看起来仿佛修改了自身的类。如同状态驱动行为、行为决定状态,适用于状态机和游戏角色的行为变化。该模式能消除大量条件判断,使状态转换逻辑集中管理。
在游戏中,角色的不同状态(如正常、中毒、昏迷)会影响其行为表现,状态模式可以很好地管理这种状态与行为的对应关系,使代码更具扩展性和维护性。
#include <iostream>
#include <string>
// 状态接口 - 定义了所有具体状态必须实现的方法
// 这些方法表示了在不同状态下对象可以执行的操作
class State {
public:
// 处理方法:根据当前状态执行相应的行为
// 接收游戏角色对象作为参数,以便状态可以影响角色
virtual void handle(GameCharacter& character) = 0;
// 虚析构函数确保正确释放派生类对象
virtual ~State() = default;
};
// 正常状态 - 具体状态实现,表示角色的正常状态
class NormalState : public State {
public:
// 处理方法:当角色处于正常状态时执行的行为
void handle(GameCharacter& character) override {
std::cout << "Character is in normal state" << std::endl;
// 可以在这里实现正常状态下的其他行为
}
};
// 中毒状态 - 具体状态实现,表示角色的中毒状态
class PoisonedState : public State {
public:
// 处理方法:当角色处于中毒状态时执行的行为
void handle(GameCharacter& character) override {
std::cout << "Character is poisoned, losing health slowly" << std::endl;
// 可以在这里实现中毒状态下的其他行为,如降低攻击力等
// 中毒状态可能会随时间变化,这里模拟中毒状态可能会自动恢复
// 实际应用中,状态转换逻辑可能更复杂
// character.setState(new NormalState());
}
};
// 游戏角色类 - 上下文类,维护一个当前状态的引用
// 并将与状态相关的行为委托给当前状态对象处理
class GameCharacter {
private:
State* state; // 当前状态
public:
// 构造函数:初始化角色为正常状态
GameCharacter() : state(new NormalState()) {}
// 析构函数:确保释放状态对象占用的资源
~GameCharacter() {
delete state;
}
// 设置角色的当前状态
// 注意:这里会删除旧状态并创建新状态,可能会导致资源管理问题
// 在实际应用中,可能需要使用智能指针或状态池来优化
void setState(State* s) {
delete state;
state = s;
}
// 执行与当前状态相关的操作
// 将操作委托给当前状态对象处理
void performAction() {
state->handle(*this);
}
};
// 示例主函数:演示状态模式的使用
int main() {
// 创建游戏角色
GameCharacter character;
// 角色执行操作,当前处于正常状态
std::cout << "=== Initial State ===" << std::endl;
character.performAction();
// 角色进入中毒状态
std::cout << "\n=== Changing to Poisoned State ===" << std::endl;
character.setState(new PoisonedState());
character.performAction();
// 角色恢复到正常状态
std::cout << "\n=== Changing back to Normal State ===" << std::endl;
character.setState(new NormalState());
character.performAction();
return 0;
}
访问者模式(Visitor)
访问者模式将算法与对象结构分离,实现对同一对象结构的多种不同操作。如同横看成岭侧成峰、远近高低各不同,易于添加新操作但难以添加新元素类。适用于编译器中的语法树遍历和报表生成器。
在游戏开发中,当需要对不同类型的游戏对象(如不同类型的怪物、道具)进行多种不同操作(如统计信息、计算价值)时,访问者模式可以将操作与对象类型分离,便于扩展新的操作。
#include <iostream>
#include <string>
#include <vector>
// 元素接口 - 定义了所有具体元素必须实现的方法
// 该方法用于接受访问者的访问
class Element {
public:
// 接受访问者的方法
// 参数为访问者对象的指针,通过调用访问者的相应方法实现对元素的访问
virtual void accept(class Visitor* visitor) = 0;
// 虚析构函数确保正确释放派生类对象
virtual ~Element() = default;
};
// 具体元素 - 怪物类
// 实现了Element接口,代表游戏中的怪物元素
class Monster : public Element {
private:
std::string name; // 怪物名称
public:
// 构造函数:初始化怪物名称
Monster(const std::string& n) : name(n) {}
// 接受访问者的方法
// 将自己传递给访问者,调用访问者的visitMonster方法
void accept(Visitor* visitor) override {
visitor->visitMonster(this);
}
// 获取怪物名称
std::string getName() const {
return name;
}
};
// 具体元素 - 道具类
// 实现了Element接口,代表游戏中的道具元素
class Item : public Element {
private:
std::string name; // 道具名称
public:
// 构造函数:初始化道具名称
Item(const std::string& n) : name(n) {}
// 接受访问者的方法
// 将自己传递给访问者,调用访问者的visitItem方法
void accept(Visitor* visitor) override {
visitor->visitItem(this);
}
// 获取道具名称
std::string getName() const {
return name;
}
};
// 访问者接口 - 定义了所有具体访问者必须实现的方法
// 每个方法对应一种具体元素的访问操作
class Visitor {
public:
// 访问怪物元素的方法
virtual void visitMonster(Monster* monster) = 0;
// 访问道具元素的方法
virtual void visitItem(Item* item) = 0;
// 虚析构函数确保正确释放派生类对象
virtual ~Visitor() = default;
};
// 具体访问者 - 统计信息访问者
// 实现了Visitor接口,用于统计游戏中元素的信息
class StatisticsVisitor : public Visitor {
public:
// 访问怪物元素的具体实现
// 这里只是简单地打印怪物的统计信息
void visitMonster(Monster* monster) override {
std::cout << "统计怪物: " << monster->getName() << std::endl;
// 可以在这里添加更复杂的统计逻辑,如计算怪物数量、类型等
}
// 访问道具元素的具体实现
// 这里只是简单地打印道具的统计信息
void visitItem(Item* item) override {
std::cout << "统计道具: " << item->getName() << std::endl;
// 可以在这里添加更复杂的统计逻辑,如计算道具价值、稀有度等
}
};
// 具体访问者 - 描述信息访问者
// 实现了Visitor接口,用于获取游戏中元素的描述信息
class DescriptionVisitor : public Visitor {
public:
// 访问怪物元素的具体实现
// 这里提供了怪物的描述信息
void visitMonster(Monster* monster) override {
std::cout << "怪物描述: " << monster->getName()
<< " - 一种危险的生物,可能会攻击玩家。" << std::endl;
}
// 访问道具元素的具体实现
// 这里提供了道具的描述信息
void visitItem(Item* item) override {
std::cout << "道具描述: " << item->getName()
<< " - 一种有用的物品,可以帮助玩家完成任务。" << std::endl;
}
};
// 对象结构类 - 管理和操作元素集合
// 提供了一个接口让访问者可以访问集合中的所有元素
class GameWorld {
private:
std::vector<Element*> elements; // 元素集合
public:
// 添加元素到集合
void addElement(Element* element) {
elements.push_back(element);
}
// 移除元素从集合
void removeElement(Element* element) {
// 从vector中移除指定元素
elements.erase(std::remove(elements.begin(), elements.end(), element), elements.end());
}
// 接受访问者,让访问者访问集合中的所有元素
void accept(Visitor* visitor) {
for (Element* element : elements) {
element->accept(visitor);
}
}
// 析构函数:释放所有元素占用的资源
~GameWorld() {
for (Element* element : elements) {
delete element;
}
}
};
// 示例主函数:演示访问者模式的使用
int main() {
// 创建游戏世界(对象结构)
GameWorld gameWorld;
// 添加元素到游戏世界
gameWorld.addElement(new Monster("巨龙"));
gameWorld.addElement(new Item("魔法剑"));
gameWorld.addElement(new Monster("地精"));
gameWorld.addElement(new Item("治疗药水"));
// 创建统计信息访问者并访问游戏世界中的元素
std::cout << "=== 使用统计信息访问者 ===" << std::endl;
StatisticsVisitor statsVisitor;
gameWorld.accept(&statsVisitor);
// 创建描述信息访问者并访问游戏世界中的元素
std::cout << "\n=== 使用描述信息访问者 ===" << std::endl;
DescriptionVisitor descVisitor;
gameWorld.accept(&descVisitor);
return 0;
}
中介者模式(Mediator)
中介者模式定义一个中介对象来封装一系列对象之间的交互,使对象无需直接通信,降低系统耦合度。如同联系方式我给你,怎么搞定,我不管,减少对象间直接通信,降低耦合度。适用于聊天室系统和空中交通管制系统。
在大型多人在线游戏中,玩家之间的交互(如聊天、交易)如果直接进行,会导致复杂的耦合关系。中介者模式可以通过引入中介者(如游戏服务器)来协调这些交互,简化系统结构,如聊天室中介者负责转发消息给所有玩家,当新增功能(如私聊、禁言)时只需修改中介者。
#include <iostream>
#include <string>
#include <vector>
// 同事类抽象 - 定义了所有具体同事必须实现的方法
// 同事类通过中介者进行通信,而不是直接相互通信
class Colleague {
protected:
class Mediator* mediator; // 指向中介者的指针
public:
// 构造函数:初始化同事的中介者
Colleague(Mediator* m) : mediator(m) {}
// 发送消息的方法:将消息委托给中介者发送
virtual void send(const std::string& message) = 0;
// 接收消息的方法:处理接收到的消息
virtual void receive(const std::string& message) = 0;
// 虚析构函数确保正确释放派生类对象
virtual ~Colleague() = default;
};
// 具体同事类 - 玩家A
// 实现了Colleague接口,代表游戏中的一个玩家
class PlayerA : public Colleague {
public:
// 构造函数:调用基类构造函数初始化中介者
PlayerA(Mediator* m) : Colleague(m) {}
// 发送消息的具体实现:委托给中介者发送
void send(const std::string& message) override {
std::cout << "Player A sends: " << message << std::endl;
mediator->send(message, this);
}
// 接收消息的具体实现:处理接收到的消息
void receive(const std::string& message) override {
std::cout << "Player A received: " << message << std::endl;
}
};
// 具体同事类 - 玩家B
// 实现了Colleague接口,代表游戏中的另一个玩家
class PlayerB : public Colleague {
public:
// 构造函数:调用基类构造函数初始化中介者
PlayerB(Mediator* m) : Colleague(m) {}
// 发送消息的具体实现:委托给中介者发送
void send(const std::string& message) override {
std::cout << "Player B sends: " << message << std::endl;
mediator->send(message, this);
}
// 接收消息的具体实现:处理接收到的消息
void receive(const std::string& message) override {
std::cout << "Player B received: " << message << std::endl;
}
};
// 中介者接口 - 定义了所有具体中介者必须实现的方法
// 中介者负责协调同事之间的通信
class Mediator {
public:
// 发送消息的方法:处理消息并转发给适当的同事
// 参数包括消息内容和发送者
virtual void send(const std::string& message, Colleague* sender) = 0;
// 虚析构函数确保正确释放派生类对象
virtual ~Mediator() = default;
};
// 具体中介者 - 游戏服务器
// 实现了Mediator接口,负责协调玩家之间的通信
class GameServer : public Mediator {
private:
PlayerA* playerA; // 玩家A的引用
PlayerB* playerB; // 玩家B的引用
public:
// 构造函数:初始化中介者的两个同事
GameServer(PlayerA* p1, PlayerB* p2) : playerA(p1), playerB(p2) {}
// 发送消息的具体实现:根据发送者决定将消息转发给谁
void send(const std::string& message, Colleague* sender) override {
if (sender == playerA) {
// 如果消息由玩家A发送,则转发给玩家B
playerB->receive(message);
} else {
// 如果消息由玩家B发送,则转发给玩家A
playerA->receive(message);
}
}
};
// 示例主函数:演示中介者模式的使用
int main() {
// 创建具体中介者(游戏服务器)
// 注意:这里使用前向声明的指针,因为在创建中介者时,同事对象还未完全构造
PlayerA* playerA = nullptr;
PlayerB* playerB = nullptr;
GameServer* gameServer = new GameServer(playerA, playerB);
// 创建具体同事(玩家)并关联中介者
playerA = new PlayerA(gameServer);
playerB = new PlayerB(gameServer);
// 更新中介者中的同事引用(因为同事对象现在已经完全构造)
// 注意:在实际应用中,可能需要修改GameServer的构造函数或添加setter方法
// 这里为了简化示例,假设GameServer的构造函数已经正确设置了引用
// 演示玩家之间的通信
std::cout << "=== 玩家A向玩家B发送消息 ===" << std::endl;
playerA->send("你好,玩家B!准备好开始游戏了吗?");
std::cout << "\n=== 玩家B向玩家A发送消息 ===" << std::endl;
playerB->send("是的,我准备好了!让我们开始吧!");
// 清理资源
delete playerA;
delete playerB;
delete gameServer;
return 0;
}
解释器模式(Interpreter)
解释器模式定义一个语言的文法并定义一个解释器来解释该语言中的句子。如同我想说方言一切解释权都归我,适用于编译器、规则引擎和正则表达式引擎。适用于特定类型问题频繁出现且可以表示为语言中的句子的场景。
在游戏中,解释器模式可以用于解析自定义的指令语言。例如,游戏内的脚本、GM系统,玩家可以编写一些自定义的指令,解释器模式能够解析并执行这些指令。虽然该模式实现较为复杂,实际应用相对较少,但在特定场景下非常有用。
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include <sstream>
#include <functional>
#include <stdexcept>
// 前向声明
class GameContext;
// 抽象表达式接口 - 所有GM命令表达式的基类
class GMExpression {
public:
virtual ~GMExpression() = default;
// 解释并执行表达式
virtual void interpret(GameContext& context) = 0;
// 返回表达式的字符串表示
virtual std::string toString() const = 0;
};
// 游戏上下文 - 存储游戏状态并执行命令
class GameContext {
private:
// 存储变量值(如玩家等级、金币数量等)
std::unordered_map<std::string, int> variables_;
// 存储命令名称到执行函数的映射
std::unordered_map<std::string, std::function<void(int)>> commands_;
public:
GameContext() {
// 注册基础命令
registerCommand("add_gold", [](int amount) {
std::cout << "[执行] 添加 " << amount << " 金币\n";
});
registerCommand("spawn", [](int enemyId) {
std::cout << "[执行] 生成敌人 ID: " << enemyId << "\n";
});
registerCommand("set_level", [](int level) {
std::cout << "[执行] 设置等级为: " << level << "\n";
});
}
// 注册新的GM命令
void registerCommand(const std::string& name, std::function<void(int)> action) {
commands_[name] = action;
}
// 执行已注册的命令
void executeCommand(const std::string& name, int value) {
if (commands_.count(name)) {
commands_[name](value);
} else {
std::cerr << "[错误] 未知命令: " << name << "\n";
}
}
// 设置变量值
void setVariable(const std::string& name, int value) {
variables_[name] = value;
std::cout << "[执行] 设置变量 " << name << " = " << value << "\n";
}
// 获取变量值
int getVariable(const std::string& name) const {
auto it = variables_.find(name);
return it != variables_.end() ? it->second : 0;
}
};
// 终结符表达式:基础命令 - 表示单个GM命令
class CommandExpression : public GMExpression {
private:
std::string command_; // 命令名称
int param_; // 命令参数
public:
CommandExpression(const std::string& cmd, int param)
: command_(cmd), param_(param) {}
// 解释执行命令
void interpret(GameContext& context) override {
context.executeCommand(command_, param_);
}
std::string toString() const override {
return command_ + " " + std::to_string(param_);
}
};
// 终结符表达式:变量赋值 - 表示设置游戏变量
class SetVariableExpression : public GMExpression {
private:
std::string varName_; // 变量名
int value_; // 变量值
public:
SetVariableExpression(const std::string& name, int value)
: varName_(name), value_(value) {}
// 解释执行变量赋值
void interpret(GameContext& context) override {
context.setVariable(varName_, value_);
}
std::string toString() const override {
return "set " + varName_ + " " + std::to_string(value_);
}
};
// 非终结符表达式:复合命令 - 表示由多个子表达式组成的复合命令
class CompositeExpression : public GMExpression {
private:
std::vector<std::shared_ptr<GMExpression>> expressions_; // 子表达式列表
public:
// 添加子表达式
void addExpression(const std::shared_ptr<GMExpression>& expr) {
expressions_.push_back(expr);
}
// 依次解释执行所有子表达式
void interpret(GameContext& context) override {
std::cout << "\n=== 执行复合命令 ===" << std::endl;
for (const auto& expr : expressions_) {
expr->interpret(context);
}
std::cout << "=== 复合命令执行完毕 ===\n" << std::endl;
}
std::string toString() const override {
std::string result;
for (const auto& expr : expressions_) {
result += expr->toString() + "; ";
}
return result;
}
};
// 解析器 - 将GM命令字符串解析为表达式树
class GMParser {
public:
static std::shared_ptr<GMExpression> parse(const std::string& input) {
std::istringstream iss(input);
std::string token;
auto composite = std::make_shared<CompositeExpression>();
// 分词并解析命令
while (iss >> token) {
if (token == "set") {
// 解析变量赋值命令
std::string varName;
int value;
if (iss >> varName >> value) {
composite->addExpression(
std::make_shared<SetVariableExpression>(varName, value));
} else {
throw std::invalid_argument("变量赋值命令格式错误: " + input);
}
} else {
// 解析普通命令
int value;
if (iss >> value) {
composite->addExpression(
std::make_shared<CommandExpression>(token, value));
} else {
throw std::invalid_argument("命令格式错误: " + token);
}
}
}
return composite;
}
};
// GM系统核心类 - 封装GM功能,提供用户接口
class GMSystem {
private:
GameContext context_; // 游戏上下文
public:
// 注册新的GM命令
void registerCommand(const std::string& name, std::function<void(int)> action) {
context_.registerCommand(name, action);
std::cout << "[系统] 注册命令: " << name << std::endl;
}
// 执行GM命令字符串
void executeCommand(const std::string& command) {
try {
auto expr = GMParser::parse(command);
expr->interpret(context_);
} catch (const std::exception& e) {
std::cerr << "[错误] " << e.what() << std::endl;
}
}
};
// 示例主函数:演示解释器模式的使用
int main() {
std::cout << "=== GM系统演示 ===" << std::endl;
// 创建GM系统实例
GMSystem gmSystem;
// 执行单个命令
std::cout << "\n[演示1] 执行单个命令:" << std::endl;
gmSystem.executeCommand("add_gold 5000");
// 执行变量赋值
std::cout << "\n[演示2] 执行变量赋值:" << std::endl;
gmSystem.executeCommand("set player_level 99");
// 执行复合命令
std::cout << "\n[演示3] 执行复合命令:" << std::endl;
gmSystem.executeCommand("spawn 10086 set player_health 1000");
// 注册并使用自定义命令
std::cout << "\n[演示4] 注册并使用自定义命令:" << std::endl;
gmSystem.registerCommand("teleport", [](int locationId) {
std::cout << "[执行] 传送到位置: " << locationId << std::endl;
});
gmSystem.executeCommand("teleport 7");
return 0;
}
以上完整呈现了 23 种设计模式在游戏开发领域中一些思考以及基于C++ 的实践。
设计模式的设计原则
设计模式应用应遵循七大面向对象设计原则:单一职责、开放封闭、里氏替换、接口隔离、依赖倒置、迪米特法则和合成复用原则。
实际开发中要避免过度设计,优先解决实际问题而非强制使用模式。简单if-else能解决的不要用策略模式。可以组合使用模式,如工厂方法加原型模式实现高效创建复杂对象
选择设计模式要根据具体场景:
- 需要灵活控制对象创建选择工厂/建造者模式;
- 全局唯一访问点选择单例模式;
- 接口不兼容选择适配器模式;
- 动态功能扩展选择装饰器模式;
- 事件驱动系统选择观察者模式;
- 算法切换选择策略模式等。
理解不同模式的核心区别很重要:
- 工厂方法生产单个对象而抽象工厂生产对象家族;
- 装饰器增强功能而代理控制访问;
- 策略模式是算法替换而状态模式是状态驱动行为变化。
结语
设计模式是软件开发者的宝贵工具箱,但并非银弹。关键在于理解模式背后的思想,而非机械套用。应遵循"简单设计"原则,只有当模式能真正解决问题时才使用。设计模式的终极目标是写出可维护、可扩展、可复用的高质量代码。