类型1.适配器模式将一个类的接口转换成客户端希望的另一个接口2.桥接模式将抽象部分与实现部分分离使它们可以独立变化3.组合模式将对象组合成树形结构以表示“部分-整体”的层次结构4.装饰器模式动态地给一个对象添加额外的职责5.外观模式为复杂的子系统提供一个简化的接口6.享元模式通过共享对象来优化大量的细粒度对象7.代理模式为其他对象提供代理以控制对该对象的访问适配器模式对键盘、鼠标、手柄等输入设备通过适配器模式将接口统一让不兼容的接口互相协作。具体输入设备 键盘类和手柄类 每个类都有自己的输入接口class Keyboard { public: void pressKey() { std::cout Keyboard Press Key std::endl; } }; class Gamepad { public: void pressButton() { std::cout Gamepad Press Button std::endl; } };下面我们通过适配器模式对接口进行统一class InputDevice { public: virtual void handleInput() 0; }; class KeyboardAdapter : public InputDevice { private: Keyboard* keyboard; public: KeyboardAdapter(Keyboard* kb):keyboard(kb){}; void handleInput()override { keyboard-pressKey(); } }; class GamepadAdapter : public InputDevice { private: Gamepad* gamepad; public: GamepadAdapter(Gamepad* gp):gamepad(gp){}; void handleInput()override { gamepad-pressButton(); } };再通过一下的代码就可以统一不同适配器对象重写的输入逻辑InputDevice* input_device nullptr; if (useKeyboard) { Keyboard* keyboard new Keyboard(); input_device new KeyboardAdapter(keyboard); } else { Gamepad* gamepad new Gamepad(); input_device new GamepadAdapter(gamepad); } input_device-handleInput();关键要素1.目标接口handleInput方法客户端代码希望使用的接口2.源接口pressKey和pressButton方法3.适配器KeyboardAdapter和GamepadAdapter子类优点符合单一职责原则和开闭原则灵活可以十分方便地增删已有的适配器实际代码统一cjosn和xml的载入的接口//adapter.h struct GameObject { double rotation 0; SDL_Rect rect { 0 }; SDL_Texture* texture nullptr; GameObject(SDL_Texture* _texture, const SDL_Rect _rect, double _rotation) : texture(_texture), rect(_rect), rotation(_rotation) {} }; using GameObjectList std::vectorGameObject; class LoaderImpl { public: virtual bool load(GameObjectList dst, const std::string path) 0; }; class JSONLoader : public LoaderImpl { public: bool load(GameObjectList dst, const std::string path) override; }; class XMLLoader : public LoaderImpl { public: bool load(GameObjectList dst, const std::string path) override; };//adapter.cpp #include 1.adapter.h #include resources_manager.h #include imgui.h #include cJSON.h #include pugixml.hpp #include fstream #include sstream using namespace _AdapterPattern; bool JSONLoader::load(GameObjectList dst, const std::string path) { std::ifstream file(path); if (!file.good()) return false; std::stringstream str_stream; str_stream file.rdbuf(); file.close(); cJSON* json_root cJSON_Parse(str_stream.str().c_str()); if (!json_root) return false; cJSON* json_game_obj nullptr; cJSON_ArrayForEach(json_game_obj, json_root) { cJSON* json_rect cJSON_GetObjectItem(json_game_obj, rect); cJSON* json_texture cJSON_GetObjectItem(json_game_obj, texture); cJSON* json_rotation cJSON_GetObjectItem(json_game_obj, rotation); SDL_Rect rect { cJSON_GetObjectItem(json_rect, x)-valueint, cJSON_GetObjectItem(json_rect, y)-valueint, cJSON_GetObjectItem(json_rect, w)-valueint, cJSON_GetObjectItem(json_rect, h)-valueint, }; dst.emplace_back(ResourcesManager::instance()-find_texture(json_texture-valuestring), rect, json_rotation-valuedouble); } cJSON_Delete(json_root); return true; } bool _AdapterPattern::XMLLoader::load(GameObjectList dst, const std::string path) { pugi::xml_document doc; if (!doc.load_file(path.c_str())) return false; pugi::xml_node xml_root doc.child(Scene); for (const pugi::xml_node xml_game_obj : xml_root.children(GameObject)) { const pugi::xml_node xml_rect xml_game_obj.child(Rect); double rotation xml_game_obj.attribute(rotation).as_double(); SDL_Texture* texture ResourcesManager::instance()-find_texture(xml_game_obj.attribute(texture).as_string()); SDL_Rect rect { xml_rect.attribute(x).as_int(), xml_rect.attribute(y).as_int(), xml_rect.attribute(w).as_int(), xml_rect.attribute(h).as_int(), }; dst.emplace_back(texture, rect, rotation); } return true; } AdapterPattern::AdapterPattern(SDL_Renderer* renderer) { texture_target SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 768, 768); } AdapterPattern::~AdapterPattern() { SDL_DestroyTexture(texture_target); } void AdapterPattern::on_update(float delta) { if (ImGui::Button(u8 \data/json_scene.json\ ļ, { ImGui::GetContentRegionAvail().x / 2, 35 })) { reload_scene(json_loader, data/json_scene.json); } ImGui::SameLine(); if (ImGui::Button(u8 \data/xml_scene.xml\ ļ, { ImGui::GetContentRegionAvail().x, 35 })) { reload_scene(xml_loader, data/xml_scene.xml); } ImGui::BeginChild(scene, ImGui::GetContentRegionAvail()); ImGui::Image(texture_target, ImGui::GetContentRegionAvail()); ImGui::EndChild(); } void AdapterPattern::on_render(SDL_Renderer* renderer) { SDL_SetRenderTarget(renderer, texture_target); SDL_SetRenderDrawColor(renderer, 15, 15, 15, 255); SDL_RenderClear(renderer); for (const GameObject game_object : game_object_list) { SDL_RenderCopyEx(renderer, game_object.texture, nullptr, game_object.rect, game_object.rotation, nullptr, SDL_RendererFlip::SDL_FLIP_NONE); } SDL_SetRenderTarget(renderer, nullptr); } void AdapterPattern::reload_scene(_AdapterPattern::LoaderImpl* loader, const std::string path) { game_object_list.clear(); if (!loader-load(game_object_list, path)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, u8Reload Failed, u8¼سļʧܣļǷڼļǷϱ׼, nullptr); } }桥接模式对底层内容进行统一抽象暴露给用户简明易用的接口首先定义Renderer抽象基类class Renderer { public: virtual void renderShape(const std::string shapeName) 0; };每个子类都需要实现renderShape图元绘制下面我们来进行具体实现//OpenGL具体实现 class OpenGLRenderer : public Renderer { public: void renderShape(const std::string shapeName) override { std::cout OpenGL Renderer std::endl; } }; //Direct具体实现 class DirectXRenderer : public Renderer { public: void renderShape(const std::string shapeName) override { std::cout DirectX Renderer std::endl; } };之后是对图形的抽象class Shape { protected: Renderer* renderer; public: Shape(Renderer* renderer): renderer(renderer) {}; virtual void draw() 0; };图形的具体抽象类//具体抽象类 圆 class Circle : public Shape { public: Circle(Renderer* renderer): Shape(renderer) {}; void draw() override { renderer-renderShape(Circle); } }; //具体抽象类 矩形 class Rectangle : public Shape { public: Rectangle(Renderer* renderer): Shape(renderer) {}; void draw() override { renderer-renderShape(Rectangle); } };设计要点抽象层抽象类Shape类扩展抽象类Circle和Rectangle类实现层实现类接口Renderer接口类具体类接口 OpenGLRenderer和DirectXRenderer类工厂模式与桥接模式的不同工厂模式关注如何创建对象根据不同条件动态创建不同的产品桥接模式使抽象与实现分离两部分可以独立变化实现解耦组合模式允许将对象组合成树形结构来表示“部分-整体”的层次结构首先定义GameObject基类所以存在于游戏场景中的物体对象都应该继承go基类游戏对象在实现自身的onUpate功能的同时要还要充当容器允许拥有子类class GameObject { private: std::vectorGameObject* cildList; public: void addChild(GameObject* go) { cildList.push_back(go); } virtual void onUpdate() { for (GameObject* child:cildList) child-onUpdate(); } };提供Sprite和Music两种子类的封装class Sprite : public GameObject { public: void onUpdate() override { std::cout Sprite std::endl; GameObject::onUpdate(); } }; class Music : public GameObject { public: void onUpdate() override { std::cout Music std::endl; GameObject::onUpdate(); } };在客户端的代码中我们可以简单的通过下面的代码来实现一个拥有画面形象并自带bgm的角色GameObject* player new GameObject(); player-addChild(new Sprite()); player-addChild(new Music()); player-onUpdate();关键要素组件定义所以叶子节点和复合节点的共有的接口。叶子结点最终执行操作的基础对象或终端元素不包括子对象。符合结点包括一个或多个子对象的节点提供对子树的管理接口。ECS与组合ECS(Entity Component System):数据定义实体的理念关注的是性能优化和行为分离。组合模式强调层次结构和对象组织。装饰器模式在不改变原始对象结构的情况下动态地给一个对象添加额外的职责或功能例如角色的buff系统动态的改变玩家的数值属性class Character { public: virtual void attack() 0; };下面是没有属性加成的基础角色class BasicCharacter: public Character { public: void attack() override { std::cout Basic attack std::endl; } };下面实现角色装饰器 可以调用基础攻击class CharacterDecorator: public Character { protected: Character* character; public: CharacterDecorator(Character* character) : character(character) {} void attack() override { character-attack(); } };在下面的代码中我们首先对攻击的属性进行修改再调用攻击。class AttackPowerDecorator: public CharacterDecorator { private: int additionalPower; public: AttackPowerDecorator(Character* character, int additionalPower) : CharacterDecorator(character), additionalPower(additionalPower) {} void attack() override { std::coutAttack power increased byadditionalPower!std::end; CharacterDecorator::attack(); } };在客户端我们就可以执行以下代码Character* character new BasicCharacter(); character-attack(); Character* enhancedCharacter new AttackPowerDecorator(character,20); enhancedCharacter-attack();关键要素组件定义统一接口Character类具体组件实现基础功能的类BasicCharacter类装饰器继承自组件并持有组件对象CharacterDecorator类具体装饰器实际实现装饰功能的装饰器子类AttackPowerDecorator类应用场景权限管理系统控制用户的增删改查游戏输入系统实现抖动过滤、坐标转换等功能数据加解密数据传输过程中压缩、加密和协议封装优点无需创建新的子类就可以扩展对象行为灵活可以在运行时动态添加删除对象功能单一职责可以拆分多个装饰器共同完成复杂行为缺点删除特定装饰器比较困难确保装饰器效果不会因为顺序受到影响外观模式可以为子系统提供一个简化的接口来减少客户端与子系统之间的交互的复杂性封装 外观类要单独抽离出来作为代码结构层以下为简单实例//硬件层 class GameFacade { public: void InitializeGame() { graphicsSystem-Initialize(); audioSystem-Initialize(); physicsEngine-Initialize(); inputManager-Initialize(); } private: GraphixsSystem* graphicsSystem; AudioSystem* audioSystem; PhysicsEngine* physicsEngine; InputManager* inputManager; };关键要素外观类提供统一的接口供客户端调用。子系统实现具体业务逻辑的类包括了系统的核心功能优缺点优点简明同时让代码独立于复杂子系统缺点可能会成为与所有类都耦合到上帝对象享元模式优化大量对象的内存占用 通过共享技术来有效地支持大量细粒度对象的复用从而减少内存占用。//享元类 class BulletFlyweight { public: BulletFlyweight(const std::string texture):texture(texture) {} void Render(int x, int y) { std::coutRendering bullet at (x, y) with texturetexturestd::endl; } private: std::string texture; }; //享元工厂 class BulletFactory { public: BulletFlyweight* GetBullet(const std::string texture) { if (bullets.find(texture) bullets.end()) { bullets[texture] new BulletFlyweight(texture); } return bullets[texture]; } private: std::unordered_mapstd::string,BulletFlyweight* bullets; };在内存中每种类型子弹只有一种class Bullet { public: Bullet(BulletFlyweight* bullet,int x, int y):bullet(bullet),x(x),y(y){} void Move(int dx, int dy) { xdx; ydy; } void Render() { bullet-Render(x,y); } private: BulletFlyweight* bullet; int x; int y; };在创建独有的子弹对象时内存中增加的只是不同子弹对象独有的数据关键要素享元多个对象所共享的数据或状态BulletFlyweight类。享元工厂对已有的享元缓存池进行管理BulletFactory类。上下文每个对象所独有的状态Bullet类。典型场景瓦片地图对单个瓦片对象所使用的纹理、属性进行共享粒子系统对粒子所使用的纹理材质以及运动规律等数据进行共享缺点更新上下文可能需要牺牲执行速度代码结构会因此变得复杂单个对象的功能由多个对象组合完成代理模式创建一个代理层用来替代客户端对原始对象的访问 代理原始对象的替代品或者占位符可以在将客户端提交的请求经过处理后再传递给原始对象。//服务接口区块接口 class IChunk { public: virtual void Load() 0; virtual void Rneder() 0; };//真实服务具体的区块 class RealChunk : public IChunk { public: RealChunk(int x, int y):x(x),y(y) { std::coutRealChunk createdstd::endl; } void Load() override { std::coutRealChunk loadedstd::endl; } void Rneder() override { std::coutRealChunk renderedstd::endl; }; private: int x; int y; };//代理区块加载代理 class ChunkProxy : public IChunk { public: ChunkProxy(int x, int y):x(x),y(y),realChunk(nullptr) {} void Load() override { if (!realChunk) { realChunk new RealChunk(x,y); } realChunk-Load(); } void Rneder() override { if (realChunk) { realChunk-Rneder(); } else { std::coutRealChunk renderedstd::endl; } } ~ChunkProxy() { delete realChunk; } private: int x; int y; RealChunk *realChunk; };关键组成要素服务接口代理类伪装所必须遵守的规则实际服务实际用于提供业务逻辑的类代理包括实际服务对象成员代理完成任务应用场景延迟加载大型开放世界游戏中对地图的动态加载。资源缓存对音频、模型等繁重资源进行代理避免重复从文件或服务器获取。权限控制在多人联机游戏中对玩家的行为进行校验判断是否有权限执行。远程代理远程过程调用RPC将客户端与服务端的通信简化为本地对象调用。智能代理为已有的业务添加日志监控性能记录等功能。优点可以在客户端毫无察觉的情况下控制服务对象实现对实际服务对象生命周期的智能管理代理类可以在服务对象还为准备好或不存在时正常工作缺点在服务没有正常起作用时调试多了一个维度。实际服务的响应可能会延迟。