9、设计模式

设计模式

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 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 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* arc = new Archer; 110 arc->attack(); 111 ​ 112 delete arc; 113 arc = nullptr; 114 115 return 0; 116 } ``` #### 2.2、使用函数指针实现策略模式 ``` 1 #include 2 #include 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 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 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 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 6 #include 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 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 ```

相关推荐
OpenC++5 分钟前
【C++QT】Layout 布局管理控件详解
c++·经验分享·qt·leetcode
前端大白话13 分钟前
Vue2和Vue3语法糖差异大揭秘:一文读懂,开发不纠结!
javascript·vue.js·设计模式
前端大白话19 分钟前
JavaScript中`Symbol.for()`和`Symbol()`的区别,在创建全局唯一的`Symbol`值时如何选择使用?
前端·javascript·设计模式
1白天的黑夜139 分钟前
贪心算法-860.柠檬水找零-力扣(LeetCode)
c++·算法·leetcode·贪心算法
CHQIUU42 分钟前
Java 设计模式心法之第25篇 - 中介者 (Mediator) - 用“中央协调”降低对象间耦合度
java·设计模式·中介者模式
BS_Li1 小时前
C++类和对象(上)
开发语言·c++·类和对象
超能力MAX1 小时前
关于C++中的指针值的传递
开发语言·c++
wuqingshun3141592 小时前
蓝桥杯 16. 密文搜索
c++·算法·职场和发展·蓝桥杯·深度优先
Pasregret2 小时前
备忘录模式:实现对象状态撤销与恢复的设计模式
运维·服务器·设计模式
LuckyRich12 小时前
【仿Mudou库one thread per loop式并发服务器实现】服务器边缘测试+性能测试
服务器·c++