学习设计模式

设计模式分类

  1. 创造型
  • 主要关注创造对象的过程,隐藏创造逻辑
  1. 结构型
  • 主要管理类和对象的组合,形成较大的结构的同时保持结构的灵活性和高效性
  1. 行为型
  • 主要优化对象间的交互和职责分配

创造型

工厂方法模式

  • 定义创建对象的接口,但由子类决定具体实例化的类。将对象的创建延迟到子类

要素

  1. 抽象产品
  • 产品基类
  1. 具体产品
  • 继承抽象产品,即要生产的对象
  1. 抽象工厂/创建者
  • 工厂基类,提供统一的抽象创造方法(接口),由具体工厂实现(重写)(可省略?)
  1. 具体工厂
  • 继承抽象工厂,实现(重写)抽象工厂的方法(接口),根据条件去创造具体产品

示例代码

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. 解耦客户端与具体类依赖
  • 将产品的代码与使用该产品的客户端内容进行分离
  • 譬如在游戏开发中,玩家类只需要依赖具体工厂类,无需关注产品类内部实现与构造需求
  1. 提高系统的可扩展性
  • 引入新产品无需修改已有代码
  • 引入新产品时,只需要构建新的具体工厂类
  1. 封装复杂的对象创建逻辑
  • 工厂内部屏蔽对象创建和初始化的复杂操作
  1. 遵循开闭原则和单一职责原则

  2. 提高可维护性

  • 将对象创建的代码逻辑集中在一起方便后续修改维护

抽象工厂方法

要素

  1. 抽象产品
  • 提供一个接口,用于创建相关或依赖对象的家族,而无需指定具体类。
  1. 具体产品

  2. 抽象工厂

  3. 具体工厂

示例代码

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. 工厂模式方法基础上,有更多维度的定制化

建造者模式

  • 可以叫做“生成器模式”,通常在复杂对象的构造中,把对象构造的逻辑从其本身的类中抽离出来,让专门的生成器来负责

要素

  1. 目标产品

  2. 抽象生成器

  • 定义建造者的抽象接口
  1. 具体生成器

  2. 导演/主管

  • 代理调用生成器的各个建造步骤,如果对生成的步骤没有严格要求就可以省略

示例代码

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. 简化构造参数
  • 拆分单个类的构造任务到多个生成器步骤,避免构造产品对象时需要过多的构造参数
  1. 灵活组合步骤
  • 解耦构造过程,从而使用组合灵活地产出更多变种的产品
  1. 动态感知构造过程
  • 导演类可以看作是对构造器的屏蔽与封装,借助导演类可以在运行时根据建造过程调整各部分任务,暂缓创建或执行递归构造

原型模式

  • 通过复制现有对象来创建新对象,而不是通过实例化类来创建新对象
  • 适用于创建大量相似对象的场景,避免了重复的初始化和构造过程

要素

  1. 原型接口

  2. 具体原型

示例代码

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. 动态生成对象副本,支持运行时配置

思考

  1. 为何不直接使用拷贝构造函数
  • 拷贝构造函数是静态实现,要使用拷贝构造函数时需要知道具体的类;而原型模式是动态实现的,使用原型模式时只需要知道原型接口即可
  • 拷贝构造函数涉及到浅拷贝和深拷贝,除去内存上的问题,若不保留原型直接使用场内原有的对象进行拷贝,容易拷贝多余的对象状态造成污染
  1. 为何不能在类外手动调用拷贝类内字段
  • 私有字段不宜访问,会产生依赖;而原型模式可以通过原型接口来实现对类内字段的访问和操作
  • 面对复杂字段的对象非常繁琐,会增加人为失误的可能
  • 这种方法同样需要知晓具体子类,产生不必要的依赖

单例模式

  • 确保一个类只有一个实例,并提供一个全局访问点,为静态变量的生命周期。分为懒汉模式和饿汉模式
  • 懒汉模式:在类加载时单例对象为空,在第一次使用时创建实例,对比饿汉模式更加节省空间,可能存在多线程隐患
  • 饿汉模式:在类加载时创建实例,对外提供静态方法来获取单例对象的引用

要素

  1. 单例对象

示例代码(懒汉模式)

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. 严格保证只有一个实例
  • 通过私有化构造函数和静态方法来控制实例的创建

创造型设计模式对比

模式名称 核心目标
工厂方法 子类决定实例化哪个类
抽象工厂 创建相关或依赖对象的家族,而无需指定具体类
建造者 分步骤构建复杂对象
原型 通过复制现有对象来创建新对象
单例 确保一个类只有一个实例,并提供一个全局访问点

结构型

适配器模式

  • 将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作
  • 利用继承适配不同接口

要素

  1. 目标接口
  • 客户需要调用的接口
  1. 原接口
  • 需要由适配器适配(兼容)的接口
  1. 适配器类
  • 提供兼容层,避免客户端代码直接依赖不同的对象
  • 可以对底层的输入设备指令进行翻译,对客户端暴露更贴近逻辑的接口

示例代码

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. 灵活,可以方便地增删适配器

桥接模式

  • 将抽象部分与实现部分分离,使它们可以独立地变化

要素

  1. 抽象类
  2. 扩展抽象类
  3. 实现类接口
  4. 具体实现类

示例代码

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. 隐藏了实现细节

思考

  1. 桥接模式和工厂方法模式
  • 工厂方法模式关注的是对象的创建,根据不同条件动态创建不同产品
  • 桥接模式关注的是对象的实现和抽象的分离,两者可以独立变化实现解耦
  • 二者可以结合使用,工厂方法模式可以用于创建桥接模式中的实现类,而桥接模式可以用于创建工厂方法模式中的抽象类

示例代码

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");
}
};

// 使用工厂模式创建Renderer对象
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. 桥接模式和适配器模式
  • 桥接模式关注抽象与实现分离,通过组合来代替继承,允许独立变化
  • 桥接模式类似于将实现类作为适配器模式的客户端,在适配器模式上进一步地扩展
  • 适配器模式关注接口的转换(统一),通过继承的方式来实现不同接口之间的适配

组合模式

  • 将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性

要素

  1. 组件接口
  • 定义所有叶子节点和复合节点的公共接口
  1. 叶子节点
  • 具体的对象,不能再包含其他对象,类似于数据结构中树的叶子节点
  1. 复合节点
  • 包含一个或多个子节点的对象,提供子树的管理接口,客户端只需要关注最顶层的复合节点即可

示例代码

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. 支持递归组合,灵活扩展结构

装饰器模式

  • 动态地给一个对象添加一些额外的职责,装饰模式提供了比继承更灵活的替代方案
  • 装饰器模式可以在不改变对象自身的情况下,动态地添加功能

要素

  1. 组件接口
  2. 具体组件
  3. 装饰器接口
  4. 具体装饰器

示例代码

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. 符合单一职责原则

外观模式

  • 可以为子系统提供一个简化的接口,来减少客户端与子系统之间的交互的复杂性

要素

  1. 外观类
  • 提供统一接口,供客户端调用
  1. 子系统类
  • 具体实现类,提供具体的功能实现
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
// 子系统类:CPU
class CPU {
public:
void start() { /* 启动 CPU */ }
};

// 子系统类:内存
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. 简明,同时让代码独立于复杂子系统

享元模式

  • 通过共享对象来支持大量细粒度的对象,减少内存使用和提高性能

要素

  1. 享元
  • 多个对象共享的数据或状态
  1. 享元工厂
  • 负责创建和管理享元对象,确保共享的对象是唯一的
  1. 上下文
  • 享元对象的外部状态,通常是不可共享的状态

示例代码仅完成了享元部分
示例代码(资源管理器头文件)

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> // SDL2内容
#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 // !_RESOURCES_MANAGER_H_

示例代码(资源管理器实现文件)

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. 节省内存,减少对象创建

代理模式

  • 通过代理对象来控制对原始对象的访问,提供一个替代品或占位符,并且可以将客户端提交的请求经过处理后转发给原始对象

要素

  1. 服务接口
  • 代理类伪装所必须遵循的规则
  1. 实际服务
  • 实际用于提供业务逻辑的类
  1. 代理类
  • 包含实际服务对象成员,代理完成任务的同时可以对请求进行预处理或后处理

示例代码(代理类)

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
// 由于需要使用不同大小的相同字体,TTF_Font类不适合直接使用
// 需要一个字体池来缓存TTF_Font类的实例,避免重复加载
#ifndef _FONT_WRAPPER_H_
#define _FONT_WRAPPER_H_

#include <SDL_ttf.h>

#include <string>
#include <unordered_map>

// 替代了TTF_Font类
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 // !_FONT_WRAPPER_H_

示例代码(业务调用)

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. 可以充当“占位符”,在服务对象还未准备好或不存在时正常工作

结构型设计模式对比

模式名称 核心目标
适配器 将一个类的接口转换成客户希望的另一个接口
桥接 分离抽象与实现,支持多维度扩展
组合 将对象组合成树形结构以表示“部分-整体”的层次结构
装饰器 动态地给一个对象添加一些额外的职责
外观 为子系统提供一个简化的接口
享元 通过共享对象来支持大量细粒度的对象
代理 通过代理对象来控制对原始对象的访问

行为型

责任链模式

  • 将请求沿处理链传递,直到有对象处理它
  • 允许请求沿着处理者链依次发送,每一个处理者收到请求后,可以对请求进行处理或者发送给下一个处理者

要素

  1. 请求
  • 记录了责任链上的处理者所关心的数据
  1. 抽象处理者
  • 定义处理的接口和设置后继处理者的方法
  1. 具体处理者
  • 实现具体的处理逻辑,并决定是否将请求传递给下一个处理者

示例代码

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. 符合开闭原则,新增处理者时不影响已有的处理者代码

命令模式

  • 把客户端的调用行为封装成一个对象

要素

  1. 接收者
  • 接收具体的任务类
  1. 命令接口
  • 规定所有的具体命令类必须实现的接口
  1. 具体命令
  • 实现命令接口,调用接收者的具体方法
  1. 发送者
  • 用来触发命令调用,也被叫做触发者

示例代码

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();

// 根据list回放操作
for (Command* cmd : list)
{
cmd->execute();
}
}

private:
Player* player;
};

// 当前代码Player和MoveCommand相互包含,可以新建一个“存储类”来存储命令列表

优点

  1. 将请求发送者和请求接收者解耦
  2. 可以将请求(命令)参数化
  3. 可以支持撤销和重做操作
  4. 扩展性强
  • 命令接口通常计较简单,当有新的命令对象时,无需修改接收者,发送者以及现有命令,只需添加新的命令类即可

迭代器模式

  • 提供遍历集合的统一接口,允许在不暴露底层数据结构的前提下,遍历集合中所有的元素

要素

  1. 迭代器
  • 接口声明了获取下一元素等必要的操作
  1. 具体迭代器
  • 实现遍历集合的一种算法并记录自己的进度
  1. 集合
  • 接口声明获取迭代器的方法
  1. 具体集合
  • 实现返回具体迭代器的接口

示例代码

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. 允许对同一集合实现不同的遍历逻辑

中介者模式

  • 限制对象之间的彼此交互,强迫他们通过中介者对象进行合作通信
  • 解决需要互相通信的两个对象之间必须依赖彼此

要素

  1. 抽象组件
  2. 具体组件
  3. 抽象中介者
  • 通常仅包含一个通知方法本身并不一个规定太多复杂行为
  1. 具体中介者
  • 持有各个组件的引用,有时还要管理组件的生命周期

示例代码

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. 可以管理各个组件的生命周期

思考

  1. 代理模式和中介者模式
  • 代理模式是使用代理对象控制对目标对象的直接访问,实现缓存,延迟加载以及权限控制等功能
  • 中介者模式着重解决的痛点是不同组件(对象)之间的通信问题,程序层面通信总是要落实到某个接口函数的调用,中介者模式抽象出第三者代替完成这项任务

备忘录模式

  • 允许在不暴露对象实现细节的情况下,保存和恢复对象的状态

要素

  1. 备忘录
  2. 发起人
  3. 管理者
  • 管理备忘录列表,提供保存和恢复的接口

示例代码

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. 符合开闭原则

思考

  1. 备忘录模式和原型模式
  • 原型模式可以看作是备忘录模式的一种退化,原型模式克隆了对象的所有列表,备忘录模式可以选择性的克隆对象的状态
  1. 备忘录模式和命令模式
  • 备忘录模式是通过必要的字段,对对象的状态记录
  • 命令模式是对行为的记录,实现撤销和重做操作时相当于是对行为的逐一调用,对对象进行增量的修改与恢复

观察者模式

  • 定义了一种一对多的依赖关系,当一个对象状态发生变化时,所有依赖于它的对象都会得到通知并自动更新

要素

  1. 主题
  2. 观察者
  3. 具体主题
  • 允许添加移除观察者,并能在状态变化时通知观察者
  1. 具体观察者

示例代码

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; //定义更新逻辑,state是需要观察的主题某个状态
};

// 主题
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) // 更新调用notify
{
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. 上下文
  • 持有当前状态对象指针(若有状态机则存入状态机中),若没有状态机则提供方法来切换状态
  1. 状态机(可选)
  • 可以将状态机的状态和转换逻辑封装在一个类中,简化上下文的实现

示例代码(没有状态机)

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; // 使用状态机
};

优点

  1. 简化代码
  • 抛弃 if…else 和 switch 语句,避免了复杂的条件判断
  1. 易于扩展
  • 新增状态时只需添加新的状态类,无需修改现有的状态类和上下文类
  1. 符合开闭原则
  2. 符合单一职责原则

策略模式

  • 封装一组可互换的算法,使它们可以独立于客户端变化
  • 通过组合(策略对象注入)动态替换算法,而不是通过继承

要素

  1. 策略接口
  2. 具体策略
  3. 上下文
  • 包含策略对象,提供方法来设置策略对象

示例代码

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. 可以动态的切换算法,对同类型调用对象形成差异化

思考

  1. 策略模式和状态模式
  • 策略模式封装一组可换的算法,客户端可以根据需求选择不同策略。策略是主动选择的,策略之间彼此独立,通常不感知其他策略的存在
  • 状态模式根据对象的内部状态改变其行为,使对象在不同状态下表现不同。状态是被动切换的,状态之间可能相互关联

模板方法模式

  • 定义一个算法的骨架,子类重写骨架中某些步骤而不改变算法的结构
  • 相当于多态的一种实现方式

要素

  1. 抽象类
  2. 具体类

示例代码

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. 符合开闭原则

思考

  1. 模板方法模式和策略模式
  • 模板方法模式中,继承(父类定义模板方法)是实现扩展的主要手段,父类复用公共代码,子类实现差异步骤,允许子类重写部分步骤
  • 策略模式中,组合(策略对象注入)是实现扩展的主要手段,策略类之间独立,无复用关系

访问者模式

  • 将数据结构和作用于数据结构上的操作分离
  • 思想上可以看作是将原属于成员方法的部分分离到类外部(访问者类)进行独立实现

要素

  1. 访问者接口
  • 允许访问具体元素的接口
  1. 具体访问者
  • 实现接口逻辑,定义对具体元素的实际操作
  1. 元素接口
  • 约定允许访问器访问的接口
  1. 具体元素
  • 实现接收访问的具体逻辑,调用访问者对应方法

代码示例

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;
}
}
};

优点

  1. 符合开闭原则
  • 新增操作(访问者)时不需要修改元素类的代码
  1. 将处理逻辑集中放置,提高可读性降低维护成本

解释器模式

  • 用于定义一种语言的文法,并提供一个解释器来处理该语言的句子
  • 通常用于编译器和脚本语言的实现

优点

  1. 灵活度高
  • 用户可以任意扩展自己的逻辑
  1. 易于扩展
  • 解释器本身与表达式完全解耦,可以独立升级提供新的特性

行为型设计模式对比

模式名称 核心目标
责任链模式 将请求沿着处理者链传递,直到找到合适的处理者
命令模式 将请求封装为对象,允许参数化和撤销操作
迭代器模式 提供遍历集合的统一接口,允许在不暴露底层数据结构的前提下,遍历集合中所有的元素
中介者模式 限制对象之间的彼此交互,强迫他们通过中介者对象进行合作通信
备忘录模式 允许在不暴露对象实现细节的情况下,保存和恢复对象的状态
观察者模式 定义了一种一对多的依赖关系,当一个对象状态发生变化时,所有依赖于它的对象都会得到通知并自动更新
状态模式 允许对象在其内部状态改变时改变它的行为,就像是改变了自身的类一样
策略模式 封装一组可互换的算法,使它们可以独立于客户端变化
模板方法模式 定义一个算法的骨架,子类重写骨架中某些步骤而不改变算法的结构
访问者模式 将数据结构和作用于数据结构上的操作分离
解释器模式 用于定义一种语言的文法,并提供一个解释器来处理该语言的句子