C++ 常用設計模式(學習筆記)

原文鏈接:https://www.cnblogs.com/chengjundu/p/8473564.html

設計模式

1、工廠模式

在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象。工廠模式作爲一種創建模式,一般在創建複雜對象時,考慮使用;在創建簡單對象時,建議直接new完成一個實例對象的創建。

1.1、簡單工廠模式

主要特點是需要在工廠類中做判斷,從而創造相應的產品,當增加新產品時,需要修改工廠類。使用簡單工廠模式,我們只需要知道具體的產品型號就可以創建一個產品。

缺點:工廠類集中了所有產品類的創建邏輯,如果產品量較大,會使得工廠類變的非常臃腫。

複製代碼

 1 /*
 2 關鍵代碼:創建過程在工廠類中完成。
 3 */
 4 ​
 5 #include <iostream>
 6 ​
 7 using namespace std;
 8 ​
 9 //定義產品類型信息
10 typedef enum
11 {
12     Tank_Type_56,
13     Tank_Type_96,
14     Tank_Type_Num
15 }Tank_Type;
16 ​
17 //抽象產品類
18 class Tank
19 {
20 public:
21     virtual const string& type() = 0;
22 };
23 ​
24 //具體的產品類
25 class Tank56 : public Tank
26 {
27 public:
28     Tank56():Tank(),m_strType("Tank56")
29     {
30     }
31 ​
32     const string& type() override
33     {
34         cout << m_strType.data() << endl;
35         return m_strType;
36     }
37 private:
38     string m_strType;
39 };
40 ​
41 //具體的產品類
42 class Tank96 : public Tank
43 {
44 public:
45     Tank96():Tank(),m_strType("Tank96")
46     {
47     }
48     const string& type() override
49     {
50         cout << m_strType.data() << endl;
51         return m_strType;
52     }
53 ​
54 private:
55     string m_strType;
56 }; 
57 ​
58 //工廠類
59 class TankFactory
60 {
61 public:
62     //根據產品信息創建具體的產品類實例,返回一個抽象產品類
63     Tank* createTank(Tank_Type type)
64     {
65         switch(type)
66         {
67         case Tank_Type_56:
68             return new Tank56();
69         case Tank_Type_96:
70             return new Tank96();
71         default:
72             return nullptr;
73         }
74     }
75 };
76 ​
77 ​
78 int main()
79 {
80     TankFactory* factory = new TankFactory();
81     Tank* tank56 = factory->createTank(Tank_Type_56);
82     tank56->type();
83     Tank* tank96 = factory->createTank(Tank_Type_96);
84     tank96->type();
85 ​
86     delete tank96;
87     tank96 = nullptr;
88     delete tank56;
89     tank56 = nullptr;
90     delete factory;
91     factory = nullptr;
92 ​
93     return 0;
94 }

複製代碼

 

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     {
51         cout << "Peripheral start" << endl;
52     }
53     void shutdown() override
54     {
55         cout << "Peripheral shutdown" << endl;
56     }
57 };
58 ​
59 class Computer
60 {
61 public:
62     void start()
63     {
64         m_host.start();
65         m_display.start();
66         m_peripheral.start();
67         cout << "Computer start" << endl;
68     }
69     void shutdown()
70     {
71         m_host.shutdown();
72         m_display.shutdown();
73         m_peripheral.shutdown();
74         cout << "Computer shutdown" << endl;
75     }
76 private:
77     Host   m_host;
78     LCDDisplay m_display;
79     Peripheral   m_peripheral;
80 };
81 ​
82 int main()
83 {
84     Computer computer;
85     computer.start();
86 ​
87     //do something
88 ​
89     computer.shutdown();
90 ​
91     return 0;
92 }

複製代碼

 

9、組合模式

組合模式:將對象組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得客戶端對單個對象和組合對象的使用具有一直性。

既然講到以樹形結構表示“部分-整體”,那可以將組合模式想象成一根大樹,將大樹分成樹枝和樹葉兩部分,樹枝上可以再長樹枝,也可以長樹葉,樹葉上則不能再長出別的東西。

以下情況可以考慮使用組合模式:

  • 希望表示對象的部分-整體層次結構。

  • 希望客戶端忽略組合對象與單個對象的不同,客戶端將統一的使用組合結構中的所有對象。

複製代碼

  1 /*
  2 * 關鍵代碼:樹枝內部組合該接口,並且含有內部屬性list,裏面放Component。
  3 */
  4 
  5 #include <iostream>
  6 #include <list>
  7 #include <memory>
  8 
  9 using namespace std;
 10 
 11 //抽象類,提供組合和單個對象的一致接口
 12 class Company
 13 {
 14 public:
 15     Company(const string& name): m_name(name){}
 16     virtual ~Company(){ cout << "~Company()" << endl;}
 17 
 18     virtual void add(Company* ) = 0;
 19     virtual void remove(const string&) = 0;
 20     virtual void display(int depth) = 0;
 21 
 22     virtual const string& name()
 23     {
 24         return m_name;
 25     }
 26 
 27 protected:
 28     string m_name;
 29 };
 30 
 31 //具體的單個對象實現類,“樹枝”類
 32 class HeadCompany : public Company
 33 {
 34 public:
 35     HeadCompany(const string& name): Company(name){}
 36     virtual ~HeadCompany(){ cout << "~HeadCompany()" << endl;}
 37 
 38     void add(Company* company) override
 39     {
 40         shared_ptr<Company> temp(company);
 41         m_companyList.push_back(temp);
 42     }
 43 
 44     void remove(const string& strName) override
 45     {
 46         list<shared_ptr<Company>>::iterator iter = m_companyList.begin();
 47         for(; iter != m_companyList.end(); iter++)
 48         {
 49             if((*iter).get()->name() == strName)
 50             {
 51             //不應該在此處使用list<T>.erase(list<T>::iterator iter),會導致iter++錯誤,這裏刪除目               標元素之後,必須return。
 52                 m_companyList.erase(iter);
 53                 return;
 54             }
 55         }
 56     }
 57 
 58     void display(int depth) override
 59     {
 60         for(int i = 0; i < depth; i++)
 61         {
 62             cout << "-";
 63         }
 64         cout << this->name().data() << endl;
 65         list<shared_ptr<Company>>::iterator iter = m_companyList.begin();
 66         for(; iter!= m_companyList.end(); iter++)
 67         {
 68             (*iter).get()->display(depth + 1);
 69         }
 70     }
 71 
 72 private:
 73     list<shared_ptr<Company>> m_companyList;
 74 };
 75 
 76 //具體的單個對象實現類,“樹葉”類
 77 class ResearchCompany : public Company
 78 {
 79 public:
 80     ResearchCompany(const string& name): Company(name){}
 81     virtual ~ResearchCompany(){ cout << "~ResearchCompany()" << endl;}
 82 
 83     void add(Company* ) override
 84     {
 85     }
 86 
 87     void remove(const string&) override
 88     {
 89     }
 90 
 91     void display(int depth) override
 92     {
 93         for(int i = 0; i < depth; i++)
 94         {
 95             cout << "-";
 96         }
 97         cout << m_name.data() << endl;
 98     }
 99 };
100 
101 //具體的單個對象實現類,“樹葉”類
102 class SalesCompany : public Company
103 {
104 public:
105     SalesCompany(const string& name): Company(name){}
106     virtual ~SalesCompany(){ cout << "~SalesCompany()" << endl;}
107 
108     void add(Company* ) override
109     {
110     }
111 
112     void remove(const string&) override
113     {
114     }
115 
116     void display(int depth) override
117     {
118         for(int i = 0; i < depth; i++)
119         {
120             cout << "-";
121         }
122         cout << m_name.data() << endl;
123     }
124 };
125 
126 //具體的單個對象實現類,“樹葉”類
127 class FinanceCompany : public Company
128 {
129 public:
130     FinanceCompany(const string& name): Company(name){}
131     virtual ~FinanceCompany(){ cout << "~FinanceCompany()" << endl;}
132 
133     void add(Company* ) override
134     {
135     }
136 
137     void remove(const string&) override
138     {
139     }
140 
141     void display(int depth) override
142     {
143         for(int i = 0; i < depth; i++)
144         {
145             cout << "-";
146         }
147         cout << m_name.data() << endl;
148     }
149 };
150 
151 
152 int main()
153 {
154     HeadCompany* headRoot = new HeadCompany("Head Root Company");
155 
156     HeadCompany* childRoot1 = new HeadCompany("Child Company A");
157     ResearchCompany* r1 = new ResearchCompany("Research Company A");
158     SalesCompany* s1 = new SalesCompany("Sales Company A");
159     SalesCompany* s2 = new SalesCompany("Sales Company B");
160     FinanceCompany* f1 = new FinanceCompany("FinanceCompany A");
161     
162     childRoot1->add(r1);
163     childRoot1->add(s1);
164     childRoot1->add(s2);
165     childRoot1->add(f1);
166 
167     HeadCompany* childRoot2 = new HeadCompany("Child Company B");
168     ResearchCompany* r2 = new ResearchCompany("Research Company B");
169     SalesCompany* s3 = new SalesCompany("Sales Company C");
170     SalesCompany* s4 = new SalesCompany("Sales Company D");
171     FinanceCompany* f2 = new FinanceCompany("FinanceCompany B");
172     
173     childRoot2->add(r2);
174     childRoot2->add(s3);
175     childRoot2->add(s4);
176     childRoot2->add(f2);
177 
178     headRoot->add(childRoot1);
179     headRoot->add(childRoot2);
180     headRoot->display(1);
181     
182     cout << "\n***************\n" << endl;
183 
184     childRoot1->remove("Sales Company B");
185     headRoot->display(1);
186     
187     cout << "\n***************\n" << endl;
188 
189     delete headRoot;
190     headRoot = nullptr;
191 
192     return 0;
193 }

複製代碼

 

10、代理模式

代理模式:爲其它對象提供一種代理以控制這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介作用。

優點:

  • 職責清晰。真實的角色只負責實現實際的業務邏輯,不用關心其它非本職責的事務,通過後期的代理完成具體的任務。這樣代碼會簡潔清晰。

  • 代理對象可以在客戶端和目標對象之間起到中介的作用,這樣就保護了目標對象。

  • 擴展性好。

複製代碼

 1 /*
 2 * 關鍵代碼:一個是真正的你要訪問的對象(目標類),一個是代理對象,真正對象與代理對象實現同一個接口,先訪問代理*         類再訪問真正要訪問的對象。
 3 */
 4 #include <iostream>
 5 ​
 6 using namespace std;
 7 ​
 8 class Gril
 9 {
10 public:
11     Gril(const string& name = "gril"):m_string(name){}
12     string getName()
13     {
14         return m_string;
15     }
16 private:
17     string m_string;
18 };
19 ​
20 class Profession
21 {
22 public:
23     virtual ~Profession(){}
24     virtual void profess() = 0;
25 };
26 ​
27 class YoungMan : public Profession
28 {
29 public:
30     YoungMan(const Gril& gril):m_gril(gril){}
31     void profess()
32     {
33         cout << "Young man love " << m_gril.getName().data() << endl;
34     }
35 ​
36 private:
37     Gril m_gril;
38 };
39 ​
40 class ManProxy : public Profession
41 {
42 public:
43     ManProxy(const Gril& gril):m_pMan(new YoungMan(gril)){}
44     ~ManProxy()
45     {
46         delete m_pMan;
47         m_pMan = nullptr;
48     }
49     void profess()
50     {
51         m_pMan->profess();
52     }
53 private:
54     YoungMan* m_pMan;
55 };
56 ​
57 int main(int argc, char *argv[])
58 {
59     Gril gril("heihei");
60     ManProxy* proxy = new ManProxy(gril);
61     proxy->profess();
62 ​
63     delete proxy;
64     proxy = nullptr;
65     return 0;
66 }

複製代碼

 

11、享元模式

享元模式:運用共享技術有效地支持大量細粒度的對象。在有大量對象時,把其中共同的部分抽象出來,如果有相同的業務請求,直接返回內存中已有的對象,避免重新創建。

以下情況可以考慮使用享元模式:

  • 系統中有大量的對象,這些對象消耗大量的內存,且這些對象的狀態可以被外部化。

對於享元模式,需要將對象的信息分爲兩個部分:內部狀態和外部狀態。內部狀態是指被共享出來的信息,儲存在享元對象內部且不隨環境變化而改變;外部狀態是不可以共享的,它隨環境改變而改變,是由客戶端控制的。

複製代碼

  1 /*
  2 * 關鍵代碼:將內部狀態作爲標識,進行共享。
  3 */
  4 #include <iostream>
  5 #include <map>
  6 #include <memory>
  7 ​
  8 using namespace std;
  9 ​
 10 //抽象享元類,提供享元類外部接口。
 11 class AbstractConsumer
 12 {
 13 public:
 14     virtual ~AbstractConsumer(){}
 15     virtual void setArticle(const string&) = 0;
 16     virtual const string& article() = 0;
 17 };
 18 ​
 19 //具體的享元類
 20 class Consumer : public AbstractConsumer
 21 {
 22 public:
 23     Consumer(const string& strName) : m_user(strName){}
 24     ~Consumer()
 25     {
 26         cout << " ~Consumer()" << endl;
 27     }
 28 ​
 29     void setArticle(const string& info) override
 30     {
 31         m_article = info;
 32     }
 33 ​
 34     const string& article() override
 35     {
 36         return m_article;
 37     }
 38 ​
 39 private:
 40     string m_user;
 41     string m_article;
 42 };
 43 ​
 44 //享元工廠類
 45 class Trusteeship
 46 {
 47 public:
 48     ~Trusteeship()
 49     {
 50          m_consumerMap.clear();
 51     }
 52 ​
 53     void hosting(const string& user, const string& article)
 54     {
 55         if(m_consumerMap.count(user))
 56         {
 57             cout << "A customer named " << user.data() << " already exists" << endl;
 58             Consumer* consumer = m_consumerMap.at(user).get();
 59             consumer->setArticle(article);
 60         }
 61         else
 62         {
 63             shared_ptr<Consumer> consumer(new Consumer(user));
 64             consumer.get()->setArticle(article);
 65             m_consumerMap.insert(pair<string, shared_ptr<Consumer>>(user, consumer));
 66         }
 67     }
 68 ​
 69     void display()
 70     {
 71         map<string, shared_ptr<Consumer>>::iterator iter = m_consumerMap.begin();
 72         for(; iter != m_consumerMap.end(); iter++)
 73         {
 74             cout << iter->first.data() << " : "<< iter->second.get()->article().data() << endl;
 75         }
 76     }
 77 ​
 78 private:
 79     map<string, shared_ptr<Consumer>> m_consumerMap;
 80 };
 81 ​
 82 ​
 83 int main()
 84 {
 85     Trusteeship* ts = new Trusteeship;
 86     ts->hosting("zhangsan", "computer");
 87     ts->hosting("lisi", "phone");
 88     ts->hosting("wangwu", "watch");
 89 ​
 90     ts->display();
 91 ​
 92     ts->hosting("zhangsan", "TT");
 93     ts->hosting("lisi", "TT");
 94     ts->hosting("wangwu", "TT");
 95 ​
 96     ts->display();
 97 ​
 98     delete ts;
 99     ts = nullptr;
100 ​
101     return 0;
102 }

複製代碼

 

12、橋接模式

橋接模式:將抽象部分與實現部分分離,使它們都可以獨立變換。

以下情形考慮使用橋接模式:

  • 當一個對象有多個變化因素的時候,考慮依賴於抽象的實現,而不是具體的實現。

  • 當多個變化因素在多個對象間共享時,考慮將這部分變化的部分抽象出來再聚合/合成進來。

  • 當一個對象的多個變化因素可以動態變化的時候。

優點:

  • 將實現抽離出來,再實現抽象,使得對象的具體實現依賴於抽象,滿足了依賴倒轉原則。

  • 更好的可擴展性。

  • 可動態的切換實現。橋接模式實現了抽象和實現的分離,在實現橋接模式時,就可以實現動態的選擇具體的實現。

複製代碼

 1 /*
 2 * 關鍵代碼:將現實獨立出來,抽象類依賴現實類。
 3 * 以下示例中,將各類App、各類手機獨立開來,實現各種App和各種手機的自由橋接。
 4 */
 5 #include <iostream>
 6 ​
 7 using namespace std;
 8 ​
 9 //抽象App類,提供接口
10 class App
11 {
12 public:
13     virtual ~App(){ cout << "~App()" << endl; }
14     virtual void run() = 0;
15 };
16 ​
17 //具體的App實現類
18 class GameApp:public App
19 {
20 public:
21     void run()
22     {
23         cout << "GameApp Running" << endl;
24     }
25 };
26 ​
27 //具體的App實現類
28 class TranslateApp:public App
29 {
30 public:
31     void run()
32     {
33         cout << "TranslateApp Running" << endl;
34     }
35 };
36 ​
37 //抽象手機類,提供接口
38 class MobilePhone
39 {
40 public:
41     virtual ~MobilePhone(){ cout << "~MobilePhone()" << endl;}
42     virtual void appRun(App* app) = 0;  //實現App與手機的橋接
43 };
44 ​
45 //具體的手機實現類
46 class XiaoMi:public MobilePhone
47 {
48 public:
49     void appRun(App* app)
50     {
51         cout << "XiaoMi: ";
52         app->run();
53     }
54 };
55 ​
56 //具體的手機實現類
57 class HuaWei:public MobilePhone
58 {
59 public:
60     void appRun(App* app)
61     {
62         cout << "HuaWei: ";
63         app->run();
64     }
65 };
66 ​
67 int main()
68 {
69     App* gameApp = new GameApp;
70     App* translateApp = new TranslateApp;
71     MobilePhone* mi = new XiaoMi;
72     MobilePhone* hua = new HuaWei;
73     mi->appRun(gameApp);
74     mi->appRun(translateApp);
75     hua->appRun(gameApp);
76     hua->appRun(translateApp);
77 ​
78     delete hua;
79     hua = nullptr;
80     delete mi;
81     mi = nullptr;
82     delete gameApp;
83     gameApp = nullptr;
84     delete translateApp;
85     translateApp = nullptr;
86 ​
87     return 0;
88 }

複製代碼

 

13、裝飾模式

裝飾模式:動態地給一個對象添加一些額外的功能,它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。新增加功能來說,裝飾器模式比生產子類更加靈活。

以下情形考慮使用裝飾模式:

  • 需要擴展一個類的功能,或給一個類添加附加職責。

  • 需要動態的給一個對象添加功能,這些功能可以再動態的撤銷。

  • 需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關係變的不現實。

  • 當不能採用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因爲類定義被隱藏,或類定義不能用於生成子類。

複製代碼

  1 /*
  2 * 關鍵代碼:1、Component 類充當抽象角色,不應該具體實現。 2、修飾類引用和繼承 Component 類,具體擴展類重寫父類方法。
  3 */
  4 #include <iostream>
  5 ​
  6 using namespace std;
  7 ​
  8 //抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。
  9 class Component
 10 {
 11 public:
 12     virtual ~Component(){}
 13 ​
 14     virtual void configuration() = 0;
 15 };
 16 ​
 17 //具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
 18 class Car : public Component
 19 {
 20 public:
 21     void configuration() override
 22     {
 23         cout << "A Car" << endl;
 24     }
 25 };
 26 ​
 27 //裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並實現一個與抽象構件接口一致的接口。
 28 class DecorateCar : public Component
 29 {
 30 public:
 31     DecorateCar(Component* car) : m_pCar(car){}
 32 ​
 33     void configuration() override
 34     {
 35         m_pCar->configuration();
 36     }
 37 ​
 38 private:
 39     Component* m_pCar;
 40 };
 41 ​
 42 //具體裝飾(Concrete Decorator)角色:負責給構件對象添加上附加的責任。
 43 class DecorateLED : public DecorateCar
 44 {
 45 public:
 46     DecorateLED(Component* car) : DecorateCar(car){}
 47 ​
 48     void configuration() override
 49     {
 50         DecorateCar::configuration();
 51         addLED();
 52     }
 53 ​
 54 private:
 55     void addLED()
 56     {
 57         cout << "Install LED" << endl;
 58     }
 59 ​
 60 };
 61 ​
 62 //具體裝飾(Concrete Decorator)角色:負責給構件對象添加上附加的責任。
 63 class DecoratePC : public DecorateCar
 64 {
 65 public:
 66     DecoratePC(Component* car) : DecorateCar(car){}
 67 ​
 68     void configuration() override
 69     {
 70         DecorateCar::configuration();
 71         addPC();
 72     }
 73 ​
 74 private:
 75     void addPC()
 76     {
 77         cout << "Install PC" << endl;
 78     }
 79 };
 80 ​
 81 //具體裝飾(Concrete Decorator)角色:負責給構件對象添加上附加的責任。
 82 class DecorateEPB : public DecorateCar
 83 {
 84 public:
 85     DecorateEPB(Component* car) : DecorateCar(car){}
 86 ​
 87     void configuration() override
 88     {
 89         DecorateCar::configuration();
 90         addEPB();
 91     }
 92 ​
 93 private:
 94     void addEPB()
 95     {
 96         cout << "Install Electrical Park Brake" << endl;
 97     }
 98 };
 99 ​
100 int main()
101 {
102     Car* car = new Car;
103     DecorateLED* ledCar = new DecorateLED(car);
104     DecoratePC* pcCar = new DecoratePC(ledCar);
105     DecorateEPB* epbCar = new DecorateEPB(pcCar);
106 ​
107     epbCar->configuration();
108 ​
109     delete epbCar;
110     epbCar = nullptr;
111 ​
112     delete pcCar;
113     pcCar = nullptr;
114 ​
115     delete ledCar;
116     ledCar = nullptr;
117 ​
118     delete car;
119     car = nullptr;
120 ​
121     return 0;
122 }

複製代碼

 

14、備忘錄模式

備忘錄模式:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可以將該對象恢復到原來保存的狀態。

備忘錄模式中需要定義的角色類:

  1. Originator(發起人):負責創建一個備忘錄Memento,用以記錄當前時刻自身的內部狀態,並可使用備忘錄恢復內部狀態。Originator可以根據需要決定Memento存儲自己的哪些內部狀態。

  1. Memento(備忘錄):負責存儲Originator對象的內部狀態,並可以防止Originator以外的其他對象訪問備忘錄。備忘錄有兩個接口:Caretaker只能看到備忘錄的窄接口,他只能將備忘錄傳遞給其他對象。Originator卻可看到備忘錄的寬接口,允許它訪問返回到先前狀態所需要的所有數據。

  1. Caretaker(管理者):負責備忘錄Memento,不能對Memento的內容進行訪問或者操作。

複製代碼

  1 /*
  2 * 關鍵代碼:Memento類、Originator類、Caretaker類;Originator類不與Memento類耦合,而是與Caretaker類耦合。
  3 */
  4 ​
  5 include <iostream>
  6 ​
  7 using namespace std;
  8 ​
  9 //需要保存的信息
 10 typedef struct  
 11 {
 12     int grade;
 13     string arm;
 14     string corps;
 15 }GameValue;
 16 ​
 17 //Memento類
 18 class Memento   
 19 {
 20 public:
 21     Memento(){}
 22     Memento(GameValue value):m_gameValue(value){}
 23     GameValue getValue()
 24     {
 25         return m_gameValue;
 26     }
 27 private:
 28     GameValue m_gameValue;
 29 };
 30 ​
 31 //Originator類
 32 class Game   
 33 {
 34 public:
 35     Game(GameValue value):m_gameValue(value)
 36     {}
 37     void addGrade()  //等級增加
 38     {
 39         m_gameValue.grade++;
 40     }
 41     void replaceArm(string arm)  //更換武器
 42     {
 43         m_gameValue.arm = arm;
 44     }
 45     void replaceCorps(string corps)  //更換工會
 46     {
 47         m_gameValue.corps = corps;
 48     }
 49     Memento saveValue()    //保存當前信息
 50     {
 51         Memento memento(m_gameValue);
 52         return memento;
 53     }
 54     void load(Memento memento) //載入信息
 55     {
 56         m_gameValue = memento.getValue();
 57     }
 58     void showValue()
 59     {
 60         cout << "Grade: " << m_gameValue.grade << endl;
 61         cout << "Arm  : " << m_gameValue.arm.data() << endl;
 62         cout << "Corps: " << m_gameValue.corps.data() << endl;
 63     }
 64 private:
 65     GameValue m_gameValue;
 66 };
 67 ​
 68 //Caretaker類
 69 class Caretake 
 70 {
 71 public:
 72     void save(Memento memento)  //保存信息
 73     {
 74         m_memento = memento;
 75     }
 76     Memento load()            //讀已保存的信息
 77     {
 78         return m_memento;
 79     }
 80 private:
 81     Memento m_memento;
 82 };
 83 ​
 84 int main()
 85 {
 86     GameValue v1 = {0, "Ak", "3K"};
 87     Game game(v1);    //初始值
 88     game.addGrade();
 89     game.showValue();
 90     cout << "----------" << endl;
 91     Caretake care;
 92     care.save(game.saveValue());  //保存當前值
 93     game.addGrade();          //修改當前值
 94     game.replaceArm("M16");
 95     game.replaceCorps("123");
 96     game.showValue();
 97     cout << "----------" << endl;
 98     game.load(care.load());   //恢復初始值
 99     game.showValue();
100     return 0;
101 }

複製代碼

 

15、中介者模式

中介者模式:用一箇中介對象來封裝一系列的對象交互,中介者使各對象不需要顯示地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之前的交互。

如果對象與對象之前存在大量的關聯關係,若一個對象改變,常常需要跟蹤與之關聯的對象,並做出相應的處理,這樣勢必會造成系統變得複雜,遇到這種情形可以考慮使用中介者模式。當多個對象存在關聯關係時,爲它們設計一箇中介對象,當一個對象改變時,只需要通知它的中介對象,再由它的中介對象通知每個與它相關的對象。

複製代碼

  1 /*
  2 * 關鍵代碼:將相關對象的通信封裝到一個類中單獨處理。
  3 */
  4 #include <iostream>
  5 ​
  6 using namespace std;
  7 ​
  8 class Mediator;
  9 ​
 10 //抽象同事類。
 11 class Businessman
 12 {
 13 public:
 14     Businessman(){}
 15     Businessman(Mediator* mediator) : m_pMediator(mediator){}
 16 ​
 17     virtual ~Businessman(){}
 18 ​
 19     virtual void setMediator(Mediator* m)
 20     {
 21         m_pMediator = m;
 22     }
 23 ​
 24     virtual void sendMessage(const string& msg) = 0;
 25     virtual void getMessage(const string& msg) = 0;
 26 ​
 27 protected:
 28     Mediator* m_pMediator;
 29 };
 30 ​
 31 //抽象中介者類。
 32 class  Mediator
 33 {
 34 public:
 35     virtual ~Mediator(){}
 36     virtual void setBuyer(Businessman* buyer) = 0;
 37     virtual void setSeller(Businessman* seller) = 0;
 38     virtual void send(const string& msg, Businessman* man) = 0;
 39 };
 40 ​
 41 //具體同事類
 42 class Buyer : public Businessman
 43 {
 44 public:
 45     Buyer() : Businessman(){}
 46     Buyer(Mediator* mediator) : Businessman(mediator){}
 47 ​
 48     void sendMessage(const string& msg) override
 49     {
 50         m_pMediator->send(msg, this);
 51     }
 52 ​
 53     void getMessage(const string& msg)
 54     {
 55         cout << "Buyer recv: " << msg.data() << endl;
 56     }
 57 };
 58 ​
 59 //具體同事類
 60 class Seller : public Businessman
 61 {
 62 public:
 63     Seller() : Businessman(){}
 64     Seller(Mediator* mediator) : Businessman(mediator){}
 65 ​
 66     void sendMessage(const string& msg) override
 67     {
 68         m_pMediator->send(msg, this);
 69     }
 70 ​
 71     void getMessage(const string& msg)
 72     {
 73         cout << "Seller recv: " << msg.data() << endl;
 74     }
 75 };
 76 ​
 77 //具體中介者類
 78 class HouseMediator : public Mediator
 79 {
 80 public:
 81     void setBuyer(Businessman* buyer) override
 82     {
 83         m_pBuyer = buyer;
 84     }
 85 ​
 86     void setSeller(Businessman* seller) override
 87     {
 88         m_pSeller = seller;
 89     }
 90 ​
 91     void send(const string& msg, Businessman* man) override
 92     {
 93         if(man == m_pBuyer)
 94         {
 95             m_pSeller->getMessage(msg);
 96         }
 97         else if(man == m_pSeller)
 98         {
 99             m_pBuyer->getMessage(msg);
100         }
101     }
102 ​
103 private:
104     Businessman* m_pBuyer;
105     Businessman* m_pSeller;
106 };
107 ​
108 int main()
109 {
110     HouseMediator* hMediator = new HouseMediator;
111     Buyer* buyer = new Buyer(hMediator);
112     Seller* seller = new Seller(hMediator);
113 ​
114     hMediator->setBuyer(buyer);
115     hMediator->setSeller(seller);
116 ​
117     buyer->sendMessage("Sell not to sell?");
118     seller->sendMessage("Of course selling!");
119 ​
120     delete buyer;
121     buyer = nullptr;
122 ​
123     delete seller;
124     seller = nullptr;
125 ​
126     delete hMediator;
127     hMediator = nullptr;
128 ​
129 ​
130     return 0;
131 }

複製代碼

 

16、職責鏈模式

職責鏈模式:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之前的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞請求,直到有一個對象處理它爲止。

職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無需關心請求的處理細節和請求的傳遞,所有職責鏈將請求的發送者和請求的處理者解耦了。

複製代碼

 1 /*
 2 * 關鍵代碼:Handler內指明其上級,handleRequest()裏判斷是否合適,不合適則傳遞給上級。
 3 */
 4 #include <iostream>
 5 ​
 6 using namespace std;
 7 ​
 8 enum RequestLevel
 9 {
10     Level_One = 0,
11     Level_Two,
12     Level_Three,
13     Level_Num
14 };
15 ​
16 //抽象處理者(Handler)角色,提供職責鏈的統一接口。
17 class Leader
18 {
19 public:
20     Leader(Leader* leader):m_leader(leader){}
21     virtual ~Leader(){}
22     virtual void handleRequest(RequestLevel level) = 0;
23 protected:
24     Leader* m_leader;
25 };
26 ​
27 //具體處理者(Concrete Handler)角色
28 class Monitor:public Leader   //鏈釦1
29 {
30 public:
31     Monitor(Leader* leader):Leader(leader){}
32     void handleRequest(RequestLevel level)
33     {
34         if(level < Level_Two)
35         {
36             cout << "Mointor handle request : " << level << endl;
37         }
38         else
39         {
40             m_leader->handleRequest(level);
41         }
42     }
43 };
44 ​
45 //具體處理者(Concrete Handler)角色
46 class Captain:public Leader    //鏈釦2
47 {
48 public:
49     Captain(Leader* leader):Leader(leader){}
50     void handleRequest(RequestLevel level)
51     {
52         if(level < Level_Three)
53         {
54             cout << "Captain handle request : " << level << endl;
55         }
56         else
57         {
58             m_leader->handleRequest(level);
59         }
60     }
61 };
62 ​
63 //具體處理者(Concrete Handler)角色
64 class General:public Leader   //鏈釦3
65 {
66 public:
67     General(Leader* leader):Leader(leader){}
68     void handleRequest(RequestLevel level)
69     {
70         cout << "General handle request : " << level << endl;
71     }
72 };
73 ​
74 int main()
75 {
76     Leader* general = new General(nullptr);
77     Leader* captain = new Captain(general);
78     Leader* monitor = new Monitor(captain);
79     monitor->handleRequest(Level_One);
80 ​
81     delete monitor;
82     monitor = nullptr;
83     delete captain;
84     captain = nullptr;
85     delete general;
86     general = nullptr;
87     return 0;
88 }

複製代碼

 

17、觀察者模式

觀察者模式:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都要得到通知並自動更新。

觀察者模式從根本上講必須包含兩個角色:觀察者和被觀察對象。

  • 被觀察對象自身應該包含一個容器來存放觀察者對象,當被觀察者自身發生改變時通知容器內所有的觀察者對象自動更新。

  • 觀察者對象可以註冊到被觀察者的中,完成註冊後可以檢測被觀察者的變化,接收被觀察者的通知。當然觀察者也可以被註銷掉,停止對被觀察者的監控。

複製代碼

  1 /*
  2 * 關鍵代碼:在目標類中增加一個ArrayList來存放觀察者們。
  3 */
  4 #include <iostream>
  5 #include <list>
  6 #include <memory>
  7 ​
  8 using namespace std;
  9 ​
 10 class View;
 11 ​
 12 //被觀察者抽象類   數據模型
 13 class DataModel
 14 {
 15 public:
 16     virtual ~DataModel(){}
 17     virtual void addView(View* view) = 0;
 18     virtual void removeView(View* view) = 0;
 19     virtual void notify() = 0;   //通知函數
 20 };
 21 ​
 22 //觀察者抽象類   視圖
 23 class View
 24 {
 25 public:
 26     virtual ~View(){ cout << "~View()" << endl; }
 27     virtual void update() = 0;
 28     virtual void setViewName(const string& name) = 0;
 29     virtual const string& name() = 0;
 30 };
 31 ​
 32 //具體的被觀察類, 整數模型
 33 class IntDataModel:public DataModel
 34 {
 35 public:
 36     ~IntDataModel()
 37     {
 38         m_pViewList.clear();
 39     }
 40 ​
 41     virtual void addView(View* view) override
 42     {
 43         shared_ptr<View> temp(view);
 44         auto iter = find(m_pViewList.begin(), m_pViewList.end(), temp);
 45         if(iter == m_pViewList.end())
 46         {
 47             m_pViewList.push_front(temp);
 48         }
 49         else
 50         {
 51             cout << "View already exists" << endl;
 52         }
 53     }
 54 ​
 55     void removeView(View* view) override
 56     {
 57         auto iter = m_pViewList.begin();
 58         for(; iter != m_pViewList.end(); iter++)
 59         {
 60             if((*iter).get() == view)
 61             {
 62                 m_pViewList.erase(iter);
 63                 cout << "remove view" << endl;
 64                 return;
 65             }
 66         }
 67     }
 68 ​
 69     virtual void notify() override
 70     {
 71         auto iter = m_pViewList.begin();
 72         for(; iter != m_pViewList.end(); iter++)
 73         {
 74             (*iter).get()->update();
 75         }
 76     }
 77 ​
 78 private:
 79     list<shared_ptr<View>> m_pViewList; 
 80 };
 81 ​
 82 //具體的觀察者類    表視圖
 83 class TableView : public View
 84 {
 85 public:
 86     TableView() : m_name("unknow"){}
 87     TableView(const string& name) : m_name(name){}
 88     ~TableView(){ cout << "~TableView(): " << m_name.data() << endl; }
 89 ​
 90     void setViewName(const string& name)
 91     {
 92         m_name = name;
 93     }
 94 ​
 95     const string& name()
 96     {
 97         return m_name;
 98     }
 99 ​
100     void update() override
101     {
102         cout << m_name.data() << " update" << endl;
103     }
104 ​
105 private:
106     string m_name;
107 };
108 ​
109 int main()
110 {
111     /*
112     * 這裏需要補充說明的是在此示例代碼中,View一旦被註冊到DataModel類之後,DataModel解析時會自動解析掉     * 內部容器中存儲的View對象,因此註冊後的View對象不需要在手動去delete,再去delete View對象會出錯。
113     */
114     
115     View* v1 = new TableView("TableView1");
116     View* v2 = new TableView("TableView2");
117     View* v3 = new TableView("TableView3");
118     View* v4 = new TableView("TableView4");
119 ​
120     IntDataModel* model = new IntDataModel;
121     model->addView(v1);
122     model->addView(v2);
123     model->addView(v3);
124     model->addView(v4);
125 ​
126     model->notify();
127 ​
128     cout << "-------------\n" << endl;
129 ​
130     model->removeView(v1);
131 ​
132     model->notify();
133 ​
134     delete model;
135     model = nullptr;
136 ​
137     return 0;
138 }
139 ​

複製代碼

 

注:原文是2018年初寫的,2019年6月11日修改。如有錯誤歡迎指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章