设计模式
1、工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。工厂模式作为一种创建模式,一般在创建复杂对象时,考虑使用;在创建简单对象时,建议直接new完成一个实例对象的创建。
1.1、简单工厂模式
主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。使用简单工厂模式,我们只需要知道具体的产品型号就可以创建一个产品。
缺点:工厂类集中了所有产品类的创建逻辑,如果产品量较大,会使得工厂类变的非常臃肿。
1.2、工厂方法模式
定义一个创建对象的接口,其子类去具体现实这个接口以完成具体的创建工作。如果需要增加新的产品类,只需要扩展一个相应的工厂类即可。
缺点:产品类数据较多时,需要实现大量的工厂类,这无疑增加了代码量。
1 /*
2 关键代码:创建过程在其子类执行。
3 */
4
5 #include <iostream>
6
7 using namespace std;
8
9 //产品抽象类
10 class Tank
11 {
12 public:
13 virtual const string& type() = 0;
14 };
15
16 //具体的产品类
17 class Tank56 : public Tank
18 {
19 public:
20 Tank56():Tank(),m_strType("Tank56")
21 {
22 }
23
24 const string& type() override
25 {
26 cout << m_strType.data() << endl;
27 return m_strType;
28 }
29 private:
30 string m_strType;
31 };
32
33 //具体的产品类
34 class Tank96 : public Tank
35 {
36 public:
37 Tank96():Tank(),m_strType("Tank96")
38 {
39 }
40 const string& type() override
41 {
42 cout << m_strType.data() << endl;
43 return m_strType;
44 }
45
46 private:
47 string m_strType;
48 };
49
50 //抽象工厂类,提供一个创建接口
51 class TankFactory
52 {
53 public:
54 //提供创建产品实例的接口,返回抽象产品类
55 virtual Tank* createTank() = 0;
56 };
57
58 //具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
59 class Tank56Factory : public TankFactory
60 {
61 public:
62 Tank* createTank() override
63 {
64 return new Tank56();
65 }
66 };
67
68 //具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
69 class Tank96Factory : public TankFactory
70 {
71 public:
72 Tank* createTank() override
73 {
74 return new Tank96();
75 }
76 };
77
78
79 int main()
80 {
81 TankFactory* factory56 = new Tank56Factory();
82 Tank* tank56 = factory56->createTank();
83 tank56->type();
84
85 TankFactory* factory96 = new Tank96Factory();
86 Tank* tank96 = factory96->createTank();
87 tank96->type();
88
89 delete tank96;
90 tank96 = nullptr;
91 delete factory96;
92 factory96 = nullptr;
93
94 delete tank56;
95 tank56 = nullptr;
96 delete factory56;
97 factory56 = nullptr;
98
99 return 0;
100 }
1.3、抽象工厂模式
抽象工厂模式提供创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
当存在多个产品系列,而客户端只使用一个系列的产品时,可以考虑使用抽象工厂模式。
缺点:当增加一个新系列的产品时,不仅需要现实具体的产品类,还需要增加一个新的创建接口,扩展相对困难。
1 /*
2 * 关键代码:在一个工厂里聚合多个同类产品。
3 * 以下代码以白色衣服和黑色衣服为例,白色衣服为一个产品系列,黑色衣服为一个产品系列。白色上衣搭配白色裤子, 黑色上衣搭配黑色裤字。每个系列的衣服由一个对应的工厂创建,这样一个工厂创建的衣服能保证衣服为同一个系列。
4 */
5
6 //抽象上衣类
7 class Coat
8 {
9 public:
10 virtual const string& color() = 0;
11 };
12
13 //黑色上衣类
14 class BlackCoat : public Coat
15 {
16 public:
17 BlackCoat():Coat(),m_strColor("Black Coat")
18 {
19 }
20
21 const string& color() override
22 {
23 cout << m_strColor.data() << endl;
24 return m_strColor;
25 }
26 private:
27 string m_strColor;
28 };
29
30 //白色上衣类
31 class WhiteCoat : public Coat
32 {
33 public:
34 WhiteCoat():Coat(),m_strColor("White Coat")
35 {
36 }
37 const string& color() override
38 {
39 cout << m_strColor.data() << endl;
40 return m_strColor;
41 }
42
43 private:
44 string m_strColor;
45 };
46
47 //抽象裤子类
48 class Pants
49 {
50 public:
51 virtual const string& color() = 0;
52 };
53
54 //黑色裤子类
55 class BlackPants : public Pants
56 {
57 public:
58 BlackPants():Pants(),m_strColor("Black Pants")
59 {
60 }
61 const string& color() override
62 {
63 cout << m_strColor.data() << endl;
64 return m_strColor;
65 }
66
67 private:
68 string m_strColor;
69 };
70
71 //白色裤子类
72 class WhitePants : public Pants
73 {
74 public:
75 WhitePants():Pants(),m_strColor("White Pants")
76 {
77 }
78 const string& color() override
79 {
80 cout << m_strColor.data() << endl;
81 return m_strColor;
82 }
83
84 private:
85 string m_strColor;
86 };
87
88 //抽象工厂类,提供衣服创建接口
89 class Factory
90 {
91 public:
92 //上衣创建接口,返回抽象上衣类
93 virtual Coat* createCoat() = 0;
94 //裤子创建接口,返回抽象裤子类
95 virtual Pants* createPants() = 0;
96 };
97
98 //创建白色衣服的工厂类,具体实现创建白色上衣和白色裤子的接口
99 class WhiteFactory : public Factory
100 {
101 public:
102 Coat* createCoat() override
103 {
104 return new WhiteCoat();
105 }
106
107 Pants* createPants() override
108 {
109 return new WhitePants();
110 }
111 };
112
113 //创建黑色衣服的工厂类,具体实现创建黑色上衣和白色裤子的接口
114 class BlackFactory : public Factory
115 {
116 Coat* createCoat() override
117 {
118 return new BlackCoat();
119 }
120
121 Pants* createPants() override
122 {
123 return new BlackPants();
124 }
125 };
2、策略模式
策略模式是指定义一系列的算法,把它们单独封装起来,并且使它们可以互相替换,使得算法可以独立于使用它的客户端而变化,也是说这些算法所完成的功能类型是一样的,对外接口也是一样的,只是不同的策略为引起环境角色环境角色表现出不同的行为。
相比于使用大量的if...else,使用策略模式可以降低复杂度,使得代码更容易维护。
缺点:可能需要定义大量的策略类,并且这些策略类都要提供给客户端。
[环境角色] 持有一个策略类的引用,最终给客户端调用。
2.1、传统的策略模式实现
1 /*
2 * 关键代码:实现同一个接口。
3 * 以下代码实例中,以游戏角色不同的攻击方式为不同的策略,游戏角色即为执行不同策略的环境角色。
4 */
5
6 #include <iostream>
7
8 using namespace std;
9
10 //抽象策略类,提供一个接口
11 class Hurt
12 {
13 public:
14 virtual void blood() = 0;
15 };
16
17 //具体的策略实现类,具体实现接口, Adc持续普通攻击
18 class AdcHurt : public Hurt
19 {
20 public:
21 void blood() override
22 {
23 cout << "Adc hurt, Blood loss" << endl;
24 }
25 };
26
27 //具体的策略实现类,具体实现接口, Apc技能攻击
28 class ApcHurt : public Hurt
29 {
30 public:
31 void blood() override
32 {
33 cout << "Apc Hurt, Blood loss" << endl;
34 }
35 };
36
37 //环境角色类, 游戏角色战士,传入一个策略类指针参数。
38 class Soldier
39 {
40 public:
41 Soldier(Hurt* hurt):m_pHurt(hurt)
42 {
43 }
44 //在不同的策略下,该游戏角色表现出不同的攻击
45 void attack()
46 {
47 m_pHurt->blood();
48 }
49 private:
50 Hurt* m_pHurt;
51 };
52
53 //定义策略标签
54 typedef enum
55 {
56 Hurt_Type_Adc,
57 Hurt_Type_Apc,
58 Hurt_Type_Num
59 }HurtType;
60
61 //环境角色类, 游戏角色法师,传入一个策略标签参数。
62 class Mage
63 {
64 public:
65 Mage(HurtType type)
66 {
67 switch(type)
68 {
69 case Hurt_Type_Adc:
70 m_pHurt = new AdcHurt();
71 break;
72 case Hurt_Type_Apc:
73 m_pHurt = new ApcHurt();
74 break;
75 default:
76 break;
77 }
78 }
79 ~Mage()
80 {
81 delete m_pHurt;
82 m_pHurt = nullptr;
83 cout << "~Mage()" << endl;
84 }
85
86 void attack()
87 {
88 m_pHurt->blood();
89 }
90 private:
91 Hurt* m_pHurt;
92 };
93
94 //环境角色类, 游戏角色弓箭手,实现模板传递策略。
95 template<typename T>
96 class Archer
97 {
98 public:
99 void attack()
100 {
101 m_hurt.blood();
102 }
103 private:
104 T m_hurt;
105 };
106
107 int main()
108 {
109 Archer<ApcHurt>* arc = new Archer<ApcHurt>;
110 arc->attack();
111
112 delete arc;
113 arc = nullptr;
114
115 return 0;
116 }
2.2、使用函数指针实现策略模式
1 #include <iostream>
2 #include <functional>
3
4 void adcHurt()
5 {
6 std::cout << "Adc Hurt" << std::endl;
7 }
8
9 void apcHurt()
10 {
11 std::cout << "Apc Hurt" << std::endl;
12 }
13
14 //环境角色类, 使用传统的函数指针
15 class Soldier
16 {
17 public:
18 typedef void (*Function)();
19 Soldier(Function fun): m_fun(fun)
20 {
21 }
22 void attack()
23 {
24 m_fun();
25 }
26 private:
27 Function m_fun;
28 };
29
30 //环境角色类, 使用std::function<>
31 class Mage
32 {
33 public:
34 typedef std::function<void()> Function;
35
36 Mage(Function fun): m_fun(fun)
37 {
38 }
39 void attack()
40 {
41 m_fun();
42 }
43 private:
44 Function m_fun;
45 };
46
47 int main()
48 {
49 Soldier* soldier = new Soldier(apcHurt);
50 soldier->attack();
51 delete soldier;
52 soldier = nullptr;
53 return 0;
54 }
3、适配器模式
适配器模式可以将一个类的接口转换成客户端希望的另一个接口,使得原来由于接口不兼容而不能在一起工作的那些类可以在一起工作。通俗的讲就是当我们已经有了一些类,而这些类不能满足新的需求,此时就可以考虑是否能将现有的类适配成可以满足新需求的类。适配器类需要继承或依赖已有的类,实现想要的目标接口。
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
3.1、使用复合实现适配器模式
1 /*
2 * 关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
3 * 以下示例中,假设我们之前有了一个双端队列,新的需求要求使用栈和队列来完成。
4 双端队列可以在头尾删减或增加元素。而栈是一种先进后出的数据结构,添加数据时添加到栈的顶部,删除数据时先删 除栈顶部的数据。因此我们完全可以将一个现有的双端队列适配成一个栈。
5 */
6
7 //双端队列, 被适配类
8 class Deque
9 {
10 public:
11 void push_back(int x)
12 {
13 cout << "Deque push_back:" << x << endl;
14 }
15 void push_front(int x)
16 {
17 cout << "Deque push_front:" << x << endl;
18 }
19 void pop_back()
20 {
21 cout << "Deque pop_back" << endl;
22 }
23 void pop_front()
24 {
25 cout << "Deque pop_front" << endl;
26 }
27 };
28
29 //顺序类,抽象目标类
30 class Sequence
31 {
32 public:
33 virtual void push(int x) = 0;
34 virtual void pop() = 0;
35 };
36
37 //栈,后进先出, 适配类
38 class Stack:public Sequence
39 {
40 public:
41 //将元素添加到堆栈的顶部。
42 void push(int x) override
43 {
44 m_deque.push_front(x);
45 }
46 //从堆栈中删除顶部元素
47 void pop() override
48 {
49 m_deque.pop_front();
50 }
51 private:
52 Deque m_deque;
53 };
54
55 //队列,先进先出,适配类
56 class Queue:public Sequence
57 {
58 public:
59 //将元素添加到队列尾部
60 void push(int x) override
61 {
62 m_deque.push_back(x);
63 }
64 //从队列中删除顶部元素
65 void pop() override
66 {
67 m_deque.pop_front();
68 }
69 private:
70 Deque m_deque;
71 };
3.2、使用继承实现适配器模式
1 //双端队列,被适配类
2 class Deque
3 {
4 public:
5 void push_back(int x)
6 {
7 cout << "Deque push_back:" << x << endl;
8 }
9 void push_front(int x)
10 {
11 cout << "Deque push_front:" << x << endl;
12 }
13 void pop_back()
14 {
15 cout << "Deque pop_back" << endl;
16 }
17 void pop_front()
18 {
19 cout << "Deque pop_front" << endl;
20 }
21 };
22
23 //顺序类,抽象目标类
24 class Sequence
25 {
26 public:
27 virtual void push(int x) = 0;
28 virtual void pop() = 0;
29 };
30
31 //栈,后进先出, 适配类
32 class Stack:public Sequence, private Deque
33 {
34 public:
35 void push(int x)
36 {
37 push_front(x);
38 }
39 void pop()
40 {
41 pop_front();
42 }
43 };
44
45 //队列,先进先出,适配类
46 class Queue:public Sequence, private Deque
47 {
48 public:
49 void push(int x)
50 {
51 push_back(x);
52 }
53 void pop()
54 {
55 pop_front();
56 }
57 };
4、单例模式
单例模式顾名思义,保证一个类仅可以有一个实例化对象,并且提供一个可以访问它的全局接口。实现单例模式必须注意一下几点:
-
单例类只能由一个实例化对象。
-
单例类必须自己提供一个实例化对象。
-
单例类必须提供一个可以访问唯一实例化对象的接口。
单例模式分为懒汉和饿汉两种实现方式。
4.1、懒汉单例模式
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化一个对象。在访问量较小,甚至可能不会去访问的情况下,采用懒汉实现,这是以时间换空间。
4.1.1、非线程安全的懒汉单例模式
1 /*
2 * 关键代码:构造函数是私有的,不能通过赋值运算,拷贝构造等方式实例化对象。
3 */
4
5 //懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
6 class Singleton
7 {
8 public:
9 static Singleton* getInstance();
10 ~Singleton(){}
11
12 private:
13 Singleton(){} //构造函数私有
14 Singleton(const Singleton& obj) = delete; //明确拒绝
15 Singleton& operator=(const Singleton& obj) = delete; //明确拒绝
16
17 static Singleton* m_pSingleton;
18 };
19
20 Singleton* Singleton::m_pSingleton = NULL;
21
22 Singleton* Singleton::getInstance()
23 {
24 if(m_pSingleton == NULL)
25 {
26 m_pSingleton = new Singleton;
27 }
28 return m_pSingleton;
29 }
4.1.2、线程安全的懒汉单例模式
1 std::mutex mt;
2
3 class Singleton
4 {
5 public:
6 static Singleton* getInstance();
7 private:
8 Singleton(){} //构造函数私有
9 Singleton(const Singleton&) = delete; //明确拒绝
10 Singleton& operator=(const Singleton&) = delete; //明确拒绝
11
12 static Singleton* m_pSingleton;
13
14 };
15 Singleton* Singleton::m_pSingleton = NULL;
16
17 Singleton* Singleton::getInstance()
18 {
19 if(m_pSingleton == NULL)
20 {
21 mt.lock();
22 if(m_pSingleton == NULL)
23 {
24 m_pSingleton = new Singleton();
25 }
26 mt.unlock();
27 }
28 return m_pSingleton;
29 }
4.1.3、返回一个reference指向local static对象
这种单例模式实现方式多线程可能存在不确定性:任何一种non-const static对象,不论它是local或non-local,在多线程环境下"等待某事发生"都会有麻烦。解决的方法:在程序的单线程启动阶段手工调用所有reference-returning函数。这种实现方式的好处是不需要去delete它。
1 class Singleton
2 {
3 public:
4 static Singleton& getInstance();
5 private:
6 Singleton(){}
7 Singleton(const Singleton&) = delete; //明确拒绝
8 Singleton& operator=(const Singleton&) = delete; //明确拒绝
9 };
10
11
12 Singleton& Singleton::getInstance()
13 {
14 static Singleton singleton;
15 return singleton;
16 }
4.2、饿汉单例模式
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
1 //饿汉式:线程安全,注意一定要在合适的地方去delete它
2 class Singleton
3 {
4 public:
5 static Singleton* getInstance();
6 private:
7 Singleton(){} //构造函数私有
8 Singleton(const Singleton&) = delete; //明确拒绝
9 Singleton& operator=(const Singleton&) = delete; //明确拒绝
10
11 static Singleton* m_pSingleton;
12 };
13
14 Singleton* Singleton::m_pSingleton = new Singleton();
15
16 Singleton* Singleton::getInstance()
17 {
18 return m_pSingleton;
19 }
5、原型模式
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。通俗的讲就是当需要创建一个新的实例化对象时,我们刚好有一个实例化对象,但是已经存在的实例化对象又不能直接使用。这种情况下拷贝一个现有的实例化对象来用,可能会更方便。
以下情形可以考虑使用原型模式:
-
当new一个对象,非常繁琐复杂时,可以使用原型模式来进行复制一个对象。比如创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程。
-
当需要new一个新的对象,这个对象和现有的对象区别不大,我们就可以直接复制一个已有的对象,然后稍加修改。
-
当需要一个对象副本时,比如需要提供对象的数据,同时又需要避免外部对数据对象进行修改,那就拷贝一个对象副本供外部使用。
1 /*
2 * 关键代码:拷贝,return new className(*this);
3 */
4 #include <iostream>
5
6 using namespace std;
7
8 //提供一个抽象克隆基类。
9 class Clone
10 {
11 public:
12 virtual Clone* clone() = 0;
13 virtual void show() = 0;
14 };
15
16 //具体的实现类
17 class Sheep:public Clone
18 {
19 public:
20 Sheep(int id, string name):Clone(),
21 m_id(id),m_name(name)
22 {
23 cout << "Sheep() id address:" << &m_id << endl;
24 cout << "Sheep() name address:" << &m_name << endl;
25 }
26 ~Sheep()
27 {
28 }
29 //关键代码拷贝构造函数
30 Sheep(const Sheep& obj)
31 {
32 this->m_id = obj.m_id;
33 this->m_name = obj.m_name;
34 cout << "Sheep(const Sheep& obj) id address:" << &m_id << endl;
35 cout << "Sheep(const Sheep& obj) name address:" << &m_name << endl;
36 }
37 //关键代码克隆函数,返回return new Sheep(*this)
38 Clone* clone()
39 {
40 return new Sheep(*this);
41 }
42 void show()
43 {
44 cout << "id :" << m_id << endl;
45 cout << "name:" << m_name.data() << endl;
46 }
47 private:
48 int m_id;
49 string m_name;
50 };
51
52 int main()
53 {
54 Clone* s1 = new Sheep(1, "abs");
55 s1->show();
56 Clone* s2 = s1->clone();
57 s2->show();
58
59 delete s1;
60 s1 = nullptr;
61 delete s2;
62 s2 = nullptr;
63 return 0;
64 }
6、模板模式
模板模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
当多个类有相同的方法,并且逻辑相同,只是细节上有差异时,可以考虑使用模板模式。具体的实现上可以将相同的核心算法设计为模板方法,具体的实现细节有子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
以生产电脑为例,电脑生产的过程都是一样的,只是一些装配的器件可能不同而已。
1 /*
2 * 关键代码:在抽象类实现通用接口,细节变化在子类实现。
3 */
4
5 #include <iostream>
6
7 using namespace std;
8
9 class Computer
10 {
11 public:
12 void product()
13 {
14 installCpu();
15 installRam();
16 installGraphicsCard();
17 }
18
19 protected:
20 virtual void installCpu() = 0;
21 virtual void installRam() = 0;
22 virtual void installGraphicsCard() = 0;
23
24 };
25
26 class ComputerA : public Computer
27 {
28 protected:
29 void installCpu() override
30 {
31 cout << "ComputerA install Inter Core i5" << endl;
32 }
33
34 void installRam() override
35 {
36 cout << "ComputerA install 2G Ram" << endl;
37 }
38
39 void installGraphicsCard() override
40 {
41 cout << "ComputerA install Gtx940 GraphicsCard" << endl;
42 }
43 };
44
45 class ComputerB : public Computer
46 {
47 protected:
48 void installCpu() override
49 {
50 cout << "ComputerB install Inter Core i7" << endl;
51 }
52
53 void installRam() override
54 {
55 cout << "ComputerB install 4G Ram" << endl;
56 }
57
58 void installGraphicsCard() override
59 {
60 cout << "ComputerB install Gtx960 GraphicsCard" << endl;
61 }
62 };
63
64 int main()
65 {
66 ComputerB* c1 = new ComputerB();
67 c1->product();
68
69 delete c1;
70 c1 = nullptr;
71
72 return 0;
73 }
7、建造者模式
建造者模式:将复杂对象的构建和其表示分离,使得相同的构建过程可以产生不同的表示。
以下情形可以考虑使用建造者模式:
-
对象的创建复杂,但是其各个部分的子对象创建算法一定。
-
需求变化大,构造复杂对象的子对象经常变化,但将其组合在一起的算法相对稳定。
建造者模式的优点:
-
将对象的创建和表示分离,客户端不需要了解具体的构建细节。
-
增加新的产品对象时,只需要增加其具体的建造类即可,不需要修改原来的代码,扩展方便。
产品之间差异性大,内部变化较大、较复杂时不建议使用建造者模式。
1 /*
2 *关键代码:建造者类:创建和提供实例; Director类:管理建造出来的实例的依赖关系。
3 */
4
5 #include <iostream>
6 #include <string>
7
8 using namespace std;
9
10 //具体的产品类
11 class Order
12 {
13 public:
14 void setFood(const string& food)
15 {
16 m_strFood = food;
17 }
18
19 const string& food()
20 {
21 cout << m_strFood.data() << endl;
22 return m_strFood;
23 }
24
25 void setDrink(const string& drink)
26 {
27 m_strDrink = drink;
28 }
29
30 const string& drink()
31 {
32 cout << m_strDrink << endl;
33 return m_strDrink;
34 }
35
36 private:
37 string m_strFood;
38 string m_strDrink;
39 };
40
41 //抽象建造类,提供建造接口。
42 class OrderBuilder
43 {
44 public:
45 virtual ~OrderBuilder()
46 {
47 cout << "~OrderBuilder()" << endl;
48 }
49 virtual void setOrderFood() = 0;
50 virtual void setOrderDrink() = 0;
51 virtual Order* getOrder() = 0;
52 };
53
54 //具体的建造类
55 class VegetarianOrderBuilder : public OrderBuilder
56 {
57 public:
58 VegetarianOrderBuilder()
59 {
60 m_pOrder = new Order;
61 }
62
63 ~VegetarianOrderBuilder()
64 {
65 cout << "~VegetarianOrderBuilder()" << endl;
66 delete m_pOrder;
67 m_pOrder = nullptr;
68 }
69
70 void setOrderFood() override
71 {
72 m_pOrder->setFood("vegetable salad");
73 }
74
75 void setOrderDrink() override
76 {
77 m_pOrder->setDrink("water");
78 }
79
80 Order* getOrder() override
81 {
82 return m_pOrder;
83 }
84
85 private:
86 Order* m_pOrder;
87 };
88
89 //具体的建造类
90 class MeatOrderBuilder : public OrderBuilder
91 {
92 public:
93 MeatOrderBuilder()
94 {
95 m_pOrder = new Order;
96 }
97 ~MeatOrderBuilder()
98 {
99 cout << "~MeatOrderBuilder()" << endl;
100 delete m_pOrder;
101 m_pOrder = nullptr;
102 }
103
104 void setOrderFood() override
105 {
106 m_pOrder->setFood("beef");
107 }
108
109 void setOrderDrink() override
110 {
111 m_pOrder->setDrink("beer");
112 }
113
114 Order* getOrder() override
115 {
116 return m_pOrder;
117 }
118
119 private:
120 Order* m_pOrder;
121 };
122
123 //Director类,负责管理实例创建的依赖关系,指挥构建者类创建实例
124 class Director
125 {
126 public:
127 Director(OrderBuilder* builder) : m_pOrderBuilder(builder)
128 {
129 }
130 void construct()
131 {
132 m_pOrderBuilder->setOrderFood();
133 m_pOrderBuilder->setOrderDrink();
134 }
135
136 private:
137 OrderBuilder* m_pOrderBuilder;
138 };
139
140
141 int main()
142 {
143 // MeatOrderBuilder* mBuilder = new MeatOrderBuilder;
144 OrderBuilder* mBuilder = new MeatOrderBuilder; //注意抽象构建类必须有虚析构函数,解析时才会 调用子类的析构函数
145 Director* director = new Director(mBuilder);
146 director->construct();
147 Order* order = mBuilder->getOrder();
148 order->food();
149 order->drink();
150
151 delete director;
152 director = nullptr;
153
154 delete mBuilder;
155 mBuilder = nullptr;
156
157 return 0;
158 }
8、外观模式
外观模式:为子系统中的一组接口定义一个一致的界面;外观模式提供一个高层的接口,这个接口使得这一子系统更加容易被使用;对于复杂的系统,系统为客户端提供一个简单的接口,把负责的实现过程封装起来,客户端不需要连接系统内部的细节。
以下情形建议考虑外观模式:
-
设计初期阶段,应有意识的将不同层分离,层与层之间建立外观模式。
-
开发阶段,子系统越来越复杂,使用外观模式提供一个简单的调用接口。
-
一个系统可能已经非常难易维护和扩展,但又包含了非常重要的功能,可以为其开发一个外观类,使得新系统可以方便的与其交互。
优点:
-
实现了子系统与客户端之间的松耦合关系。
-
客户端屏蔽了子系统组件,减少了客户端所需要处理的对象数据,使得子系统使用起来更方便容易。
-
更好的划分了设计层次,对于后期维护更加的容易。
1 /*
2 * 关键代码:客户与系统之间加一个外观层,外观层处理系统的调用关系、依赖关系等。
3 *以下实例以电脑的启动过程为例,客户端只关心电脑开机的、关机的过程,并不需要了解电脑内部子系统的启动过程。
4 */
5 #include <iostream>
6
7 using namespace std;
8
9 //抽象控件类,提供接口
10 class Control
11 {
12 public:
13 virtual void start() = 0;
14 virtual void shutdown() = 0;
15 };
16
17 //子控件, 主机
18 class Host : public Control
19 {
20 public:
21 void start() override
22 {
23 cout << "Host start" << endl;
24 }
25 void shutdown() override
26 {
27 cout << "Host shutdown" << endl;
28 }
29 };
30
31 //子控件, 显示屏
32 class LCDDisplay : public Control
33 {
34 public:
35 void start() override
36 {
37 cout << "LCD Display start" << endl;
38 }
39 void shutdown() override
40 {
41 cout << "LCD Display shutdonw" << endl;
42 }
43 };
44
45 //子控件, 外部设备
46 class Peripheral : public Control
47 {
48 public:
49 void start() override
50