学习笔记|设计模式

本文最后更新于:9 个月前

2.面对对象设计原则

面对对象设计原则为支持可维护性复用而诞生,这些原则则蕴含在很多设计模式中,他们是从很多设计方案中总结出来的 指导性原则

原则的目的:高内聚、低耦合。

2.1面向对象设计原则表

名称定义
单一职责原则
Single Responsibolity Principle
开闭原则
Open-Closed principle, OCP
里氏代换原则
Liskov Substitution Principle, LSP
依赖倒转原则
Dependence Inversion Principle, DIP
接口隔离原则
Interface Segregation Principle, ISP
合成复用原则
Composite Reuse Principle, CRP
迪米特法则
Law of Demeter, LOD

2.2开闭原则

  • 对扩展开放,对修改关闭。
  • 增加功能是通过增加代码实现的,而不是修改源代码。

🙅错误案例:

以下面 Calculator 类为例,当我需要增加功能:取模运算,我只能修改 getResult() 函数。不符合开闭原则!

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
template<class T>
class Calculator {
public:
Calculator(T a, T b, string operator_string) {
this->m_a = a;
this->m_b = b;
this->m_operator = operator_string;
}

// 计算的函数
T getResult() {
if (m_operator.compare("+") == 0) {
return m_a + m_b;
} else if (m_operator.compare("-") == 0) {
return m_a - m_b;
} else if (m_operator.compare("*") == 0) {
return m_a * m_b;
} else if (m_operator.compare("/") == 0) {
return m_a / m_b;
}
return 0;
}

private:
T m_a, m_b;
string m_operator;
T result;
};

// 使用
void test01() {
Calculator<int>* calculator = new Calculator<int>(10, 20, "+");
cout << calculator->getResult() << endl;
if (calculator != nullptr) {
delete calculator;
calculator = nullptr;
}
return;
}

🙆修改为符合开闭原则的案例:

  1. 定义一个抽象类 AbstractCalculator,有两个纯虚函数:setOperatorNumber()getResult()
  2. 实现加减乘除功能时,只需要继承抽象类 AbstractCalculator,并重写其虚函数即可。
  3. 注意:记得写虚析构函数(原因:delete 父类指针时,只会调用父类的析构函数,无法调用析构子类对象的析构函数)。
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
// 写一个抽象类
template<class T>
class AbstractCalculator {
public:
// 定义两个纯虚函数
virtual void setOperatorNumber(T a, T b) = 0;
virtual T getResult() = 0;
virtual ~AbstractCalculator() {} // 【定义抽象类记得定义 虚析构函数】
};

// 加法计算器
template<class T>
class PlusCalculator : public AbstractCalculator<T> {
public:
// 重写抽象类的纯虚函数
virtual void setOperatorNumber(T a, T b) {
this->m_a = a;
this->m_b = b;
}
virtual T getResult() {
return m_a + m_b;
}

virtual ~PlusCalculator() {}

private:
T m_a, m_b;
};

// 减法计算器
template<class T>
class SubtractCalculator : public AbstractCalculator<T> {
public:
virtual void setOperatorNumber(T a, T b) {
this->m_a = a;
this->m_b = b;
}
virtual T getResult() {
return m_a - m_b;
}
virtual ~SubtractCalculator() {}

private:
T m_a, m_b;
};

// 乘法计算器
template<class T>
class MultiplyCalculator : public AbstractCalculator<T> {
public :
virtual void setOperatorNumber(T a, T b) {
this->m_a = a;
this->m_b = b;
}
virtual T getResult() {
return m_a * m_b;
}
virtual ~MultiplyCalculator() {}

private:
T m_a, m_b;
};

// 除法计算器
template<class T>
class DivisionCalculator : public AbstractCalculator<T> {
public :
virtual void setOperatorNumber(T a, T b) {
this->m_a = a;
this->m_b = b;
}
virtual T getResult() {
return m_a / m_b;
}
virtual ~DivisionCalculator() {}

private:
T m_a, m_b;
};

// 【增加功能】:取模计算器
template<class T>
class ModuloCalculator : public AbstractCalculator<T> {
public :
virtual void setOperatorNumber(T a, T b) {
this->m_a = a;
this->m_b = b;
}
virtual int getResult() {
return m_a % m_b;
}
virtual ~ModuloCalculator() {}

private:
T m_a, m_b;
};

void test01() {
AbstractCalculator<int>* modulo = new ModuloCalculator<int>();
modulo->setOperatorNumber(10, 20);
cout << modulo->getResult() << endl;
if (modulo != nullptr) {
delete modulo;
modulo = nullptr;
}
return;
}

2.3迪米特法则

又叫 最小知识原则

知道的越少,建立耦合关系越弱。

举个例子:客户的需求是买一个100平的房子,但是并不关心 房子价格、地址、几楼、拥有者。

建立一个中间层,客户不需要和每个房子交流,只需要和中间层(中介)交流就可以。

🙅错误代码实现:

与每个实体类交互,耦合度高!

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
//【🏠房子类】

// 因为有多套房子,需要有扩展性,符合开闭原则,使用抽象类
class AbstractHouse {
public:
virtual int getArea() = 0;
virtual int getPrice() = 0;
virtual void Sell() = 0;
virtual ~AbstractHouse() {}
};

// 🏠房子A
class HouseA : public AbstractHouse {
public:
HouseA() {
this->m_area = 100;
this->m_price = 1500000;
}
virtual int getArea() {
return m_area;
}
virtual int getPrice() {
return m_price;
}
virtual void Sell() {
cout << "售出" << m_area << " 平的房子!" << endl;
}

private:
int m_area;
int m_price;
};

// 🏠房子B
class HouseB : public AbstractHouse {
public:
HouseB() {
this->m_area = 80;
this->m_price = 12000000;
}
virtual int getArea() {
return m_area;
}
virtual int getPrice() {
return m_price;
}
virtual void Sell() {
cout << "售出" << m_area << " 平的房子!" << endl;
}

private:
int m_area;
int m_price;
};

// 🏠房子C
class HouseC : public AbstractHouse {
public:
HouseC() {
this->m_area = 50;
this->m_price = 750000;
}
virtual int getArea() {
return m_area;
}
virtual int getPrice() {
return m_price;
}
virtual void Sell() {
cout << "售出" << m_area << " 平的房子!" << endl;
}

private:
int m_area;
int m_price;
};
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
// 客户端使用--------【与每个实体类交互,耦合度高!】
void test01() {
HouseA* house_a = new HouseA();
if (house_a->getArea() == 100) {
house_a->Sell();
}
if (house_a) {
delete house_a;
house_a = nullptr;
}

HouseB* house_b = new HouseB();
if (house_b->getArea() == 100) {
house_b->Sell();
}
if (house_b) {
delete house_b;
house_b = nullptr;
}

HouseC* house_c = new HouseC();
if (house_c->getArea() == 100) {
house_c->Sell();
}
if (house_c) {
delete house_c;
house_c = nullptr;
}
return;
}

🙆符合迪米特法则的代码:

  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
//【在房子类和客户端之间加入中介】

// 有多个中介,中介功能不一样,比如有的是管理房子面积,有的是管理房子价格
// 所以为了满足开闭原则,使用抽象的中介类
class AbstractAgency {
public:
virtual void setHouse() = 0;
virtual AbstractHouse* SellHouse(int area, int. price = 0) = 0;
virtual ~AbstractAgency() {}
};

// 管理面积的中介
class AgencyArea : public AbstractAgency {
public:
virtual void setHouse() {
AbstractHouse* house_ptr = new HouseA;
house_vector.push_back(house_ptr);
house_ptr = new HouseB;
house_vector.push_back(house_ptr);
house_ptr = new HouseC;
house_vector.push_back(house_ptr);
return;
}
virtual AbstractHouse* SellHouse(int area, int price = 0) {
for (vector<AbstractHouse*>::iterator it = house_vector.begin(); it != house_vector.end(); it++) {
if ((*it)->getArea() == area) {
(*it)->Sell();
return *it;
}
}
return nullptr;
}
~AgencyArea() {
for (vector<AbstractHouse*>::iterator it = house_vector.begin(); it != house_vector.end(); it++) {
if (*it) {
delete *it;
*it = nullptr;
}
}
}

private:
vector<AbstractHouse*> house_vector;
};

// 客户端使用
void test01() {
AbstractAgency* agency_ptr = new AgencyArea;
agency_ptr->setHouse();
if (!agency_ptr->SellHouse(100)) { // 买100平米的房子
cout << "没有买到符合条件的房子!" << endl;
}
if (agency_ptr) {
delete agency_ptr;
agency_ptr = nullptr;
}
}

如果需求变为:买100平米的房子,价格不过200万。可使用另一个类。

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
// 加入管理价格和面积的中介。
class AgencyAreaAndPrice : public AbstractAgency {
public:
virtual void setHouse() {
AbstractHouse* house_ptr = new HouseA;
house_vector.push_back(house_ptr);
house_ptr = new HouseB;
house_vector.push_back(house_ptr);
house_ptr = new HouseC;
house_vector.push_back(house_ptr);
return;
}
virtual AbstractHouse* SellHouse(int area, int price = 0) {
for (vector<AbstractHouse*>::iterator it = house_vector.begin(); it != house_vector.end(); it++) {
if ((*it)->getArea() == area && (*it)->getPrice() < price) {
(*it)->Sell();
return *it;
}
}
return nullptr;
}
~AgencyAreaAndPrice() {
for (vector<AbstractHouse*>::iterator it = house_vector.begin(); it != house_vector.end(); it++) {
if (*it) {
delete *it;
*it = nullptr;
}
}
}

private:
vector<AbstractHouse*> house_vector;
};

// 客户端使用
void test01() {
AbstractAgency* agency_ptr = new AgencyAreaAndPrice;
agency_ptr->setHouse();
if (!agency_ptr->SellHouse(1002000000)) { // 买100平米的房子,价格不超过200万。
cout << "没有买到符合条件的房子!" << endl;
}
if (agency_ptr) {
delete agency_ptr;
agency_ptr = nullptr;
}
}

2.4合成复用原则

继承和组合,优先使用组合。

🙅错误的代码:

车类的代码。(正确)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//🚗车的类
class AbstractCar {
public:
virtual void Run() = 0;
};

class BMW : public AbstractCar {
public:
virtual void Run() {
cout << "BMW 启动了..." << endl;
}
virtual ~AbstractCar() {} // 父类指针指向子类对象,当 delete 父类指针时,需要虚析构才能先调用子类的析构函数。
};

class Jeep : public AbstractCar {
public:
virtual void Run() {
cout << "Jeep 启动了..." << endl;
}
};

Person 继承具体的车,实现开车功能(错误)。

因为人只能开 BMW 这一种车。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 人的类,继承具体的类(🙅错误)
class Person : public BMW {
public:
Person(string name) {
this->m_name = name;
}
void Airing() {
cout << this->m_name >> " 的 ";
Run();
}
private:
string m_name;
};

// 使用
void test01() {
Person* tom = new Person("Tom");
tom->Airing();
if (tom) {
delete tom;
tom = nullptr;
}
}

🙆符合合成复用原则的代码:

  1. 把抽象车 AbstractCar 作为 Person 的一个参数传入。这就是组合(低耦合性)。
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
class Person {
public:
Person(string name) {
this->m_name = name;
}
void Airing(AbstractCar* my_car) {
cout << this->m_name << " 的 ";
my_car->Run();
}
private:
string m_name;

};

// 使用
void test01() {
Person* tom = new Person("Tom");
AbstractCar* my_car = new BMW();
tom->Airing(my_car);
if (tom) {
delete tom;
tom = nullptr;
}
if (my_car) {
delete my_car;
my_car = nullptr;
}
}

2.5依赖倒转原则

🙅错误的依赖方向:

高层依赖底层,层层依赖。(耦合性强!)

🙆正确的依赖方向:

高层业务依赖抽象层,底层实现层继承于抽象层(底层实现依赖于抽象层)。

🙅错误的代码:

底层类 -> 中层模块 -> 高层业务,层层依赖,耦合度高。

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
// 【1.底层模块】
class BankWorker {
public:
void PayService() {
cout << "办理支付业务..." << endl;
}
void TransferService() {
cout << "办理转账业务..." << endl;
}
void SaveService() {
cout << "办理存款业务..." << endl;
}
};

// 【2.中层模块】
// 中层模块--办理存款业务
void DoSaveService(BankWorker* worker) {
worker->SaveService();
}

// 中层模块--办理转账业务
void DoTransferService(BankWorker* worker) {
worker->TransferService();
}

// 中层模块--办理支付业务
void DoPayService(BankWorker* worker) {
worker->PayService();
}

// 【3.高层业务模块】
void test01() {
BankWorker* worker = new BankWorker;
// 办理支付业务
DoPayService(worker);
// 办理转账业务
DoTransferService(worker);
if (worker) {
delete worker;
worker = nullptr;
}
return;
}

🙆符合依赖倒转原则的代码:

底层依赖中层,高层依赖中层。

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
// 【1.底层模块】---  符合【单一职责原则】,每个类只做一件事。
// 底层模块 -- 抽象的银行工作人员
class AbstractWorker {
public:
virtual void doBusiness() = 0;
};

// 底层模块 -- 办理存款业务的工作人员
class SaveBankWorker : public AbstractWorker {
public:
virtual void doBusiness() {
cout << "办理存款业务..." << endl;
}
};

// 底层模块 -- 办理转账业务的工作人员
class TransferBankWorker : public AbstractWorker {
public:
virtual void doBusiness() {
cout << "办理转账业务..." << endl;
}
};

// 底层模块 -- 办理支付业务的工作人员
class PayBankWorker : public AbstractWorker {
public:
virtual void doBusiness() {
cout << "办理支付业务..." << endl;
}
};

// 【中层模块】 使用抽象类来调用具体类,减少耦合度。
void DoNewBusiness(AbstractWorker* bank_worker) {
bank_worker->doBusiness();
return;
}

// 高层业务
void test01() {
AbstractWorker* bank_worker = new TransferBankWorker;
DoNewBusiness(bank_worker);
return;
}

3.简单工厂模式

简单工厂模式并不属于 GoF 的 23 种设计模式。

3.1为什么需要简单工厂模式

如果一个类创建对象时,很复杂,我们可以考虑利用简单工厂,帮忙实例化对象(让客户端和具体实现类解耦)。

例如以下 未使用简单工厂模式 的代码,有如下几个问题:

  • 不符合开闭原则:添加其他类型的水果,Fruit 这个类就需要修改。(不具扩展性)
  • 不符合单一职责原则: Fruit 类处理的事务过多。不利于类的重用和维护。
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
class Fruit {
public:
Fruit(string name) {
this->m_name = name;
if (m_name == "apple") {
m_price = 5;
} else if (m_name == "banana") {
m_price = 6;
} else if (m_name == "pear") {
m_price = 7;
}
}
void getName() {
if (m_name == "apple") {
cout << "我是苹果业务" << endl;
} else if (m_name == "banana") {
cout << "我是香蕉业务" << endl;
} else if (m_name == "pear") {
cout << "我是鸭梨业务" << endl;
}
}

private:
string m_name;
int m_price;
};

// 客户端使用
int main() {
Fruit apple("apple");
Fruit banana("banana");
Fruit pear = Fruit("bear");

apple.getName();
banana.getName();
pear.getName();
}

3.2角色

工厂角色(Factory):

  • 简单工厂模式的核心。
  • 负责实现创建所有对象的内部逻辑。
  • 外界调用工厂,来创建产品对象。

抽象产品角色(Abstract Product):

  • 工厂所创建的所有对象的父类,抽象类。

具体产品角色(Concrete Product):

  • 工厂所创建的对象。

3.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
// 【抽象产品】—— 抽象水果
class AbstractFruit {
public:
virtual void ShowName() = 0; // ShowName 同 getName 方法。
virtual ~AbstractFruit(){}
};

// 【具体产品】—— 具体水果的实现
class Apple : public AbstractFruit {
public:
Apple() {
this->m_name = "apple";
}
virtual void ShowName() {
cout << "我是苹果" << endl;
}

private:
string m_name;
};

// 【具体产品】—— 具体水果的实现
class Banana : public AbstractFruit {
public:
Banana() {
this->m_name = "banana";
}
virtual void ShowName() {
cout << "我是🍌" << endl;
}

private:
string m_name;
};

// 【具体产品】—— 具体水果的实现
class Pear : public AbstractFruit {
public:
Pear() {
this->m_name = "pear";
}
virtual void ShowName() {
cout << "我是鸭梨🍐" << endl;
}

private:
string m_name;
};
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
// 简单工厂模式
// 【工厂】
class FruitFactory {
public:
static AbstractFruit* CreateFruit(string name) {
if (name == "apple") {
return new Apple;
} else if (name == "banana") {
return new Banana;
} else if (name == "pear") {
return new Pear;
}
}
};


// 业务
void test01() {
AbstractFruit* apple = FruitFactory::CreateFruit("apple");
apple->ShowName();
delete apple;

AbstractFruit* banana = FruitFactory::CreateFruit("banana");
banana->ShowName();
delete banana;

AbstractFruit* pear = FruitFactory::CreateFruit("pear");
pear->ShowName();
delete pear;

return;
}

3.4优缺点

优点:

  • 实现了对象创建和使用的分离。
  • 不需要记住具体类名,记住参数即可,减少使用者记忆量。

缺点:

  • 不符合开闭原则:添加新产品,需要修改工厂逻辑。
  • 不符合单一职责原则:工厂的职责过重,一旦不能工作,系统将受到影响。
  • 增加系统中类的个数,复杂度和理解度增加。

3.5适用场景

  • 工厂创建的对象少。工厂的业务逻辑不会太复杂。
  • 客户端只知道创建对象的参数,对如何创建对象不关心。

4.工厂方法模式

4.1为什么需要工厂方法模式

为了解决 简单工厂模式不符合开闭原则 和 单一职责原则 的问题。

4.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
// 相比于简单工厂模式,水果类没有变。
// 【抽象产品】—— 抽象水果
class AbstractFruit {
public:
virtual void ShowName() = 0;
~AbstractFruit(){}
};

// 【具体产品】—— 具体水果的实现
class Apple : public AbstractFruit {
public:
Apple() {
this->m_name = "apple";
}
virtual void ShowName() {
cout << "我是苹果" << endl;
}

private:
string m_name;
};

// 【具体产品】—— 具体水果的实现
class Banana : public AbstractFruit {
public:
Banana() {
this->m_name = "banana";
}
virtual void ShowName() {
cout << "我是🍌" << endl;
}

private:
string m_name;
};

// 【具体产品】—— 具体水果的实现
class Pear : public AbstractFruit {
public:
Pear() {
this->m_name = "pear";
}
virtual void ShowName() {
cout << "我是鸭梨🍐" << endl;
}

private:
string m_name;
};
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
// 【工厂方法模式】
// 抽象工厂
class AbstractFruitFactory {
public:
virtual AbstractFruit* CreateFruit() = 0;
virtual ~AbstractFruitFactory(){}
};

// 具体工厂
class AppleFactory : public AbstractFruitFactory {
public:
virtual AbstractFruit* CreateFruit() {
return new Apple;
}
};

// 具体工厂
class BananaFactory : public AbstractFruitFactory {
public:
virtual AbstractFruit* CreateFruit() {
return new Banana;
}
};

// 具体工厂
class PearFactory : public AbstractFruitFactory {
public:
virtual AbstractFruit* CreateFruit() {
return new Pear;
}
};

// 业务
void test01() {
AbstractFruitFactory* factory = new AppleFactory;
AbstractFruit* Fruit = factory->CreateFruit();
Fruit->ShowName();
return;
}

4.3优缺点

缺点:

  • 类的个数成倍增加,导致类越来越多,增加维护成本。

优点:

  • 符合开闭原则。
  • 符合单一职责原则。

4.4适用场景

  • 客户端不知道它需要的对象的类。(让具体的工厂来创建对象)。
  • 抽象工厂类通过其子类(具体工厂)来指定创建哪个对象。

5.抽象工厂模式

5.1为什么需要抽象工厂模式

  • 为了让水果类满足 开闭原则和单一职责原则,我们使用简单工厂模式;但是工厂类职责太重,工厂类不符合 开闭原则和单一职责原则。
  • 为了使工厂类符合 开闭原则和单一职责原则,我们使用 工厂方法模式;但是每个工厂只生产一种产品,导致系统中有大量的工厂类,增加系统开销。
  • 为什么解决 工厂方法模式 的问题,我们将相关产品组成一个产品族,由同一个工厂来生产。

PS:当有不同产地的同种水果加入(比如增加 美国苹果、中国苹果),更适合使用抽象工厂。

5.2产品族和产品等级结构

  • 产品族:同一产地、同一厂商、功能不同

  • 产品等级:产地或者厂商不同,功能相同

  • 抽象工厂不是针对产品等级结构的,是针对产品族的。

    • 针对产品等级结构,抽象工厂模式不符合开闭原则。(增加产品等级结构,比如增加中国西瓜、美国西瓜、日本西瓜,需要修改中国工厂、美国工厂、日本工厂的类,不符合开闭原则。
    • 针对产品族,抽象工厂符合开闭原则。(增加产品族,比如增加印度苹果、印度香蕉、印度梨,只需要增加印度工厂即可,符合开闭原则。

5.3角色

抽象工厂(Abstract Factory):

  • 声明了一组用于创建一族产品的方法,每一个方法对应一种产品。

具体工厂(Concrete Factory):

  • 实现抽象工厂,创建一个产品族。
  • 创建的每个产品都分别位于一个产品等级结构中。

抽象产品(Abstrcat Product):

  • 声明抽象产品的业务。

具体产品(Concerte Product):

  • 定义一个具体工厂的具体产品。

5.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
class AbstractFruit {
public:
virtual void getName() = 0;
virtual ~AbstractFruit(){}
};

// 【抽象产品】—— 抽象苹果
class AbstractApple : public AbstractFruit {
public:
virtual void getName() = 0;
virtual ~AbstractApple(){}
};

// 具体产品——苹果
class ChinaApple : public AbstractApple {
public:
virtual void getName() {
cout << "我是中国苹果!" << endl;
}
};

// 具体产品——苹果
class AmericaApple : public AbstractApple {
public:
virtual void getName() {
cout << "我是美国苹果!" << endl;
}
};

// 具体产品——苹果
class JapanApple : public AbstractApple {
public:
virtual void getName() {
cout << "我是日本苹果!" << endl;
}
};

//【抽象产品】—— 抽象香蕉
class AbstractBanana : public AbstractFruit {
public:
virtual void getName() = 0;
virtual ~AbstractBanana(){}
};

// 具体产品——香蕉
class ChinaBanana : public AbstractBanana {
public:
virtual void getName() {
cout << "我是中国香蕉!" << endl;
}
};

// 具体产品——香蕉
class AmericaBanana : public AbstractBanana {
virtual void getName() {
cout << "我是美国香蕉!" << endl;
}
};

// 具体产品——香蕉
class JapanBanana : public AbstractBanana {
virtual void getName() {
cout << "我是日本香蕉!" << endl;
}
};

// 抽象产品
class AbstractPear : public AbstractFruit {
public:
virtual void getName() = 0;
virtual ~AbstractPear(){}
};

// 具体产品——梨
class ChinaPear : public AbstractPear {
public:
virtual void getName() {
cout << "我是中国梨" << endl;
}
};

// 具体产品——梨
class AmericaPear : public AbstractPear {
public:
virtual void getName() {
cout << "我是美国梨" << endl;
}
};

// 具体产品——梨
class JapanPear : public AbstractPear {
public:
virtual void getName() {
cout << "我是日本梨" << 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
// 抽象工厂
class AbstractFactory {
public:
virtual AbstractApple* CreateApple() = 0;
virtual AbstractBanana* CreateBanana() = 0;
virtual AbstractPear* CreatePear() = 0;
virtual ~AbstractFactory() {}
};

// 具体工厂——中国工厂
class ChinaFactory : public AbstractFactory {
public:
virtual AbstractApple* CreateApple() {
return new ChinaApple;
}

virtual AbstractBanana* CreateBanana(){
return new ChinaBanana;
}

virtual AbstractPear* CreatePear(){
return new ChinaPear;
}
};

// 具体工厂——美国工厂
class AmericaFactory : public AbstractFactory {
public:
virtual AbstractApple* CreateApple() {
return new AmericaApple;
}

virtual AbstractBanana* CreateBanana(){
return new AmericaBanana;
}

virtual AbstractPear* CreatePear(){
return new AmericaPear;
}
};

// 具体工厂——日本工厂
class JapanFactory : public AbstractFactory {
public:
virtual AbstractApple* CreateApple() {
return new JapanApple;
}

virtual AbstractBanana* CreateBanana(){
return new JapanBanana;
}

virtual AbstractPear* CreatePear(){
return new JapanPear;
}
};

// 客户端
void test01() {
AbstractFactory* my_factory = new ChinaFactory;
AbstractFruit* my_fruit = my_factory->CreateBanana();
my_fruit->getName();
delete my_fruit;
delete my_factory;
}

5.5适用场景

  • 系统中有多个的产品族。而每次只使用其中某一产品族。
  • 产品等级结构稳定。设计完成之后,不会向系统中增加或删除已有的产品等级结构。

6.单例模式

6.1概念

在现实生活中,有些类的实例化对象的个数只能有一个。

保证一个类,只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。

三个要点:

  • 某个类只能有一个实例;

  • 它必须自行创建这个实例;

  • 它必须自行向整个系统提供这个实例。

6.2角色

单例:(Singleton)

  • 单例内部实现只产生一个实例(instance),即在单例类中定义一个 Singleton 类型的对象;
  • 提供一个 静态的 getInstance 方法,让客户端可以访问它唯一的实例。
  • 防止外部对其实例化,将其构造函数设计为私有。

6.3实现:

  1. 构造函数私有化。
  2. 类中增加私有的 静态 指针,初始化为空;在 getInstance 方法中,指向唯一的实例对象。
  3. 提供 静态 对外接口 getInstance ,可以让用户获得单例对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton {
private:
Singleton(){ // 1.构造函数私有化
instance_ptr = new Singleton;
}

public:
static Singleton* getInstance() { // 3.提供静态的接口 getInstance,让客户端可以访问单例对象
return instance_ptr;
}

private:
static Singleton* instance_ptr; // 2.增加私有的 静态 指针
};

Singleton* Singleton::instance_ptr = nullptr; // 2.静态指针初始化为 nullptr

6.4懒汉模式

线程不安全。

第一次调用时,才会实例化对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SingletonLazy {
private:
SingletonLazy(){}

public:
static SingletonLazy* getInstance() {
if (!instance_ptr) { // 第一次调用时,才实例化对象。
instance_ptr = new SingletonLazy;
}
return instance_ptr;
}

private:
static SingletonLazy* instance_ptr;
};

SingletonLazy* SingletonLazy::instance_ptr = nullptr;

6.5饿汉模式

线程安全。

初始化时,实例化对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SingletonHungry {
private:
SingletonHungry(){}

public:
static SingletonHungry* getInstance() {
return instance_ptr;
}

private:
static SingletonHungry* instance_ptr;
};

SingletonHungry* SingletonHungry::instance_ptr = new SingletonHungry; // 初始化时,实例化对象。

6.6该不该释放单例对象

不应该给单例对象提供 释放单例对象内存的函数

  • 因为全局就这一份,一旦释放,全局都会受到影响。
  • 只有一份,不占用内存,可以等待程序结束,自动释放单例对象内存。

6.7多线程和单例模式

多个线程并发执行,不一定哪一个先执行完。

  • 对于懒汉模式,只有调用 getInstance 函数时,才实例化单例对象,可能多个线程同时实例化单例对象,线程不安全。(需要加锁,保证线程安全。)

  • 对于饿汉模式,在程序执行前,就已经实例化出单例对象了(静态成员变量,编译阶段已经分配空间,储存在 data 区),线程安全!

PS:测试饿汉模式 “在程序执行前,就实例出单例对象”。

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
class SingletonHungry {
private:
SingletonHungry(){
cout << "begin create instance..." << endl;
}

public:
static SingletonHungry* getInstance() {
return instance_ptr;
}

private:
static SingletonHungry* instance_ptr;
};

SingletonHungry* SingletonHungry::instance_ptr = new SingletonHungry;

int main() {
cout << "main() begin..." << endl;
SingletonHungry::getInstance();
return 0;
}

/* 打印结果如下:
begin create instance... // 先完成构造
main() begin... // 再开始 main 函数
*/

6.8适用场景

  • 系统只需要一个实例对象,比如一个资源管理器(无法打开第二个),或者因为资源消耗大而只允许创建一个对象。
  • 客户端调用单个实例,只允许使用一个公共访问点。(除了公共访问点,无允许使用其他途径访问。)

7.代理模式Proxy

7.1概念

构造型的设计模式之一。

代理模式可以 为其他对象提供一种代理,以 控制对该对象的访问

所谓代理,就是与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与代理元交互。

7.2角色

抽象主题(Subject):

  • 真实主题和代理主题共同的接口。

真实主题(RealSubject):

  • 定义了真实的对象。

代理主题(Proxy):

  • 含有对真实主题角色的引用。
  • 代理角色并不是单纯的返回真实对象,而是在之前或之后做一些操作以控制对该对象的访问。

7.3实现

案例: 利用代理类,在启动系统前进行校验。

  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
// 抽象主题 Subject
class AbstractInterface {
public:
virtual void Run() = 0;
4 virtual ~AbstractInterface(){}
};

// 真实主题 RealSubject
class MySystem : public AbstractInterface {
public:
virtual void Run() {
cout << "Begin MySystem..." << endl;
}
};


// 代理主题 Proxy
class MySystemProxy : public AbstractInterface {
public:
MySystemProxy(string user_name, string user_password) {
this->m_user_name = user_name;
this->m_user_passworld = user_password;
my_system_ptr = new MySystem;
}

virtual void Run() {
if (CheckUser()) { // 返回真实类之前,进行校验。
cout << "用户名和密码正确,验证通过!" << endl;
this->my_system_ptr->Run();
} else {
cout << "用户名和密码错误,权限不足!" << endl;
}
}

~MySystemProxy(){
if (my_system_ptr) {
delete my_system_ptr;
my_system_ptr = nullptr;
}
}

private:
bool CheckUser() {
if (m_user_passworld == "admin" && m_user_name == "admin") {
return true;
}
return false;
}

private:
string m_user_name;
string m_user_passworld;
MySystem* my_system_ptr; // 代理类中,含有真实类。
};

// 客户端
int client() {
string user_name = "admin", user_passworld = "admin";
AbstractInterface* begin = new MySystemProxy(user_name, user_passworld);
begin->Run();
if (begin) {
delete begin;
begin = nullptr;
}
return 0;
}

7.4优缺点

优点:

  • 能够协调调用者和被调用者,降低了系统的耦合度(真实主题和客户端之间无直接关系。)。
  • 客户端可以针对抽象主题进行编程,增加和更换代理类无须修改源代码,符合 开闭原则

缺点:

  • 代理实现较为复杂。

7.5适用场景

为其他对象提供一种代理,以 控制对该对象的访问

8.外观模式Facada

8.1概念

根据迪米特法则,如果两个类不必直接通信,那么两个类就不应该发生直接的相互作用。

Facada模式为一组具有类似功能的类群,比如类库,子系统等等,提供一个一致的简单的界面(该界面就叫做 Facade)。

8.2角色

外观角色(Facade):

  • 为客户端定义简单的调用接口。

子系统角色(SubSystem):

  • 功能提供者,提供功能的类群。

8.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
// 子系统类
class System1 {
public:
void Run() {
cout << "System1 启动..." << endl;
}
};

class System2 {
public:
void Run() {
cout << "System2 启动..." << endl;
}
};

class System3 {
public:
void Run() {
cout << "System3 启动..." << endl;
}
};

class System4 {
public:
void Run() {
cout << "System4 启动..." << 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
// 外观类
class Facade {
public:
Facade() {
System1_ptr = new System1;
System2_ptr = new System2;
System3_ptr = new System3;
System4_ptr = new System4;
}

void Run() {
System1_ptr->Run();
System2_ptr->Run();
System3_ptr->Run();
System4_ptr->Run();
}

~Facade() {
if (System1_ptr) {
delete System1_ptr;
System1_ptr = nullptr;
}
if (System2_ptr) {
delete System1_ptr;
System2_ptr = nullptr;
}
if (System3_ptr) {
delete System1_ptr;
System3_ptr = nullptr;
}
if (System4_ptr) {
delete System1_ptr;
System4_ptr = nullptr;
}
}

private:
System1* System1_ptr;
System2* System2_ptr;
System3* System3_ptr;
System4* System4_ptr;
};


// 客户端
void Client() {
Facade facade;
facade.Run();
return;
}

8.4家庭影院案例

实现KTV模式:电视打开,灯关掉,音响打开,麦克风打开,DVD打开;

实现游戏模式:电视打开,音响打开,游戏机打开。

  • 用两个外观类,FacadeKTV 和 FacadeGame 两个外观模式,来管理 TV、Light、Aduio、MircoPhone、DVDPlayer、Game 灯设备。

  • 客户端通过外观类来控制这些设备,而不是直接和这些设备交互,减少耦合度。

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
// 产品设备类
class TV {
public:
void On() {
cout << "电视机打开..." << endl;
}
void Off() {
cout << "电视机关闭..." << endl;
}
};

class Light {
public:
void On() {
cout << "灯打开..." << endl;
}
void Off() {
cout << "灯关闭..." << endl;
}
};

class Audio {
public:
void On() {
cout << "音响打开..." << endl;
}
void Off() {
cout << "音响关闭..." << endl;
}
};

class Mircophone {
public:
void On() {
cout << "麦克风打开..." << endl;
}
void Off() {
cout << "麦克风关闭..." << endl;
}
};

class Game {
public:
void On() {
cout << "游戏机打开..." << endl;
}
void Off() {
cout << "游戏机关闭..." << endl;
}
};

class DVDPlayer {
public:
void On() {
cout << "DVD打开..." << endl;
}
void Off() {
cout << "DVD关闭..." << 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
// KTV 外观模式
class FacadeKTV {
public:
FacadeKTV() {
tv = new TV;
light = new Light;
audio = new Audio;
mircophone = new Mircophone;
DVD_player = new DVDPlayer;
game = new Game;
}

void On() {
tv->On();
light->Off();
audio->On();
mircophone->On();
DVD_player->On();
game->Off();
}

void Off() {
tv->Off();
light->On();
audio->Off();
mircophone->Off();
DVD_player->Off();
game->Off();
}

~FacadeKTV() {
if (tv) {
delete tv;
tv = nullptr;
}
if (light) {
delete light;
light = nullptr;
}
if (audio) {
delete audio;
audio = nullptr;
}
if (mircophone) {
delete mircophone;
mircophone = nullptr;
}
if (DVD_player) {
delete DVD_player;
DVD_player = nullptr;
}
if (game) {;
delete game;
game = nullptr;
}
}

private:
TV* tv;
Light* light;
Audio* audio;
Mircophone* mircophone;
DVDPlayer* DVD_player;
Game* game;
};
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
// 游戏 外观模式
class FacadeGame {
public:
FacadeGame() {
tv = new TV;
light = new Light;
audio = new Audio;
mircophone = new Mircophone;
DVD_player = new DVDPlayer;
game = new Game;
}

void On() {
tv->On();
light->Off();
audio->On();
mircophone->Off();
DVD_player->Off();
game->On();
}

void Off() {
tv->Off();
light->On();
audio->Off();
mircophone->Off();
DVD_player->Off();
game->Off();
}

~FacadeGame() {
if (tv) {
delete tv;
tv = nullptr;
}
if (light) {
delete light;
light = nullptr;
}
if (audio) {
delete audio;
audio = nullptr;
}
if (mircophone) {
delete mircophone;
mircophone = nullptr;
}
if (DVD_player) {
delete DVD_player;
DVD_player = nullptr;
}
if (game) {;
delete game;
game = nullptr;
}
}

private:
TV* tv;
Light* light;
Audio* audio;
Mircophone* mircophone;
DVDPlayer* DVD_player;
Game* game;
};


// 客户端使用
void Client() {
FacadeGame* facade = new FacadeGame;
facade->On();
}

8.4优缺点

优点:

  • 对客户端屏蔽了子系统组件,减少客户端所需处理的对象数目,使子系统使用起来更加容易。(引入外观模式,客户端代码更加简洁,与之关联的对象也很少。)

  • 实现了子系统和客户端之间的松耦合关系,使得子系统的变化,不会影响客户端代码,只需要更改外观类即可。

  • 一个子系统修改,不影响其他子系统。

缺点:

  • 不能限制客户端直接使用子系统。
  • 如果设计不当,需要修改外观类的代码,不符合 开闭原则

8.5适用场景

  • 复杂的系统,需要简单的入口。

  • 客户端和多个子系统之间存在很大的依赖性。

  • 在层次结构中,减少层与层之间的耦合度,利用外观模式定义系统中每一层的入口。

9.适配器模式

9.1概念

将已经写好的接口(但是这个接口不符合需求),转化为目标需要的接口。

9.2角色

目标抽象类(Target):

  • 定义客户所需的目标接口。

适配器类(Apadter):

  • 可以调用一个接口。
  • 作为转换器,对 TargetApadtee 进行适配。适配器是适配器模式的核心。
  • 继承 Target,并关联 Apadtee,使二者之间产生关联。

9.3实现

案例:Client 调用带有一个参数的函数,但是现在只有带有两个参数的函数对象,通过适配器,使带有两个参数的函数对象,成功被 Client 调用。

  1. Adaptee 等待着被适配(两个参数)。
  2. Client 只能使用一个参数的函数。
  3. Adapter 将 Adaptee 适配为 Client 可以使用的函数。
    • 将 Adaptee 作为自己的私有成员。
    • 重写父类 Target 的方法。并在方法中,调用 Adaptee 函数。
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
// Adaptee —— 函数对象
struct Print {
void operator()(int v, int parameter) {
cout << v + parameter << endl;
}
};

// Target —— 抽象适配器
class Target {
public:
virtual void operator()(int v) = 0;
};

// Adapter —— 适配器
class Adapter : public Target {
public:
Adapter(int parameter) {
this->parameter = parameter;
}

virtual void operator()(int v) {
print(v, parameter);
}

private:
int parameter;
Print print;
};


// Client
int main() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}

// for_each 第三个参数只能传递的函数,只能有一个参数。
for_each(v.begin(), v.end(), Adapter(10));
}

9.4手机充电器案例

案例:利用适配器,用 220V 电压给 5V 的手机充电。

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
// Adaptee --- 220V
class Adaptee {
public:
void Use220V() {
cout << "使用 220V 充电..." << endl;
}
};

// Target --- 5V
class Target {
public:
virtual void Use5V() = 0;
virtual ~Target() {}
};

// Adapter --- 适配器
class Adapter : public Target {
public:
virtual void Use5V() {
cout << "对电压进行适配..." << endl;
v220.Use220V();
}

private:
Adaptee v220;
};

// Client --- 手机
class Phone {
public:
Phone() {
V5 = new Adapter;
}
void Charge() {
cout << "手机进行充电..." << endl;
V5->Use5V();
}
~Phone() {
if (V5) {
delete V5;
}
}

private:
Target* V5;
};

10.模版方法模式

10.1概念

定义一个算法框架,子类继承并重写其中某一个或者多个算法。

10.2角色

抽象类(AbstractClass):

  • 定义了一系列基本操作 (PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一 个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。

具体子类(concreteClass):

  • 实现在父类中声 明的抽象基本操作以完成子类特定算法的步骤。

10.3泡茶的案例

案例:用同样的四个步骤,泡茶或者泡咖啡。

  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
// AbstractClass
class DrinkTemplate {
public:
// 煮开水
virtual void BoilWater() = 0;
// 冲泡
virtual void Brew() = 0;
//倒入杯中
virtual void PourInCup() = 0;
// 添加辅料 【特殊接口】
virtual void AddCondiments() = 0;
// hook 方法,决定某些算法(比如特殊接口)步骤是否挂钩在算法中
virtual bool CustomWantsCondiments() {
return true;
}

// 模版方法
void Make() {
BoilWater();
Brew();
PourInCup();
if (CustomWantsCondiments()) {
AddCondiments();
}
}
};

// ConcreteClass
class Coffee : public DrinkTemplate {
public:
// 煮开水
virtual void BoilWater() {
cout << "煮农夫山泉..." << endl;
}
// 冲泡
virtual void Brew() {
cout << "冲泡咖啡..." << endl;
}
//倒入杯中
virtual void PourInCup() {
cout << "咖啡倒入杯中..." << endl;
}
// 添加辅料【特殊接口】
virtual void AddCondiments() {
cout << "加牛奶..." << endl;
}
// hook 方法,决定 添加辅料 挂钩在 Coffee 中
virtual bool CustomWantsCondiments() {
return true;
}
};

// ConcreteClass
class Tea : public DrinkTemplate {
public:
// 煮开水
virtual void BoilWater() {
cout << "煮农夫山泉..." << endl;
}
// 冲泡
virtual void Brew() {
cout << "冲泡茶叶..." << endl;
}
//倒入杯中
virtual void PourInCup() {
cout << "茶水倒入杯中..." << endl;
}
// hook 方法,决定 添加辅料 挂钩在 Coffee 中
virtual bool CustomWantsCondiments() {
return false;
}
};

void Client() {
DrinkTemplate* drink = new Coffee;
drink->Make();
return;
}

10.4优缺点:

优点:

  • 抽象类中形式化的定义一个算法,让子类来处理细节,子类中并不会改变算法的执行顺序。
  • 可以实现一种反向控制,通过重写钩子函数,来控制某些步骤是否执行。
  • 更换算法中某一步骤很容易,符合开闭原则和单一职责原则。

缺点:

  • 每个基本方法的不同实现都要提供一个子类。如果父类中可变方法比较多,将会导致类的个数增加。

11策略模式

11.1概念

定义一系列算法,并将每个算法封装起来,使他们还可以互相替换。

策略模式,让算法独立于使用它的客户端。

11.2角色

环境类(Context):

  • 环境类是使用算法的角色,他在解决某个问题上,可以采用多种策略。(相当于下面案例中的人 charater )。

抽象策略类(Strategy):

  • 声明了策略的抽象方法。(使策略更具有扩展性,符合开闭原则。)

具体策略类(ConcreteStrategy):

  • 重写了父类(抽象策略类)的算法。

11.3实现

案例:人物使用不同的装备进行战斗。

  1. 匕首 和 AK47 继承抽象武器类,并实现其抽象方法。
  2. 人(Charater)类中,有一个抽象策略类的指针。用来调用不同的具体策略。
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
// 武器策略【Strategy】
class WeaponStrategy {
public:
virtual void Use() = 0;
virtual ~WeaponStrategy() {}
};

// AK47【ConcreStrategy】
class AK47 : public WeaponStrategy {
public:
virtual void Use() {
cout << "使用 AK47 ..." << endl;
}
};

// 匕首【ConcreStrategy】
class Knife : public WeaponStrategy {
public:
virtual void Use() {
cout << "使用匕首..." << endl;
}
};

// 人【Context】
class Character {
public:
void setWeapon(WeaponStrategy* weapon) {
this->m_weapon = weapon;
}
void UseWeapon() {
m_weapon->Use();
}

private:
WeaponStrategy* m_weapon;
};

// 客户端【Client】
void Client() {
Character character;
WeaponStrategy* weapon = new AK47;
character.setWeapon(weapon);
character.UseWeapon();
delete weapon;
return;
}

11.4优缺点

优点:

  • 扩展性好,符合开闭原则。(直接增加具体策略类(算法)就可以。)

  • 提供了一种算法复用的机制。

缺点:

  • 算法之间,任何细微的变化,都需要添加一个具体策略类(算法)。系统可能含有很多的具体策略类。
  • 客户端必须知道所有的具体策略类之间的区别,并自行选择使用哪一个策略。

12.命令模式

别名:动作(Action)模式、事务(Transaction)模式。

12.1概念

命令模式的本质是对请求进行封装,一个请求对应于一个命令(命令类),将发出命令的责任(客户端)和执行命令的责任(服务端)分割开。

12.2角色

抽象命令类(Command):

  • 抽象命令类一般是一个抽象类或接口,在 其中声明了用于执行请求的 Handle() (图中为 execute() 方法)等方法

具体命令类(Concrete Command):

  • 抽象命令类的子类,实现抽象命令类中的 纯虚函数。

调用者(Invoker):

  • 调用者就是 下面实现代码中的 Server 类。
  • 调用者即请求发送者,它通过命令对象来执行请求。
  • 一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。

接收者(Receiver):

  • 接受者具体执行请求相关的操作。接受者的所有函数,都被封装成类了。
  • 它具体实现的请求的业务处理。

12.3实现

案例: 客户端产生了 增加金币、增加钻石、给玩家穿装备、给玩家升级 的请求,因为有很多请求,服务器接收到这些请求,先将这些请求存入队列,然后挨个处理请求。

  1. 接受者先定义好协议类。
  2. 将协议封装成命令类,继承抽象命令类。
  3. 调用者利用抽象命令类的指针,调用,被封装的类(Receiver)。
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
// 协议处理类 ———— 【接受者 Receiver】
class HandleClientProtocol {
public:
// 处理增加金币
void AddMoney() {
cout << "给玩家增加金币." << endl;
}
// 处理增加钻石
void AddDiamond() {
cout << "给玩家增加钻石." << endl;
}
// 处理玩家装备
void AddEquipment() {
cout << "给玩家穿装备." << endl;
}
// 处理玩家升级
void AddLevel() {
cout << "给玩家升级." << endl;
}
};

// 协议命令接口 ———— 【抽象命令类 Command】
class AbstractCommand {
public:
virtual void Handle() = 0;
};

// 处理金币类 ———— 【具体命令类 Concrete Command】
class AddMoneyCommand : public AbstractCommand {
public:
AddMoneyCommand(HandleClientProtocol* protocol) {
this->m_protocol = protocol;
}

virtual void Handle() {
m_protocol->AddMoney();
}

public:
HandleClientProtocol* m_protocol;
};

// 处理钻石类 ———— 【具体命令类 Concrete Command】
class AddDiamondCommand : public AbstractCommand {
public:
AddDiamondCommand(HandleClientProtocol* protocol) {
this->m_protocol = protocol;
}

virtual void Handle() {
m_protocol->AddDiamond();
}

public:
HandleClientProtocol* m_protocol;
};

// 处理穿装备类 ———— 【具体命令类 Concrete Command】
class AddEquipmentCommand : public AbstractCommand {
public:
AddEquipmentCommand(HandleClientProtocol* protocol) {
this->m_protocol = protocol;
}

virtual void Handle() {
m_protocol->AddEquipment();
}

public:
HandleClientProtocol* m_protocol;
};

// 处理升级类 ———— 【具体命令类 Concrete Command】
class AddLevelCommand : public AbstractCommand {
public:
AddLevelCommand(HandleClientProtocol* protocol) {
this->m_protocol = protocol;
}

virtual void Handle() {
m_protocol->AddLevel();
}

public:
HandleClientProtocol* m_protocol;
};

// 【调用者 Invoker】
class Server {
public:
void AddRequest(AbstractCommand* command) {
m_commands_queue.push(command);
}

void StartHandle() {
while(!m_commands_queue.empty()) {
AbstractCommand* command = m_commands_queue.front();
command->Handle();
m_commands_queue.pop();
}
}

public:
queue<AbstractCommand*> m_commands_queue;
};


void Test01(){
HandleClientProtocol* protocol = new HandleClientProtocol;
// 客户端增加金币的请求
AbstractCommand* add_money = new AddMoneyCommand(protocol);
// 客户端增加钻石的请求
AbstractCommand* add_diamond = new AddDiamondCommand(protocol);
// 客户端穿装备的请求
AbstractCommand* add_equipment = new AddEquipmentCommand(protocol);
// 客户端升级的请求
AbstractCommand* add_level = new AddLevelCommand(protocol);

Server* server = new Server;

// 将客户端的请求加入服务器
server->AddRequest(add_money);
server->AddRequest(add_diamond);
server->AddRequest(add_equipment);
server->AddRequest(add_level);

//服务器开始处理请求
server->StartHandle();
return;
}

12.4优缺点

优点:

  • 降低系统的耦合性,调用者和请求者之间不存在引用。
  • 符合开闭原则,如需添加新的命令,直接继承抽象命令类。

缺点:

  • 可能会导致系统中出现过多的具体类。

13.观察者模式

13.1概念

观察者 观察 被观察目标,如果 被观察目标 的状态发生改变,观察者 做出相应的改变。

13.2角色

被观察者或目标,抽象主题(Subject):

  • 被观察的对象。
  • 当被观察的状态发生改变,需要通知队列里面的所有观察者。
  • Subject 需要维持(添加、删除、通知)一个观察者队列列表。

具体被观察者或目标,具体主题(ConcreteSubject):

  • 被观察者的具体实现。

观察者(Observer):

  • 当被观察者发生状态发生改变时,观察者对象会通过一个函数收到通知。

具体观察者(ConcreteObserver):

  • 观察者的具体实现。

13.3实现

案例:当 boss 存活时,英雄攻击 boss,当 boss 死去,英雄停止攻击。

  1. boss 是观察对象,hero 是观察者。
  2. boss 和 hero 都要抽象出来。
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
// 抽象的英雄
class AbstractHero {
public:
virtual void Updata() = 0;
};

// 具体英雄 ——— 【观察者】
class HeroA : public AbstractHero {
public:
HeroA() {
cout << "英雄A正在攻击BOSS..." << endl;
}
virtual void Updata() {
cout << "英雄A停止攻击BOSS,待机状态..." << endl;
}
};

// 具体英雄 ——— 【观察者】
class HeroB : public AbstractHero {
public:
HeroB() {
cout << "英雄B正在攻击BOSS..." << endl;
}
virtual void Updata() {
cout << "英雄B停止攻击BOSS,待机状态..." << endl;
}
};

// 具体英雄 ——— 【观察者】
class HeroC : public AbstractHero {
public:
HeroC() {
cout << "英雄C正在攻击BOSS..." << endl;
}
virtual void Updata() {
cout << "英雄C停止攻击BOSS,待机状态..." << endl;
}
};

// 观察目标抽象
class AbstractBoss {
public:
// 添加观察者
virtual void AddHero(AbstractHero* hero) = 0;
// 删除观察者
virtual void DeleteHero(AbstractHero* hero) = 0;
// 通知观察者
virtual void notify() = 0;
};

// 具体的观察目标 BOSS_A
class BossA : public AbstractBoss {
public:
// 添加观察者
virtual void AddHero(AbstractHero* hero) {
hero_ptr_list.push_back(hero);
}
// 删除观察者
virtual void DeleteHero(AbstractHero* hero) {
hero_ptr_list.remove(hero);
}
// 通知观察者
virtual void notify() {
for (list<AbstractHero*>::iterator it = hero_ptr_list.begin(); it != hero_ptr_list.end(); it++) {
(*it)->Updata();
}
}
public:
list<AbstractHero*> hero_ptr_list;
};

void Test01() {
AbstractHero* heroA = new HeroA;
AbstractHero* heroB = new HeroB;
AbstractHero* heroC = new HeroC;


AbstractBoss* boss_A = new BossA;
boss_A->AddHero(heroA);
boss_A->AddHero(heroB);
boss_A->AddHero(heroC);

cout << "______________heroC阵亡..." << endl;
boss_A->DeleteHero(heroC);

cout << "______________boss_A死了,通知其他英雄..." << endl;
boss_A->notify();
return;
}

13.4优缺点

优点:

  • 观察者模式支持广播通信,简化了一对多系统设计的难度。
  • 观察目标和观察者之间建立了一个抽象的耦合,具体观察者和具体观察目标之间没有过于紧密的耦合。

缺点:

  • 如果一个观察目标有多个直接或者间接的观察者,将所有观察者都通知到会花费很多时间。
  • 如果观察者和观察目标之间存在循环依赖,观察目标会触发他们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象怎么发生变化的,而仅仅是只知道发生了变化。

14.装饰模式Decorator

又叫包装模式。

14.1概念

  • 通过一种对客户端透明的方式来扩展对象功能,是继承关系的一种替代。
  • 动态地给一个对象增加一些额外的职责, 就增加对象功能来说,装饰模式比生成子类实现更为灵活。
  • 装饰模式是一种对 象结构型模式。

14.2角色

抽象构件(Component):

  • 它是具体构件和抽象装饰类的共同父类。
  • 声明了在具体构件中实现的业务方法。
  • 它的引入可以使客户端以一致的方式处理 未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。

具体构件(ConcreteComponent):

  • 用于定 义具体的构件对象,实现了在抽象构件中声明的方法。

抽象装饰类(Decorator):

  • 用于给具体构件增加职责。
  • 它维护一个指向抽象构件对象的引 用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法, 以达到装饰的目的。

具体装饰类(ConcreteDecorator):

  • 负责向构件添加新的职责。
  • 每一个具体装饰类都定义了一些新的行为,它可以调用在 抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

14.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
// 抽象英雄————【抽象构件】
class AbstractHero {
public:
virtual void ShowStatus() = 0;
virtual ~AbstractHero(){}
public:
int m_hp; // 血量
int m_mp; // 法术
int m_ap; // 攻击
int m_df; // 防御
};

// 具体英雄————【具体构件】
class HeroA : public AbstractHero {
public:
HeroA() {
m_hp = 0;
m_mp = 0;
m_ap = 0;
m_df = 0;
}
virtual void ShowStatus() {
cout << "血量:" << m_hp << endl;
cout << "魔法:" << m_mp << endl;
cout << "攻击:" << m_ap << endl;
cout << "防御:" << m_df << endl;
}
};

// 英雄穿上某些装饰物,还是个英雄。
// 【抽象装饰类】
class AbstractEquipment : public AbstractHero {
public:
AbstractEquipment(AbstractHero* hero) {
this->m_hero = hero;
}
virtual void ShowStatus() = 0;

public:
AbstractHero* m_hero;
};

// 狂徒————【具体装饰类】
class KuangtuEquipment : public AbstractEquipment {
public:
KuangtuEquipment(AbstractHero* hero) : AbstractEquipment(hero) {}

// 增加新的功能
void AddKuangtu() {
cout << "英雄穿上狂徒之后,防御力加30..." << endl;
this->m_hp = this->m_hero->m_hp;
this->m_mp = this->m_hero->m_mp;
this->m_ap = this->m_hero->m_ap;
this->m_df = this->m_hero->m_df + 30;
delete m_hero;
}

virtual void ShowStatus() {
AddKuangtu();
cout << "血量:" << m_hp << endl;
cout << "魔法:" << m_mp << endl;
cout << "攻击:" << m_ap << endl;
cout << "防御:" << m_df << endl;
}

public:
int m_hp; // 血量
int m_mp; // 法术
int m_ap; // 攻击
int m_df; // 防御
};

// 无尽之刃————【具体装饰类】
class WujinEquipment : public AbstractEquipment {
public:
WujinEquipment(AbstractHero* hero) : AbstractEquipment(hero) {}

// 增加新的功能
void AddWujin() {
cout << "英雄穿上无尽之刃之后,攻击力加100..." << endl;
this->m_hp = this->m_hero->m_hp;
this->m_mp = this->m_hero->m_mp;
this->m_ap = this->m_hero->m_ap + 100;
this->m_df = this->m_hero->m_df;
delete m_hero;
}

virtual void ShowStatus() {
AddWujin();
cout << "血量:" << m_hp << endl;
cout << "魔法:" << m_mp << endl;
cout << "攻击:" << m_ap << endl;
cout << "防御:" << m_df << endl;
}

public:
int m_hp; // 血量
int m_mp; // 法术
int m_ap; // 攻击
int m_df; // 防御
};

void Test01() {
AbstractHero* hero = new HeroA;
hero->ShowStatus();
cout << "---------------给英雄穿上衣服..." << endl;
hero = new KuangtuEquipment(hero);

hero->ShowStatus();
return;
}

本博客所有文章均个人原创,除特别声明外均采用 CC BY-SA 4.0协议,转载请注明出处!