学习设计模式 设计模式分类
创造型
结构型
主要管理类和对象的组合,形成较大的结构的同时保持结构的灵活性和高效性
行为型
创造型 工厂方法模式
定义创建对象的接口,但由子类决定具体实例化的类。将对象的创建延迟到子类
要素
抽象产品
具体产品
抽象工厂/创建者
工厂基类,提供统一的抽象创造方法(接口),由具体工厂实现(重写)(可省略?)
具体工厂
继承抽象工厂,实现(重写)抽象工厂的方法(接口),根据条件去创造具体产品
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 #include <iostream> #include <utility> #include <vector> typedef std::pair<int , int > Vector2;enum class Attribute { Ice, Grass, Fire };class GameObject { public : GameObject (const Vector2& _position) : position (_position) {} virtual void on_update (float delta) = 0 ; virtual void on_render () = 0 ; protected : Vector2 position; }; class Sword : public GameObject{ public : Sword (const Vector2& _position) : GameObject (_position) {} void on_update (float delta) override {} void on_render () override { std::cout << "生成剑纹理" << std::endl; } }; class Slime : public GameObject{ public : Slime (const Vector2& _position) : GameObject (_position) {} void on_update (float delta) override { std::cout << "slime 动画更新" << std::endl; } void on_render () override { std::cout << "生成 slime 动画" << std::endl; } }; class IceSword : public Sword{ public : IceSword (const Vector2& _position) : Sword (_position) {} }; class GrassSword : public Sword{ public : GrassSword (const Vector2& _position) : Sword (_position) {} }; class FireSword : public Sword{ public : FireSword (const Vector2& _position) : Sword (_position) {} }; class IceSlime : public Slime{ public : IceSlime (const Vector2& _position) : Slime (_position) {} }; class GrassSlime : public Slime{ public : GrassSlime (const Vector2& _position) : Slime (_position) {} }; class FireSlime : public Slime{ public : FireSlime (const Vector2& _position) : Slime (_position) {} }; class SwordFactory { public : Sword* create (Attribute attribute, const Vector2& position) { switch (attribute) { case Attribute::Ice: return new IceSword (position); break ; case Attribute::Grass: return new GrassSword (position); break ; case Attribute::Fire: return new FireSword (position); break ; default : return nullptr ; break ; } } }; class SlimeFactory { public : Slime* create (Attribute attribute, const Vector2& position) { switch (attribute) { case Attribute::Ice: return new IceSlime (position); break ; case Attribute::Grass: return new GrassSlime (position); break ; case Attribute::Fire: return new FireSlime (position); break ; default : return nullptr ; break ; } } }; class FactoryMethodPattern { public : FactoryMethodPattern () { SwordFactory sword_factory; SlimeFactory slime_factory; game_obj_list.push_back (sword_factory.create (Attribute::Ice, { 200 , 100 })); game_obj_list.push_back (sword_factory.create (Attribute::Grass, { 200 , 300 })); game_obj_list.push_back (sword_factory.create (Attribute::Fire, { 200 , 500 })); game_obj_list.push_back (slime_factory.create (Attribute::Ice, { 425 , 140 })); game_obj_list.push_back (slime_factory.create (Attribute::Grass, { 425 , 340 })); game_obj_list.push_back (slime_factory.create (Attribute::Fire, { 425 , 540 })); } ~FactoryMethodPattern () { for (GameObject* game_obj : game_obj_list) delete game_obj; } void on_update (float delta) { for (GameObject* game_obj : game_obj_list) game_obj->on_update (delta); } void on_render () { for (GameObject* game_obj : game_obj_list) game_obj->on_render (); } private : std::vector<GameObject*> game_obj_list; };
优点
解耦客户端与具体类依赖
将产品的代码与使用该产品的客户端内容进行分离
譬如在游戏开发中,玩家类只需要依赖具体工厂类,无需关注产品类内部实现与构造需求
提高系统的可扩展性
引入新产品无需修改已有代码
引入新产品时,只需要构建新的具体工厂类
封装复杂的对象创建逻辑
遵循开闭原则和单一职责原则
提高可维护性
抽象工厂方法 要素
抽象产品
提供一个接口,用于创建相关或依赖对象的家族,而无需指定具体类。
具体产品
抽象工厂
具体工厂
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 #include <iostream> class Weapon { public : virtual void used () = 0 ; }; class Armor { public : virtual void equited () = 0 ; }; class Sword : public Weapon{ public : void used () override { std::cout << "used sword" << std::endl; } }; class Shield : public Armor{ public : void equited () override { std::cout << "equited shield" << std::endl; } }; class Staff : public Weapon{ public : void used () override { std::cout << "used staff" << std::endl; } }; class SpellBook : public Armor{ public : void equited () override { std::cout << "equited spellbook" << std::endl; } }; class EquitmentFactory { public : virtual Weapon* create_weapon () = 0 ; virtual Armor* create_armor () = 0 ; }; class WarriorFactory : public EquitmentFactory{ public : Weapon* create_weapon () override { return new Sword (); } Armor* create_armor () override { return new Shield (); } }; class MageFactory : public EquitmentFactory{ public : Weapon* create_weapon () override { return new Staff (); } Armor* create_armor () override { return new SpellBook (); } }; class Character { public : Character (EquitmentFactory* factory) : factory (factory) {} ~Character () = default ; void equit () { Weapon* weapon = factory->create_weapon (); Armor* armor = factory->create_armor (); weapon->used (); armor->equited (); } private : EquitmentFactory* factory; }; int main () { EquitmentFactory* warrior_factory = new WarriorFactory (); Character warrior (warrior_factory) ; warrior.equit (); EquitmentFactory* mage_factory = new MageFactory (); Character mage (mage_factory) ; mage.equit (); return 0 ; }
优点
在工厂模式方法 基础上,有更多维度的定制化
建造者模式
可以叫做“生成器模式”,通常在复杂对象的构造中,把对象构造的逻辑从其本身的类中抽离出来,让专门的生成器来负责
要素
目标产品
抽象生成器
具体生成器
导演/主管
代理调用生成器的各个建造步骤,如果对生成的步骤没有严格要求就可以省略
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 #include <iostream> #include <string> #include <vector> class GameLevel { public : std::string level_name; std::vector<std::string> enemy_list; std::vector<std::string> item_list; void run () { std::cout << "run" << std::endl; } }; class LevelBuilder { public : virtual void set_level_name (const std::string& name) = 0 ; virtual void add_enemy (const std::string& enemy) = 0 ; virtual void add_item (const std::string& item) = 0 ; virtual void set_mode () = 0 ; virtual GameLevel* build () = 0 ; }; class EasyLevelBuilder : public LevelBuilder{ public : EasyLevelBuilder () { game_level = new GameLevel (); } void set_level_name (const std::string& name) override { game_level->level_name = name; } void add_enemy (const std::string& enemy) override { game_level->enemy_list.push_back (enemy); } void add_item (const std::string& item) override { game_level->item_list.push_back (item); } void set_mode () override { mode = "easy" ; } GameLevel* build () override { return game_level; } private : GameLevel* game_level; std::string mode; }; class HardLevelBuilder : public LevelBuilder{ public : HardLevelBuilder () { game_level = new GameLevel (); } void set_level_name (const std::string& name) override { game_level->level_name = name; } void add_enemy (const std::string& enemy) override { game_level->enemy_list.push_back (enemy); } void add_item (const std::string& item) override { game_level->item_list.push_back (item); } void set_mode () override { mode = "hard" ; } GameLevel* build () override { return game_level; } private : GameLevel* game_level; std::string mode; }; class GameLevelDirector { public : GameLevelDirector (LevelBuilder* level_build) :level_build (level_build){} ~GameLevelDirector () = default ; GameLevel* create_easy_level_1 () { level_build->set_level_name ("Swamp" ); level_build->set_mode (); level_build->add_enemy ("Slime" ); level_build->add_enemy ("Slime" ); level_build->add_enemy ("Wolf" ); level_build->add_item ("Apple" ); level_build->add_item ("Sword" ); return level_build->build (); } GameLevel* create_easy_level_2 () { level_build->set_level_name ("Forest" ); level_build->add_enemy ("Slime" ); level_build->add_enemy ("Wolf" ); level_build->add_enemy ("Goblin" ); level_build->add_item ("Stone" ); return level_build->build (); } GameLevel* create_hard_level_1 () { level_build->set_level_name ("Swamp" ); level_build->set_mode (); level_build->add_enemy ("Slime" ); level_build->add_enemy ("Slime" ); level_build->add_enemy ("Wolf" ); level_build->add_enemy ("Goblin" ); level_build->add_enemy ("Wolf" ); level_build->add_item ("Apple" ); level_build->add_item ("Sword" ); return level_build->build (); } GameLevel* create_hard_level_2 () { level_build->set_level_name ("Swamp" ); level_build->set_mode (); level_build->add_enemy ("Slime" ); level_build->add_enemy ("Goblin" ); level_build->add_enemy ("Goblin" ); level_build->add_enemy ("Wolf" ); level_build->add_enemy ("Wolf" ); level_build->add_item ("Apple" ); level_build->add_item ("Sword" ); return level_build->build (); } private : LevelBuilder* level_build; }; class BuilderPattern { public : BuilderPattern () { easy_dir = new GameLevelDirector (new EasyLevelBuilder ()); easy_dir->create_easy_level_1 (); easy_dir->create_easy_level_2 (); hard_dir = new GameLevelDirector (new HardLevelBuilder ()); hard_dir->create_hard_level_1 (); hard_dir->create_hard_level_2 (); } ~BuilderPattern () = default ; private : GameLevelDirector* easy_dir; GameLevelDirector* hard_dir; };
优点
简化构造参数
拆分单个类的构造任务到多个生成器步骤,避免构造产品对象时需要过多的构造参数
灵活组合步骤
解耦构造过程,从而使用组合灵活地产出更多变种的产品
动态感知构造过程
导演类可以看作是对构造器的屏蔽与封装,借助导演类可以在运行时根据建造过程调整各部分任务,暂缓创建或执行递归构造
原型模式
通过复制现有对象来创建新对象,而不是通过实例化类来创建新对象
适用于创建大量相似对象的场景,避免了重复的初始化和构造过程
要素
原型接口
具体原型
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include <iostream> #include <string> #include <vector> class Prototype { public : virtual Prototype* clone () const = 0 ; virtual void showInfo () const = 0 ; }; class Soldier : public Prototype{ public : Soldier (std::string type, int health, int attack, int defense) : type (type), health (health), attack (attack), defense (defense) {} ~Soldier () = default ; Prototype* clone () const override { return new Soldier (*this ); } void showInfo () const override { std::cout << "type:" << type << " health:" << health << " attack:" << attack << " defense:" << defense << std::endl; } private : std::string type; int health; int attack; int defense; }; class Archer : public Prototype{ public : Archer (std::string type, int health, int attack, int defense) : type (type), health (health), attack (attack), defense (defense) {} ~Archer () = default ; Prototype* clone () const override { return new Archer (*this ); } void showInfo () const override { std::cout << "type:" << type << " health:" << health << " attack:" << attack << " defense:" << defense << std::endl; } private : std::string type; int health; int attack; int defense; }; class Knight : public Prototype{ public : Knight (std::string type, int health, int attack, int defense) : type (type), health (health), attack (attack), defense (defense) {} ~Knight () = default ; Prototype* clone () const override { return new Knight (*this ); } void showInfo () const override { std::cout << "type:" << type << " health:" << health << " attack:" << attack << " defense:" << defense << std::endl; } private : std::string type; int health; int attack; int defense; }; int main () { Soldier* soldierPrototype = new Soldier ("Infantry" , 100 , 10 , 5 ); Archer* soldierPrototype = new Archer ("Longbow" , 80 , 15 , 3 ); Knight* soldierPrototype = new Knight ("Cavalry" , 150 , 20 , 10 ); Prototype* clone_soldier = soldierPrototype->clone (); Prototype* clone_archer = soldierPrototype->clone (); Prototype* clone_knight = soldierPrototype->clone (); clone_soldier->showInfo (); clone_archer->showInfo (); clone_knight->showInfo (); return 0 ; }
优点
避免重复初始化,提升性能
动态生成对象副本,支持运行时配置
思考
为何不直接使用拷贝构造函数
拷贝构造函数是静态实现,要使用拷贝构造函数时需要知道具体的类;而原型模式是动态实现的,使用原型模式时只需要知道原型接口即可
拷贝构造函数涉及到浅拷贝和深拷贝,除去内存上的问题,若不保留原型直接使用场内原有的对象进行拷贝,容易拷贝多余的对象状态造成污染
为何不能在类外手动调用拷贝类内字段
私有字段不宜访问,会产生依赖;而原型模式可以通过原型接口来实现对类内字段的访问和操作
面对复杂字段的对象非常繁琐,会增加人为失误的可能
这种方法同样需要知晓具体子类,产生不必要的依赖
单例模式
确保一个类只有一个实例,并提供一个全局访问点,为静态变量的生命周期。分为懒汉模式和饿汉模式
懒汉模式:在类加载时单例对象为空,在第一次使用时创建实例,对比饿汉模式更加节省空间,可能存在多线程隐患
饿汉模式:在类加载时创建实例,对外提供静态方法来获取单例对象的引用
要素
单例对象
示例代码(懒汉模式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> class Manager { public : static Manager* getInstance () { if (!instance) instance = new Manager (); return instance; } void write_note () { std::cout << "note" << std::endl; } protected : Manager () { std::cout << "create manager" << std::endl; } ~Manager () = default ; Manager (const Manager&) = delete ; Manager& operator =(const Manager&) = delete ; private : static Manager* instance; }; int main () { Manager::getInstance ()->write_note (); return 0 ; }
示例代码(饿汉模式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> class Manager { public : static Manager& getInstance () { return instance; } void write_note () { std::cout << "note" << std::endl; } protected : Manager () { std::cout << "create manager" << std::endl; } ~Manager () = default ; Manager (const Manager&) = delete ; Manager& operator =(const Manager&) = delete ; private : static Manager instance; }; int main () { Manager::getInstance ().write_note (); return 0 ; }
优点
严格保证只有一个实例
创造型设计模式对比
模式名称
核心目标
工厂方法
子类决定实例化哪个类
抽象工厂
创建相关或依赖对象的家族,而无需指定具体类
建造者
分步骤构建复杂对象
原型
通过复制现有对象来创建新对象
单例
确保一个类只有一个实例,并提供一个全局访问点
结构型 适配器模式
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作
利用继承适配不同接口
要素
目标接口
原接口
适配器类
提供兼容层,避免客户端代码直接依赖不同的对象
可以对底层的输入设备指令进行翻译,对客户端暴露更贴近逻辑的接口
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <iostream> #include <string> #include <vector> class GameObject { public : void show () const { std::cout << "health:" << health << " speed:" << speed << std::endl; } private : int health; float speed; }; class LoaderImpl { public : virtual bool load (std::vector<GameObject*> list, const std::string& path) = 0 ; }; class JSONLoader : public LoaderImpl{ public : bool load (std::vector<GameObject*> list, const std::string& path) override { std::cout << "解析JSON到dst" << std::endl; } }; class XMLLoader : public LoaderImpl{ public : bool load (std::vector<GameObject*> list, const std::string& path) override { std::cout << "解析XML到dst" << std::endl; } }; class AdapterPattern { public : AdapterPattern () = default ; ~AdapterPattern () = default ; private : XMLLoader xml_loader; JSONLoader json_loader; std::vector<GameObject*> game_object_list; private : void reload (LoaderImpl* loader, const std::string& path) { game_object_list.clear (); if (!loader->load (game_object_list, path)) { std::cout << "错误:游戏对象加载失败" << std::endl; } } };
优点
符合单一职责原则和开闭原则
灵活,可以方便地增删适配器
桥接模式
要素
抽象类
扩展抽象类
实现类接口
具体实现类
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <iostream> #include <string> class Renderer { public : virtual void draw_shape (const std::string& shape) = 0 ; }; class OpenGLRenderer : public Renderer{ public : void draw_shape (const std::string& shape) override { std::cout << "使用OpenGL绘制" << shape << std::endl; } }; class DirectXRenderer : public Renderer{ public : void draw_shape (const std::string& shape) override { std::cout << "使用DirectX绘制" << shape << std::endl; } }; class Shape { public : Shape (Renderer* renderer) : renderer (renderer) {} ~Shape () = default ; virtual void draw () = 0 ; protected : Renderer* renderer; }; class Circle : public Shape{ public : Circle (Renderer* renderer) : Shape (renderer) {} void draw () override { renderer->draw_shape ("Circle" ); } }; class Rectangle : public Shape{ public : Rectangle (Renderer* renderer) : Shape (renderer) {} void draw () override { renderer->draw_shape ("Rectangle" ); } }; int main () { Renderer* openglRenderer = new OpenGLRenderer (); Renderer* directxRenderer = new DirectXRenderer (); Shape* circle = new Circle (openglRenderer); Shape* rectangle = new Rectangle (directxRenderer); circle->draw (); rectangle->draw (); }
优点
符合单一职责原则和开闭原则
分离抽象和实现,提高灵活性
隐藏了实现细节
思考
桥接模式和工厂方法模式
工厂方法模式关注的是对象的创建,根据不同条件动态创建不同产品
桥接模式关注的是对象的实现和抽象的分离,两者可以独立变化实现解耦
二者可以结合使用,工厂方法模式可以用于创建桥接模式中的实现类,而桥接模式可以用于创建工厂方法模式中的抽象类
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 #include <iostream> #include <string> class Renderer { public : virtual void draw_shape (const std::string& shape) = 0 ; }; class OpenGLRenderer : public Renderer{ public : void draw_shape (const std::string& shape) override { std::cout << "使用OpenGL绘制" << shape << std::endl; } }; class DirectXRenderer : public Renderer{ public : void draw_shape (const std::string& shape) override { std::cout << "使用DirectX绘制" << shape << std::endl; } }; class Shape { public : Shape (Renderer* renderer) : renderer (renderer) {} ~Shape () = default ; virtual void draw () = 0 ; protected : Renderer* renderer; }; class Circle : public Shape{ public : Circle (Renderer* renderer) : Shape (renderer) {} void draw () override { renderer->draw_shape ("Circle" ); } }; class Rectangle : public Shape{ public : Rectangle (Renderer* renderer) : Shape (renderer) {} void draw () override { renderer->draw_shape ("Rectangle" ); } }; class RendererFactory { public : virtual Renderer* createRenderer () = 0 ; }; class OpenGlRendererFactory : public RendererFactory{ public : Renderer* createRenderer () override { return new OpenGLRenderer (); } }; class DirectXRendererFactory : public RendererFactory{ public : Renderer* createRenderer () override { return new DirectXRenderer (); } }; int main () { RendererFactory* factory = new OpenGlRendererFactory (); Renderer* renderer = factory->createRenderer (); Shape* circle = new Circle (renderer); Shape* rectangle = new Rectangle (renderer); circle->draw (); rectangle->draw (); }
桥接模式和适配器模式
桥接模式关注抽象与实现分离,通过组合来代替继承,允许独立变化
桥接模式类似于将实现类作为适配器模式的客户端,在适配器模式上进一步地扩展
适配器模式关注接口的转换(统一),通过继承的方式来实现不同接口之间的适配
组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性
要素
组件接口
叶子节点
具体的对象,不能再包含其他对象,类似于数据结构中树的叶子节点
复合节点
包含一个或多个子节点的对象,提供子树的管理接口,客户端只需要关注最顶层的复合节点即可
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <iostream> #include <vector> class GameObject { public : void add_child (GameObject* child) { childList.emplace_back (child); } virtual void on_update () { for (GameObject* go : childList) go->on_update (); } private : std::vector<GameObject*> childList; }; class Sprite : public GameObject{ public : void on_update () override { std::cout << "sprite on_update" << std::endl; GameObject::on_update (); } }; class Music : public GameObject{ public : void on_update () override { std::cout << "music on_update" << std::endl; GameObject::on_update (); } }; int main () { GameObject* player = new GameObject (); player->add_child (new Sprite ()); player->add_child (new Music ()); GameObject* enemy = new GameObject (); enemy->add_child (new Sprite ()); enemy->add_child (new Music ()); GameObject* scene = new GameObject (); scene->add_child (player); scene->add_child (enemy); scene->on_update (); return 0 ; }
优点
简化客户端代码,统一处理简单和复杂元素
支持递归组合,灵活扩展结构
装饰器模式
动态地给一个对象添加一些额外的职责,装饰模式提供了比继承更灵活的替代方案
装饰器模式可以在不改变对象自身的情况下,动态地添加功能
要素
组件接口
具体组件
装饰器接口
具体装饰器
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 #include <iostream> #include <memory> class Player { public : virtual int get_power () const { return power; } virtual void set_power (int val) { power = val; } virtual int get_defense () const { return defense; } virtual void set_defense (int val) { defense = val; } virtual void on_update () {} protected : int power; int defense; }; class BasePlayer : public Player{ public : BasePlayer () { power = 2 ; defense = 10 ; } ~BasePlayer () = default ; }; class PowerUpDecorator : public Player{ public : PowerUpDecorator (Player* player) { this ->player = player; } ~PowerUpDecorator () = default ; int get_power () const { return player->get_power (); } int get_defense () const { return player->get_defense (); } void set_power (int val) override { player->set_power (val); } void set_defense (int val) override { player->set_defense (val); } void on_update () override { player->set_power (4 ); player->on_update (); } private : Player* player; }; class DefenseUpDecorator : public Player{ public : DefenseUpDecorator (Player* player) { this ->player = player; } ~DefenseUpDecorator () = default ; int get_power () const { return player->get_power (); } int get_defense () const { return player->get_defense (); } void set_power (int val) override { player->set_power (val); } void set_defense (int val) override { player->set_defense (val); } void on_update () override { player->set_defense (20 ); player->on_update (); } private : Player* player; }; int main () { Player* player = new BasePlayer (); player = new PowerUpDecorator (player); player->on_update (); std::cout << player->get_power () << std::endl; std::cout << player->get_defense () << std::endl; player = new DefenseUpDecorator (player); player->on_update (); std::cout << player->get_power () << std::endl; std::cout << player->get_defense () << std::endl; player = new BasePlayer (); player->on_update (); std::cout << player->get_power () << std::endl; std::cout << player->get_defense () << std::endl; return 0 ; }
优点
无需创建新的子类来扩展功能
灵活,可以动态添加或删除装饰
符合单一职责原则
外观模式
可以为子系统提供一个简化的接口,来减少客户端与子系统之间的交互的复杂性
要素
外观类
子系统类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class CPU {public : void start () { } }; class Memory {public : void load () { } }; class ComputerFacade {private : CPU* cpu; Memory* memory; public : ComputerFacade () : cpu (new CPU ()), memory (new Memory ()) {} void start () { cpu->start (); memory->load (); } };
优点
简明,同时让代码独立于复杂子系统
享元模式
通过共享对象来支持大量细粒度的对象,减少内存使用和提高性能
要素
享元
享元工厂
上下文
示例代码仅完成了享元部分 示例代码(资源管理器头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #ifndef _RESOURCES_MANAGER_H_ #define _RESOURCES_MANAGER_H_ #include "font_wrapper.h" #include <SDL.h> #include <SDL_ttf.h> #include <SDL_mixer.h> #include <string> #include <vector> #include <functional> #include <filesystem> #include <unordered_map> class ResourcesManager { public : using ResIDList = std::vector<std::string>; public : static ResourcesManager* instance () { if (!manager) manager = new ResourcesManager (); return manager; } void load (SDL_Renderer* renderer) ; FontWrapper* find_font (const std::string& id) { return font_pool[id]; } Mix_Chunk* find_audio (const std::string& id) { return audio_pool[id]; } SDL_Texture* find_texture (const std::string& id) { return texture_pool[id]; } const ResIDList& get_font_resid_list () const { return font_resid_list; } const ResIDList& get_audio_resid_list () const { return audio_resid_list; } const ResIDList& get_texture_resid_list () const { return texture_resid_list; } private : static ResourcesManager* manager; SDL_Renderer* renderer = nullptr ; std::unordered_map<std::string, FontWrapper*> font_pool; std::unordered_map<std::string, Mix_Chunk*> audio_pool; std::unordered_map<std::string, SDL_Texture*> texture_pool; ResIDList font_resid_list, audio_resid_list, texture_resid_list; std::unordered_map<std::string, std::function<void (const std::filesystem::path& path)>> loader_pool; private : ResourcesManager (); ~ResourcesManager (); }; #endif
示例代码(资源管理器实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include "resources_manager.h" #include <SDL_image.h> ResourcesManager* ResourcesManager::manager = nullptr ; void ResourcesManager::load (SDL_Renderer* renderer) { using namespace std::filesystem; this ->renderer = renderer; for (const auto & entry : directory_iterator ("resources" )) { if (entry.is_regular_file ()) { const path& path = entry.path (); auto itor = loader_pool.find (path.extension ().u8string ()); if (itor != loader_pool.end ()) itor->second (path); } } } ResourcesManager::ResourcesManager () { loader_pool[".jpg" ] = loader_pool[".png" ] = [&](const std::filesystem::path& path) { SDL_Texture* texture = IMG_LoadTexture (renderer, path.u8string ().c_str ()); texture_resid_list.push_back (path.stem ().u8string ()); texture_pool[path.stem ().u8string ()] = texture; }; loader_pool[".wav" ] = loader_pool[".mp3" ] = [&](const std::filesystem::path& path) { Mix_Chunk* audio = Mix_LoadWAV (path.u8string ().c_str ()); audio_resid_list.push_back (path.stem ().u8string ()); audio_pool[path.stem ().u8string ()] = audio; }; loader_pool[".ttf" ] = loader_pool[".otf" ] = [&](const std::filesystem::path& path) { FontWrapper* font = new FontWrapper (path.u8string ().c_str ()); font_resid_list.push_back (path.stem ().u8string ()); font_pool[path.stem ().u8string ()] = font; }; } ResourcesManager::~ResourcesManager () { for (auto & pair : font_pool) delete pair.second; for (auto & pair : audio_pool) Mix_FreeChunk (pair.second); for (auto & pair : texture_pool) SDL_DestroyTexture (pair.second); }
优点
节省内存,减少对象创建
代理模式
通过代理对象来控制对原始对象的访问,提供一个替代品或占位符,并且可以将客户端提交的请求经过处理后转发给原始对象
要素
服务接口
实际服务
代理类
包含实际服务对象成员,代理完成任务的同时可以对请求进行预处理或后处理
示例代码(代理类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #ifndef _FONT_WRAPPER_H_ #define _FONT_WRAPPER_H_ #include <SDL_ttf.h> #include <string> #include <unordered_map> class FontWrapper { public : FontWrapper (const std::string path) { this ->path = path; } ~FontWrapper () { for (auto & pair : font_pool) TTF_CloseFont (pair.second); } TTF_Font* get_font (int size) { TTF_Font* font = font_pool[size]; if (!font) { font = TTF_OpenFont (path.c_str (), size); font_pool[size] = font; } return font; } private : std::string path; std::unordered_map<int , TTF_Font*> font_pool; }; #endif
示例代码(业务调用)
1 2 3 SDL_Surface* surface = TTF_RenderUTF8_Blended (ResourcesManager::instance () ->find_font ("SarasaMonoSC-Regular" )->get_font (font_size), str_text.c_str (), color);
优点
可以在客户端毫无察觉的情况下控制服务对象
实现对实际业务对象生命周期的智能管理
可以充当“占位符”,在服务对象还未准备好或不存在时正常工作
结构型设计模式对比
模式名称
核心目标
适配器
将一个类的接口转换成客户希望的另一个接口
桥接
分离抽象与实现,支持多维度扩展
组合
将对象组合成树形结构以表示“部分-整体”的层次结构
装饰器
动态地给一个对象添加一些额外的职责
外观
为子系统提供一个简化的接口
享元
通过共享对象来支持大量细粒度的对象
代理
通过代理对象来控制对原始对象的访问
行为型 责任链模式
将请求沿处理链传递,直到有对象处理它
允许请求沿着处理者链依次发送,每一个处理者收到请求后,可以对请求进行处理或者发送给下一个处理者
要素
请求
抽象处理者
具体处理者
实现具体的处理逻辑,并决定是否将请求传递给下一个处理者
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <iostream> class Request { public : int player_level = 10 ; int coin_num = 10 ; }; class PreAchievement { public : PreAchievement () = default ; ~PreAchievement () = default ; virtual void handle (Request* req) = 0 ; void set_next (PreAchievement* ach) { next = ach; } protected : PreAchievement* next; }; class PreLevel : public PreAchievement{ public : void handle (Request* req) override { const int pre_level = 5 ; if (req->player_level < pre_level) return ; next->handle (req); } }; class PreCoin : public PreAchievement{ public : void handle (Request* req) override { const int pre_coin_num = 10 ; if (req->coin_num < pre_coin_num) return ; std::cout << "获得成就:大佬" << std::endl; } }; int main () { Request* req = new Request (); PreAchievement* level = new PreLevel (); PreAchievement* coin = new PreCoin (); level->set_next (coin); level->handle (req); return 0 ; }
优点
灵活,请求处理的链式节点可以自由增删,通过组合一个链表数据结构来组合了动态的处理逻辑
请求的发送者无需关注具体的处理者,解耦了双端代码
符合开闭原则,新增处理者时不影响已有的处理者代码
命令模式
要素
接收者
命令接口
具体命令
发送者
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 #include <iostream> #include <vector> enum Direction { Up, Down, Left, Right }; class Command { public : virtual void execute () = 0 ; }; class Player { public : Player () = default ; ~Player () { for (Command* cmd : cmd_list) delete cmd; } void on_move (const Direction& dir) { switch (dir) { case Up: position.first--; break ; case Down: position.first++; break ; case Left: position.second--; break ; case Right: position.second++; break ; } cmd_list.emplace_back (new MoveCommand (this , dir)); } private : std::pair<int , int > position; std::vector<Command*> cmd_list; }; class MoveCommand : public Command{ public : MoveCommand (Player* player, const Direction& dir) { this ->player = player; this ->dir = dir; } public : void execute () override { player->on_move (dir); } private : Player* player; Direction dir = Direction::Up; }; class Replayer { public : Replayer (Player* player) { this ->player = player; } ~Replayer () = default ; void on_replay () { std::vector<Command*> list = player->get_cmd_list (); for (Command* cmd : list) { cmd->execute (); } } private : Player* player; };
优点
将请求发送者和请求接收者解耦
可以将请求(命令)参数化
可以支持撤销和重做操作
扩展性强
命令接口通常计较简单,当有新的命令对象时,无需修改接收者,发送者以及现有命令,只需添加新的命令类即可
迭代器模式
提供遍历集合的统一接口,允许在不暴露底层数据结构的前提下,遍历集合中所有的元素
要素
迭代器
具体迭代器
集合
具体集合
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 #include <iostream> #include <vector> #include <string> #include <algorithm> class Player { public : Player (std::string id, int level) : id (id), level (level) {} ~Player () = default ; const std::string& get_id () const { return id; } int get_level () const { return level; } private : std::string id; int level; }; class Iterator { public : Iterator (const std::vector<Player*>& list) { player_list = std::vector <Player*>(list); } ~Iterator () { for (Player* p : player_list) delete p; } virtual bool has_next () = 0 ; virtual Player* next () = 0 ; protected : std::vector<Player*> player_list; size_t index; }; class CheckIdIt : public Iterator{ public : CheckIdIt (const std::vector<Player*>& list) : Iterator (list) { std::sort (player_list.begin (), player_list.end (), [](const Player* t_1, const Player* t_2) { return t_1->get_id () < t_2->get_id (); }); } bool has_next () override { return player_list.size () > index; } Player* next () override { if (!has_next ()) return nullptr ; return player_list[index++]; } }; class CheckLevelIt : public Iterator{ public : CheckLevelIt (const std::vector<Player*>& list) : Iterator (list) { std::sort (player_list.begin (), player_list.end (), [](const Player* t_1, const Player* t_2) { return t_1->get_level () > t_2->get_level (); }); } bool has_next () override { return player_list.size () > index; } Player* next () override { if (!has_next ()) return nullptr ; return player_list[index++]; } }; class Collection { public : virtual Iterator* create_iterator (std::string itor_type) = 0 ; }; class PlayerList : public Collection{ public : PlayerList () { player_list.emplace_back (new Player ("ZhangSan" , 10 )); player_list.emplace_back (new Player ("LiSi" , 8 )); player_list.emplace_back (new Player ("WangWu" , 16 )); player_list.emplace_back (new Player ("ZhaoLiu" , 12 )); } ~PlayerList () { for (Player* p : player_list) delete p; } Iterator* create_iterator (std::string itor_type) override { if (itor_type == "level" ) return new CheckLevelIt (player_list); else if (itor_type == "id" ) return new CheckIdIt (player_list); return nullptr ; } private : std::vector<Player*> player_list; }; int main () { Collection* player_list = new PlayerList (); Iterator* id_itor = player_list->create_iterator ("id" ); while (id_itor->has_next ()) { std::cout << id_itor->next ()->get_id () << " " ; } std::cout << std::endl; Iterator* level_itor = player_list->create_iterator ("level" ); while (level_itor->has_next ()) { std::cout << level_itor->next ()->get_level () << " " ; } std::cout << std::endl; return 0 ; }
优点
隐藏集合内部复杂的细节,更加便利和安全
减少程序中重复的遍历代码
统一的接口,可以应对不同的和未来无法预知的数据结构
允许对同一集合实现不同的遍历逻辑
中介者模式
限制对象之间的彼此交互,强迫他们通过中介者对象进行合作通信
解决需要互相通信的两个对象之间必须依赖彼此
要素
抽象组件
具体组件
抽象中介者
通常仅包含一个通知方法本身并不一个规定太多复杂行为
具体中介者
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <iostream> #include <string> #include <vector> class Colleague ;class Mediator { public : virtual void send (const std::string& message, Colleague* sender) = 0 ; }; class Colleague { public : Colleague (Mediator* mediator) : mediator (mediator) {} virtual void receive (const std::string& message) = 0 ; protected : Mediator* mediator; }; class ChatRoom : public Mediator{ public : void add_colleague (Colleague* colleague) { colleague_list.emplace_back (colleague); } void send (const std::string& message, Colleague* sender) override { for (Colleague* colleague : colleague_list) { if (colleague != sender) { colleague->receive (message); } } } private : std::vector<Colleague*> colleague_list; }; class User : public Colleague{ public : User (Mediator* mediator, const std::string& name) : Colleague (mediator), name (name) {} void send (const std::string& message) { mediator->send (message, this ); } void receive (const std::string& message) override { std::cout << name << "接收到:" << message << std::endl; } private : std::string name; }; int main () { ChatRoom chat_room; User* zhangsan = new User (&chat_room, "ZhangSan" ); User* lisi = new User (&chat_room, "LiSi" ); User* wangwu = new User (&chat_room, "WangWu" ); chat_room.add_colleague (zhangsan); chat_room.add_colleague (lisi); chat_room.add_colleague (wangwu); zhangsan->send ("im zhangsan" ); return 0 ; }
优点
减少类间依赖
可以管理各个组件的生命周期
思考
代理模式和中介者模式
代理模式是使用代理对象控制对目标对象的直接访问,实现缓存,延迟加载以及权限控制等功能
中介者模式着重解决的痛点是不同组件(对象)之间的通信问题,程序层面通信总是要落实到某个接口函数的调用,中介者模式抽象出第三者代替完成这项任务
备忘录模式
允许在不暴露对象实现细节的情况下,保存和恢复对象的状态
要素
备忘录
发起人
管理者
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include <iostream> #include <unordered_map> #include <vector> class Memento { public : Memento (const std::pair<int , int >& position, float speed, int health) : position (position), speed (speed), health (health) {} const std::pair<int , int > get_position () const { return position; } float get_speed () const { return speed; } int get_health () const { return health; } private : std::pair<int , int > position; float speed; int health; }; class Player { public : void set_position (const std::pair<int , int >& pos) { position = pos; } void set_speed (float val) { speed = val; } void set_health (int val) { health = val; } Memento save_state () const { return Memento (position, speed, health); } void restone_memento (const Memento& memento) { position = memento.get_position (); speed = memento.get_speed (); health = memento.get_health (); } private : std::pair<int , int > position = { 0 ,0 }; float speed = 2.0f ; int health = 10 ; }; class Caretaker { public : void save_memento (const Memento& memento) { history.emplace_back (memento); } Memento get_memento (int index) const { return history[index]; } private : std::vector<Memento> history; }; int main () { Player player; player.set_position ({ 10 ,10 }); player.set_speed (0.5f ); player.set_health (20 ); Caretaker caretaker; caretaker.save_memento (player.save_state ()); player.set_position ({ 0 ,0 }); player.set_speed (2.0f ); player.set_health (10 ); player.restone_memento (caretaker.get_memento (0 )); return 0 ; }
优点
可以在不暴露对象实现细节的情况下保存和恢复对象的状态
符合单一职责原则
符合开闭原则
思考
备忘录模式和原型模式
原型模式可以看作是备忘录模式的一种退化,原型模式克隆了对象的所有列表,备忘录模式可以选择性的克隆对象的状态
备忘录模式和命令模式
备忘录模式是通过必要的字段,对对象的状态记录
命令模式是对行为的记录,实现撤销和重做操作时相当于是对行为的逐一调用,对对象进行增量的修改与恢复
观察者模式
定义了一种一对多的依赖关系,当一个对象状态发生变化时,所有依赖于它的对象都会得到通知并自动更新
要素
主题
观察者
具体主题
具体观察者
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 #include <iostream> #include <vector> #include <string> class Observer { public : virtual void on_update (int state) = 0 ; }; class Subject { public : virtual void attach (Observer* observer) = 0 ; virtual void detach (Observer* observer) = 0 ; virtual void notify () = 0 ; }; class ConcreteSubject : public Subject{ public : void attach (Observer* observer) override { observer_list.emplace_back (observer); } void detach (Observer* observer) override { observer_list.erase (remove (observer_list.begin (), observer_list.end (), observer), observer_list.end ()); } void notify () override { for (auto & observer : observer_list) { observer->on_update (state); } } void set_state (int val) { state = val; notify (); } private : std::vector<Observer*> observer_list; int state = 0 ; }; class ConcreteObserver : public Observer{ public : ConcreteObserver (const std::string& name) : name (name) {} void on_update (int state) override { std::cout << name << ":监听到新状态:" << state << std::endl; } private : std::string name; }; int main () { ConcreteSubject sub; ConcreteObserver ob_1 ("ob_1" ) ; ConcreteObserver ob_2 ("ob_2" ) ; ConcreteObserver ob_3 ("ob_3" ) ; sub.attach (&ob_1); sub.attach (&ob_2); sub.attach (&ob_3); sub.set_state (1 ); return 0 ; }
优点
状态模式
允许对象在其内部状态改变时改变它的行为,就像是改变了自身的类一样
要素
状态接口
具体状态
上下文
持有当前状态对象指针(若有状态机则存入状态机中),若没有状态机则提供方法来切换状态
状态机(可选)
可以将状态机的状态和转换逻辑封装在一个类中,简化上下文的实现
示例代码(没有状态机)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <iostream> class State { public : virtual void on_update () = 0 ; virtual void on_exit () = 0 ; virtual void on_enter () = 0 ; }; class Run : public State{ public : void on_update () override { std::cout << "正在奔跑" << std::endl; } void on_exit () override { std::cout << "退出run" << std::endl; } void on_enter () override { std::cout << "进入run" << std::endl; } }; class Attack : public State{ public : void on_update () override { std::cout << "正在攻击" << std::endl; } void on_exit () override { std::cout << "退出attack" << std::endl; } void on_enter () override { std::cout << "进入attack" << std::endl; } }; class Player { public : Player (State* state) : current_state (state) {} void update () { current_state->on_update (); } void set_state (State* state) { current_state->on_exit (); current_state = state; current_state->on_enter (); } private : State* current_state; };
示例代码(有状态机)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include <iostream> class State { public : virtual void on_update () = 0 ; virtual void on_exit () = 0 ; virtual void on_enter () = 0 ; }; class Run : public State{ public : void on_update () override { std::cout << "正在奔跑" << std::endl; } void on_exit () override { std::cout << "退出run" << std::endl; } void on_enter () override { std::cout << "进入run" << std::endl; } }; class Attack : public State{ public : void on_update () override { std::cout << "正在攻击" << std::endl; } void on_exit () override { std::cout << "退出attack" << std::endl; } void on_enter () override { std::cout << "进入attack" << std::endl; } }; class StateMachine { public : void set_entry (State* state) { current_state = state; } void switch_state (State* state) { current_state->on_exit (); current_state = state; current_state->on_enter (); } void on_update () { current_state->on_update (); } private : State* current_state; }; class Player { public : Player () { machine.set_entry (new Run ()); } void update () { machine.on_update (); } void switch_state (State* state) { machine.switch_state(state); } private : StateMachine machine; };
优点
简化代码
抛弃 if…else 和 switch 语句,避免了复杂的条件判断
易于扩展
新增状态时只需添加新的状态类,无需修改现有的状态类和上下文类
符合开闭原则
符合单一职责原则
策略模式
封装一组可互换的算法,使它们可以独立于客户端变化
通过组合(策略对象注入)动态替换算法,而不是通过继承
要素
策略接口
具体策略
上下文
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> class Strategy { public : virtual void move () = 0 ; }; class FollowStrategy : public Strategy{ public : void move () override { std::cout << "follow player" << std::endl; } }; class StayAwayStrategy : public Strategy{ public : void move () override { std::cout << "stay away player" << std::endl; } }; class Enemy { public : void on_update () { strategy->move (); } void set_strategy (Strategy* strategy) { this ->strategy = strategy; } private : Strategy* strategy; };
优点
符合开闭原则
符合单一职责原则
可以动态的切换算法,对同类型调用对象形成差异化
思考
策略模式和状态模式
策略模式封装一组可换的算法,客户端可以根据需求选择不同策略。策略是主动选择的,策略之间彼此独立,通常不感知其他策略的存在
状态模式根据对象的内部状态改变其行为,使对象在不同状态下表现不同。状态是被动切换的,状态之间可能相互关联
模板方法模式
定义一个算法的骨架,子类重写骨架中某些步骤而不改变算法的结构
相当于多态的一种实现方式
要素
抽象类
具体类
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <iostream> #include <vector> #include <unordered_map> #include <random> class Item { public : Item (const std::pair<int , int >& pos) { this ->pos = pos; } const std::pair<int , int >& get_pos () const { return pos; } private : std::pair<int , int > pos; }; class Enemy : public Item{ public : Enemy (const std::pair<int , int >& pos) : Item (pos) {} }; class Barrier : public Item{ public : Barrier (const std::pair<int , int >& pos) : Item (pos) {} }; class Coin : public Item{ public : Coin (const std::pair<int , int >& pos) : Item (pos) {} }; class Map { public : Map () { map_content = std::vector<std::vector<Item*>>(10 , std::vector <Item*>(10 , nullptr )); } ~Map () { for (const std::vector<Item*>& line : map_content) for (Item* item : line) delete item; } void add_item (Item* item) { delete map_content[item->get_pos ().first][item->get_pos ().second]; map_content[item->get_pos ().first][item->get_pos ().second] = item; } private : std::vector<std::vector<Item*>> map_content; }; class MapGenTemplate { virtual void generate_barrier (Map* map) = 0 ; virtual void generate_enemy (Map* map) = 0 ; virtual void generate_coin (Map* map) = 0 ; }; class CustomizedMapGen : public MapGenTemplate{ void generate_barrier (Map* map) override { map->add_item (new Barrier ({ rand () % 10 , rand () % 10 })); } void generate_enemy (Map* map) override { map->add_item (new Enemy ({ rand () % 10 , rand () % 10 })); } void generate_coin (Map* map) override { map->add_item (new Coin ({ rand () % 10 , rand () % 10 })); } };
优点
要素简单,结构简单
符合开闭原则
思考
模板方法模式和策略模式
模板方法模式中,继承(父类定义模板方法)是实现扩展的主要手段,父类复用公共代码,子类实现差异步骤,允许子类重写部分步骤
策略模式中,组合(策略对象注入)是实现扩展的主要手段,策略类之间独立,无复用关系
访问者模式
将数据结构和作用于数据结构上的操作分离
思想上可以看作是将原属于成员方法的部分分离到类外部(访问者类)进行独立实现
要素
访问者接口
具体访问者
元素接口
具体元素
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <iostream> class Enemy ;class Chest ;class Visitor { public : virtual void click (Enemy& enemy) = 0 ; virtual void click (Chest& chest) = 0 ; }; class Element { public : virtual void accept (Visitor& visitor) = 0 ; }; class Enemy : public Element{ public : void accept (Visitor& visitor) override { visitor.click (*this ); } public : int health = 20 ; }; class Chest : public Element{ public : void accept (Visitor& visitor) override { visitor.click (*this ); } public : bool is_locked = true ; }; class MouseClickedVisitor : public Visitor{ public : void click (Enemy& enemy) override { enemy.health -= 5 ; std::cout << "怪物遭受攻击" << std::endl; } void click (Chest& chest) override { if (chest.is_locked) { chest.is_locked = true ; std::cout << "宝箱打开了" << std::endl; } } };
优点
符合开闭原则
将处理逻辑集中放置,提高可读性降低维护成本
解释器模式
用于定义一种语言的文法,并提供一个解释器来处理该语言的句子
通常用于编译器和脚本语言的实现
优点
灵活度高
易于扩展
解释器本身与表达式完全解耦,可以独立升级提供新的特性
行为型设计模式对比
模式名称
核心目标
责任链模式
将请求沿着处理者链传递,直到找到合适的处理者
命令模式
将请求封装为对象,允许参数化和撤销操作
迭代器模式
提供遍历集合的统一接口,允许在不暴露底层数据结构的前提下,遍历集合中所有的元素
中介者模式
限制对象之间的彼此交互,强迫他们通过中介者对象进行合作通信
备忘录模式
允许在不暴露对象实现细节的情况下,保存和恢复对象的状态
观察者模式
定义了一种一对多的依赖关系,当一个对象状态发生变化时,所有依赖于它的对象都会得到通知并自动更新
状态模式
允许对象在其内部状态改变时改变它的行为,就像是改变了自身的类一样
策略模式
封装一组可互换的算法,使它们可以独立于客户端变化
模板方法模式
定义一个算法的骨架,子类重写骨架中某些步骤而不改变算法的结构
访问者模式
将数据结构和作用于数据结构上的操作分离
解释器模式
用于定义一种语言的文法,并提供一个解释器来处理该语言的句子