設計模式
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、備忘錄模式
備忘錄模式:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可以將該對象恢復到原來保存的狀態。
備忘錄模式中需要定義的角色類:
-
Originator(發起人):負責創建一個備忘錄Memento,用以記錄當前時刻自身的內部狀態,並可使用備忘錄恢復內部狀態。Originator可以根據需要決定Memento存儲自己的哪些內部狀態。
-
Memento(備忘錄):負責存儲Originator對象的內部狀態,並可以防止Originator以外的其他對象訪問備忘錄。備忘錄有兩個接口:Caretaker只能看到備忘錄的窄接口,他只能將備忘錄傳遞給其他對象。Originator卻可看到備忘錄的寬接口,允許它訪問返回到先前狀態所需要的所有數據。
-
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日修改。如有錯誤歡迎指正。