Design mode / 设计模式

没有任何稳定点的软件体系结构,设计模式没有意义

任何点都稳定的软件体系结构,设计模式也没有意义

从变化点和稳定点中寻找隔离点,分离后管理变化

编译时稳定复用

运行时多态变化

设计原则

应对变化 提高复用

二进制单位意义的复用,而不是代码片段级粘贴性质的复用

二进制单位意义的复用无需重新编译部署

依赖倒置原则 -DIP

变化应该依赖于稳定,稳定不应该依赖于变化

高层模块不应该依赖于低层模块,二者都可以依赖于抽象

抽象不应该依赖于实现,实现细节可以依赖于抽象

开闭原则 -OCP

拓展开放,对修改封闭

类模块应该是可拓展的,但是不可修改

类应该以拓展方式来应对需求变化,而不是更改类中代码

单一职责原则 -SRP

一个类应该仅有一个引起它变化的原因

桥模式

接口隔离原则 -ISP

不能让用户依赖于他们不使用的方法

就是一个类实现了多个接口,其中用户用不到的方法使用private等限定隔离

里氏替换原则 -LSP

子类型必须能替换掉它的父类型 IS-A

能使用父类对象的方法子类也能适用

假设有两个类,一个是Base类,另一个是Child类,并且Child类是Base的子类。那么一个方法如果可以接受一个基类对象b的话:method1(Base b)那么它必然可以接受一个子类的对象method1(Child c)

组合优先于继承

组合为黑盒复用,继承是白盒复用

继承一定程度上破坏了封装性,子类父类耦合度高

封装变化点

一侧变化,一侧稳定

针对接口编程,而不是针对实现编程

不将变量类型声明为某个特定的具体类,而是声明为某个接口

客户程序无需知道对象的具体类型,只需要知道对象所具有的接口

模式分类

从目的来看

  • 创建型模式

将对象的部分创建工作延迟到子类

应对需求变化为对象创建具体类型的实现带来的冲击

  • 结构型模式

通过类继承或者对象组合的结构

应对需求变化对对象结构带来的冲击

  • 行为型模式

划分对象间的职责

应对需求变化为多个交互对象带来的冲击

封装变化

​ ↓

“组件协作”模式

通过晚期绑定

实现框架应用程序之间的松耦合

Template Method 模式

Motivation

稳定操作框架结构,而其中某些子步骤有变化需求

无法实现任务和整体结构的同步

Defination

Template Method 操作算法骨架(稳定) -> 骨架中某些步骤延迟到子类实现(变化)

从而子类可以复用算法骨架或者重写算法中某些特定步骤

延迟

定义一个虚函数让子类实现or重写,从而支持子类变化

Code and UML

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
// libiary and Application
class libiary {

public:
//稳定的函数不能声明为虚函数
void template_method() {
//...
var();
}

//若不声明为虚函数,无法调用子类的构造函数
virtual ~libiary() {};
protected:
//变化的函数声明为虚函数
virtual void var();

};

class Application : public libiary {
virtual void var() {};
};

void main() {
//多态调用
libiary* p = new Application();
p->template_method();
}

-

image-20210719103524862

Strategy 模式

Motivation

一个类中包含多个算法

不再使用的算法成为性能负担

拓展算法时必须于类中添加代码,违反开闭原则

客户程序段存在 **if-else散弹 ** - bad smell

Defination

单独封装算法,算法可相互替换

算法可独立于客户程序(稳定)而变化(拓展,子类化)

拓展的方式面对算法方面需求的变化

Code and UML

1
// 税率计算

image-20210721084310497

Observer 模式

Motivation

过于紧密的通知依赖关系不能很好地抵御变化

违反依赖倒置原则

Defination

弱化后的一对多的依赖中,一个对象发生变化,所有依赖他的对象都可以得到通知自动更新

Code and UML

1
2
3
4
5
//GUI
//通过稳定的Observer基类,接口来弱化耦合关系
//观察者需要通过addListenser注册成为"监听对象",并实现Observer接口从而具有自动更新的能力
//目标对象中含有某种容器存放各个观察者对象,注册实际上就是push_back
//目标对象发送通知notify方法其实是遍历容器调用接口中update()抽象方法(因此观察者对象必须实现Observer接口)

image-20210721083330460

“单一职责”模式

责任划分不清晰,继承拓展使得子类急剧膨胀

Decorator 模式

Motivation

过度使用继承拓展对象功能,使得子类过度膨胀

继承为继承静态特质,拓展不够灵活

编译时装配

运行时装配

拓展功能增多

各种组合功能

eg:

image-20210721100237043

Defination

组合代替继承

解决主体类在多个方向上的拓展

装饰的含义是在已有功能已有对象的基础上拓展

创建拓展对象的时候需要绑定 (运行时) 主体类对象

黑盒复用,因多态存在,可通过复合基类指针来实现不同方向拓展功能的组合,灵活度高

eg: 如 File 与 Crypto 是两个方向的功能拓展 本就不应该使用继承方式表示关系

image-20210721100207892

Code and UML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//功能stream
//继承体系中,子类相同的字段应该向上提取,Decorator 中间类即为 Component* 共同字段的上提

//注意:
//不把Component*字段上升到stream基类的原因是FileStream等主体类并不需要该字段
//因此把该共有字段上升到Decorator装饰器对象中

//Decorator
//接口上is-a Component用于满足 Component 接口规范
//实现上has-a Component用于使用多态基类指针黑盒复用其他功能接口,完成功能组合
class Decorator : public Component {
protected:
Component* component;
public:
//...省略
Decorator(...):...{};
}

image-20210721094228300

Bridge 模式

Motivation

某些类型具有固有的实现逻辑,使得他们具有多个变化维度

Defination

通过组合,将抽象部分实现部分分离,使他们可以独立的变化

Code and UML

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
//通信模块
//解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化
#include<iostream>
#include<string>
using namespace std;

class MessagerImp;

//业务抽象
class Messager {
protected:
MessagerImp* messager;
public:
Messager(MessagerImp* m) :messager(m) {}

virtual void Login(string username, string password) = 0;
virtual void SendMessage(string message) = 0;
virtual void SendPicture() = 0;

virtual ~Messager() {}
};

//实现抽象

class MessagerImp {

public:
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;

virtual ~MessagerImp() {}
};
//平台实现
class PCMessagerBase : public MessagerImp {
public:

virtual void PlaySound() {
//**********
}
virtual void DrawShape() {
//**********
}
virtual void WriteText() {
//**********
}
virtual void Connect() {
//**********
}
};

class MobileMessagerBase : public MessagerImp {
public:

virtual void PlaySound() {
//==========
}
virtual void DrawShape() {
//==========
}
virtual void WriteText() {
//==========
}
virtual void Connect() {
//==========
}
};

//业务逻辑
class MessagerLite:public Messager {

public:

MessagerLite(MessagerImp* m) :Messager(m) {}

virtual void Login(string username, string password) {

messager->Connect();
//........
}
virtual void SendMessage(string message) {

messager->WriteText();
//........
}
virtual void SendPicture() {

messager->DrawShape();
//........
}
};

class MessagerPerfect:public Messager {

public:

MessagerPerfect(MessagerImp* m) :Messager(m) {}

virtual void Login(string username, string password) {

messager->PlaySound();
//********
messager->Connect();
//........
}
virtual void SendMessage(string message) {

messager->PlaySound();
//********
messager->WriteText();
//........
}
virtual void SendPicture() {

messager->PlaySound();
//********
messager->DrawShape();
//........
}
};

int main() {
//编译时装配
MessagerImp* p = new PCMessagerBase();
Messager * messager = new MessagerLite(p);
}

image-20210725171507191

Decorator 和 Bridge 的异同

  • Decorator Bridge
    单一职责 功能子类的职责交叉划分不清 基类多维度职责划分不清
    共有字段 无法上升至基类,上升至Decorator装饰器接口类中 上升至业务抽象基类中
    应用场景 单维度多方向的功能拓展 多维度的需求变化
    1. 都是采用组合代替继承解决子类膨胀问题

    2. 都是运行时装配,即功能对象与主类对象的组合,各维度对象与主维度对象的组合

      通过多态在创建对象时进行对象动态组合,而非在类中直接绑定静态组合

“对象创建” 模式

避免了使用 new 进行对象创建导致的紧耦合(依赖具体类),使得对象的创建更为稳定

Factory Method 模式

Motivation

对象的具体类型经常变化,导致新对象创建时紧耦合现象

Defination

定义一个创建对象的接口(工厂基类),让子类决定实例化哪一个类(具体工厂)

通过虚函数使一个类的实例化延迟到子类实现

Code and UML

1
2
//FactoryMethod基类 将对象创建任务延迟到子类
//通过拓展策略,创建新增类型对象 --添加一个子类实现工厂基类接口

image-20210721110754696

Abstract Factory 模式

Motivation

一系列相互依赖对象 / 系列对象的创建工作,通过简单工厂模式创建的各个对象可能破坏这种依赖关系

Defination

提供一个接口,让该接口来负责创建一系列”相互依赖对象”

Code and UML

1
2
3
4
//数据库读取
//不同数据库command与connection对象相互关联且
//将各个相关联的分工厂基类(commandFactory,connectionFactory)合并为一个总工厂基类(Factory)
//该总工厂接口规范子工厂对象的创建工作

image-20210722081816272

Prototype 模式

Motivation

某些结构复杂的对象的创建工作

结构复杂的对象再面临复杂变化时,很难拥有稳定的接口

Defination

使用原型实例指定创建对象的种类,通过拷贝这些原型来创建新的对象

深拷贝

Code and UML

1
2
//原型对象只可用于clone新对象,不允许直接使用原型对象的接口
//Prototype基类规范了clone和其他稳定接口,新增变化的复杂类型是实现该接口即可

image-20210722093126545

Builder 模式

Motivation

复杂对象部分子对象通过一定算法构成

而子对象的复杂变化,使算法的稳定面临考验

Defination

将复杂对象的构建和其表示分离

使得同样的构建过程(稳定)可以创建不同的表示(变化)

注意:不能在构造函数中进行构建调用

因为对象模型中会先调用父类构造函数,多态调用时子类虚函数还未定义便被父类调用

Code and UML

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
//House
//构造房子的步骤(算法)稳定,不能改变
#include<iostream>
using namespace std;

class Builder;

class House {
public:
//...
House(Builder* b):builder(b){}
virtual void construct() = 0;
protected:
Builder* builder;
};

class Builder {
public:
virtual void init() = 0;
House* GetResult() {
return pHouse;
}
protected:
House* pHouse; //方便访问到Hosue的某些属性
virtual void buildpart1() = 0;
virtual void buildpart2() = 0;
virtual void buildpart3() = 0;
};

class StoneBuilder :public Builder {
public:
void init() {
//...
buildpart1();
//...
buildpart2();
//...
buildpart3();
cout << "StoneHouse 建造完成" << endl;
}
protected:
virtual void buildpart1() {
cout << "部分1建造完成" << endl;
}
virtual void buildpart2() {
cout << "部分2建造完成" << endl;
}
virtual void buildpart3() {
cout << "部分3建造完成" << endl;
}
};

class StoneHouse : public House{
public:
StoneHouse(Builder* b) :House(b) {}

void construct() {
builder->init();
}
};

int main() {
House* p = new StoneHouse(new StoneBuilder());
p->construct();
}

//---------------------------------------------------------------------------------
//进一步划分
#include<iostream>
using namespace std;

class Builder;

class House {
public:
House(Builder* b):builder(b){}
virtual void construct() = 0;
protected:
Builder* builder;
};

class Builder {
public:

House* GetResult() {
return pHouse;
}

virtual void buildpart1() = 0;
virtual void buildpart2() = 0;
virtual void buildpart3() = 0;

protected:
House* pHouse; //方便访问到Hosue的某些属性

};

class StoneBuilder :public Builder {

protected:
virtual void buildpart1() {
cout << "部分1建造完成" << endl;
}
virtual void buildpart2() {
cout << "部分2建造完成" << endl;
}
virtual void buildpart3() {
cout << "部分3建造完成" << endl;
}
};

class HouseDirector {

public:
HouseDirector(Builder* b) :builder(b) {}

House* construct() {
//...
builder->buildpart1();
//...
builder->buildpart2();
//...
builder->buildpart3();
//
cout << "House 建造完成" << endl;

return builder->GetResult();
}

protected:
Builder* builder;
};

class StoneHouse : public House{
};

int main() {
HouseDirector* p = new HouseDirector(new StoneBuilder());
House* h = p->construct();
}

image-20210722102754917

“对象性能” 模式

Singleton 模式

Motivation

类在系统中只存在一个实例

这是类设计者的责任,而不是使用者的责任

Defination

保证一个类仅有一个实例

并提供一个该实例的全局访问点

Code and UML

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
//限制构造函数和拷贝构造的访问权限 private / protected 
//仅提供静态方法接口获取为一个的静态对象
//单线程环境下简单但是多线程环境下需要考虑完全性问题

class Singleton {
private:
Singleton(){}
Singleton(const Singleton& a){}
public:
static Singleton* single;
static Singleton* getSingle(){
//..
}
};

Singleton* Singleton::single = nullptr;

//单线程安全
Singleton* Singleton::getSingle(){
if(single==nullptr) {
single = new Singleton();
}
return single;
}

//判断前加锁正确但是高并发代价太高
Singleton* Singleton::getSingle(){
Lock lock;
if(single==nullptr) {
single = new Singleton();
}
return single;
}

//双检查锁不安全
//因为reorder导致new运算可能未执行构造函数之前先为single赋予一个空的内存地址
//而别的线程在读时大概率可能出现问题
Singleton* Singleton::getSingle(){

if(single==nullptr) {
Lock lock;
if(single==nullptr){ //注意第二层判断
single = new Singleton();
}
}
return single;
}

//c++11 volatile
//保证new先构造在指针转型
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getSingle() {
Singleton* tmp = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}

image-20210723082322431

Flyweight 享元模式

Motivation

纯粹的面向对象方案会有大量细粒度对象充斥系统,造成内存方面性能消耗

Defination

运用共享技术有效支持大量细粒度对象

共享对象存放在享元池

Code and UML

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
//Font 字体与字符
//不同字符相同字体的情况下共享同一种字体对象
#include<map>
#include<iostream>
#include<string>
using namespace std;

class Font {
public:
Font(string n) :name(n) {}
private:
string name;
};

class FontFactory {

public:
//享元池
map<string, Font*> font_pool;
public:
Font* getFont(const string& key) {

map<string, Font*>::iterator ite = font_pool.find(key);
if (ite != font_pool.end())
{
return ite->second;
}
else {
Font* font = new Font(key);
font_pool[key] = font;
return font;
}
}
};

int main() {
FontFactory* x = new FontFactory();
Font* f1 = x->getFont("斜体");
Font* f2 = x->getFont("楷体");
Font* f3 = x->getFont("楷体");

for (auto x : x->font_pool) {
cout << x.first << endl;
}
}

image-20210725153037160

“接口隔离” 模式

Facade 模式

Motivation

客户系统组件有过多的耦合

image-20210723085413221

Defination

Facade模式定义一个稳定接口,隔离子系统的内部变化对客户带来的影响

解耦了系统内部与系统外部的紧耦合关系,系统间的(单向)对象关联

注意:Facade模式中子系统内部必须是相互耦合关系大的组件,而不是简单地功能组合 –高内聚

Code and UML

1
//Facade更注重层架构的层次去看整个系统,而不是单个类的层次

Proxy 模式

Motivation

有些对象由于某些原因通过原生接口直接访问(直接依赖)会带来很多麻烦或者根本不能直接访问

  1. 对象创建开销大
  2. 某些操作需要安全控制
  3. 进程外的访问
  4. 性能损失

Defination

为其他对象提供一种代理以控制对这个对象的透明访问

间接接口在保证透明操作的前提下进而控制额外的复杂问题

若能够实现间接控制,有时损失一些透明度也是可以的

Code and UML

1
2
3
4
//原生访问方式
Subject* sub = new RealSubject();
//间接访问方式
Subject* sub = new Proxy();

image-20210723095954374

Adapter 模式

Motivation

旧的对象,新的环境,要求旧的对象在新的环境中具有新的接口

迁移性的变化需求

Defination

将一个类不兼容的旧接口转换成与新环境兼容的新接口

要求旧接口是adaptable

GOF 23 下的两种Adapter模式

  • 对象适配器

Adapter实现新的接口规范,组合实现了旧接口的对象,复用旧接口来实现新的接口

  • 类适配器 –不提倡

Apdater多继承,public继承新接口规范,protected继承旧接口规范

灵活度不高

Code and UML

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
//简单接口兼容处理
#include <iostream>
using namespace std;

class Old {
public:
virtual void print_old() = 0;
virtual void xrint_old() = 0;
};

class Obj_old :public Old {

public:
virtual void print_old() {
cout << "print_old" << endl;
}
virtual void xrint_old() {
cout << "xrint_old" << endl;
}
};

class New {
public:
virtual void print_new() = 0;
};

class Obj_new :public New {
protected:
Old* interface_old;
public:
Obj_new(Old* p) :interface_old(p) {}

virtual void print_new() {
interface_old->print_old();
interface_old->xrint_old();
cout << "这是新的接口哦" << endl;
}
};

int main() {
New* obj = new Obj_new(new Obj_old());
obj->print_new();
}

image-20210723104524788

Mediator 模式

Motivation

系统内部多个对象关联紧密,但由于是直接的引用依赖,导致一方发生变化,这种关系也将不断地发生变化

image-20210723112631076

Defination

系统内部多个对象的复杂关系进行解耦,Mediator模式将多个对象间的控制逻辑进行集中管理

多个对象间相互关联 -> 多个对象与Mediator关联

Code and UML

1
2
3
4
//Facade模式解耦系统间(单向)关联关系 单方向的依赖
//Mediator模式解耦系统内部(双向)关联关系 双方向的依赖

//Mediator具体对象的实现,对控制逻辑的处理可能相当复杂

“状态变化”模式

State 模式

Motivation

状态变化,其行为也会发生变化,如何根据状态变化透明更改对象行为

Defination

允许一个对象在其内部状态改变时改变它的行为

Code and UML

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
//State 模式将所有 特定状态 以及有关的 特定行为 封装到state基类的子类对象中 
//使得在切换状态对象时候,在接口稳定的情况下,对象行为也随之改变

#include<iostream>
using namespace std;
class State {

protected:
//用于返回状态对象
State* state;
public:
//相应状态下的行为
virtual void operator1() = 0;
virtual void operator2() = 0;
virtual State* getState() = 0;

virtual ~State() {};
};

class StateB;

class StateA:public State {

protected:
static StateA* instance;
public:

//单线程环境下单例创建对象
static StateA* getInstance() {
if (instance == nullptr) {
instance = new StateA();
}
return instance;
}

//相应状态下的行为
virtual void operator1() {
cout << "状态A下的operator1行为" << endl;
state = StateB::getInstance();
}
virtual void operator2() {
cout << "状态A下的operator2行为" << endl;
state = StateB::getInstance();
}

virtual State* getState() {
return state;
}

virtual ~StateA() {};
};

class StateB :public State {
protected:
static StateB* instance;
public:
//单线程环境下单例创建对象
static StateB* getInstance() {
if (instance == nullptr) {
instance = new StateB();
}
return instance;
}

//相应状态下的行为
virtual void operator1() {
cout << "状态B下的operator1行为" << endl;
state = StateA::getInstance();
}
virtual void operator2() {
cout << "状态B下的operator2行为" << endl;
state = StateA::getInstance();
}

virtual State* getState() {
return state;
}

virtual ~StateB() {};
};

StateA* StateA::instance = nullptr;
StateB* StateB::instance = nullptr;

int main() {

State* p= StateB::getInstance();
p->operator2();
p = p->getState();
p->operator2();
return 0;
}

image-20210725080900892

Memento 模式

“数据结构” 模式

Composite 模式

Motivation

客户端代码过度依赖于对象容器内部实现结构而非抽象接口

Defination

将对象组合成树形结构来表示 “部分-整体” 的层次机构

使得单个对象和组合对象的使用具有一致性(对外提供稳定的一致的接口)

Code and UML

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
//客户端使用一致性的接口调用对象,无需关系调用对象的结构的复合结构还是单个对象
//达到了客户代码与复杂对象内部和结构的解耦

#include<list>
#include<iostream>
#include<string>
using namespace std;

class Compoment {

public:
virtual void process() = 0;
virtual void add(Compoment* n) = 0;
virtual void remove(Compoment* n) = 0;

virtual ~Compoment() {};
};

class Compoment_tree :public Compoment {
protected:
string name;
list<Compoment*> list;
public:
Compoment_tree(string n) :name(n) {}

void add(Compoment* n) {
list.push_back(n);
}

void remove(Compoment* n) {
list.remove(n);
}

virtual void process() {
//处理当前结点
cout << "当前String : " << name << endl;
//对子树进行遍历执行process()操作
for (auto& x : list) {
x->process();
}
}
};

class Compoment_node :public Compoment {
protected:
string name;
public:
Compoment_node(string n) :name(n) {}
virtual void process() {
//处理叶子结点
cout << "当前String : " << name << endl;
}

void add(Compoment* n) {
}

void remove(Compoment* n) {
}
};

int main() {
Compoment* p = new Compoment_tree("node1");
p->add(new Compoment_tree("node2"));
p->add(new Compoment_tree("node3"));
p->add(new Compoment_node("node4"));

//调用process时用户无需了解对象容器内部结构
p->process();
}

image-20210725103744204

Iterator 模式

Chain of Resposibility 模式

“行为变化” 模式

Command 模式

Visitor 模式

“领域问题” 模式

Interpreter 模式

重构获得模式

变化点应用设计模式

稳定部分不断抽象到上层代码

静态 -> 动态

静态绑定 -> 动态绑定

基类指针,虚函数机制,多态调用

早绑定 -> 晚绑定

绑定 : 函数体函数调用关联起来

晚绑定

在Library中通过虚函数机制建立稳定函数体与(未定义,未实现)变化的函数接口调用的绑定

早绑定

在Application中建立主函数体和已有(已定义,已实现)函数接口调用的绑定

image-20210719100028440

继承 -> 组合

继承

继承的是静态性质,拓展功能上灵活度不高

组合/委托

黑盒复用,因多态存在,可通过复合基类指针来实现拓展功能的组合,灵活度高

编译时依赖 -> 运行时依赖

编译时依赖

编译时若无响应依赖对象的定义,则无法通过编译

运行时依赖

通过多态,运行时需要通过基类指针来访问响应子类的重写方法,运行时依赖于子类

紧耦合 -> 松耦合

Summary

一个目标

管理变化,提高复用

两种手段

分解 vs 抽象

八大原则

依赖倒置原则 -DIP

变化应该依赖于稳定,稳定不应该依赖于变化

高层模块不应该依赖于低层模块,二者都可以依赖于抽象

抽象不应该依赖于实现,实现细节可以依赖于抽象

开闭原则 -OCP

拓展开放,对修改封闭

类模块应该是可拓展的,但是不可修改

类应该以拓展方式来应对需求变化,而不是更改类中代码

单一职责原则 -SRP

一个类应该仅有一个引起它变化的原因

桥模式

接口隔离原则 -ISP

不能让用户依赖于他们不使用的方法

就是一个类实现了多个接口,其中用户用不到的方法使用private等限定隔离

里氏替换原则 -LSP

子类型必须能替换掉它的父类型 IS-A

能使用父类对象的方法子类也能适用

假设有两个类,一个是Base类,另一个是Child类,并且Child类是Base的子类。那么一个方法如果可以接受一个基类对象b的话:method1(Base b)那么它必然可以接受一个子类的对象method1(Child c)

组合优先于继承

组合为黑盒复用,继承是白盒复用

继承一定程度上破坏了封装性,子类父类耦合度高

封装变化点

一侧变化,一侧稳定

针对接口编程,而不是针对实现编程

不将变量类型声明为某个特定的具体类,而是声明为某个接口

客户程序无需知道对象的具体类型,只需要知道对象所具有的接口

重构技法

静态 -> 动态

早绑定 -> 晚绑定

继承 -> 组合

编译时依赖 -> 运行时依赖

紧耦合 -> 松耦合

Sample

常见场景下的设计模式应用 - 非定式

游戏场景的转换——状态模式
游戏子功能的整合——Facade 门面模式

Facade 使用门面模式实现游戏主程序

获取游戏服务的唯一对象——Singleton 单例模式
游戏内各系统的整合——Mediator 中介者模式
角色与武器的实现——Bridge 桥接模式
角色属性的计算——Strategy 策略模式
攻击特效与击中反应——Template Method 模板方法模式
角色AI——State 状态模式
游戏角色的产生——Factory Method 工厂方法模式
角色的组装——Builder 建造者模式
关卡设计——Chain of Responsibility 责任链模式

手中有剑,心中有剑