1、工廠模式:簡單工廠模式、工廠方法模式、抽象工廠模式
1)、簡單工廠模式:主要特點是需要在工廠類中做判斷,從而創造相應的產品,當增加新產品時,需要修改工廠類。
typedef enum { T80 = 1, T99 }TankType; class Tank { public: virtual void message() = 0; }; class Tank80:public Tank { public: void message() { cout << "Tank80" << endl; } }; class Tank99:public Tank { public: void message() { cout << "Tank99" << endl; } }; class TankFactory { public: Tank* createTank(TankType type) { switch(type) { case 1: return new Tank80(); case 2: return new Tank99(); default: return NULL; } } };
2)、工廠方法模式:是指定義一個創建對象的接口,讓子類決定實例化哪一個類,Factory Method使一個類的實例化延遲到其子類。
主要解決:主要解決接口選擇的問題。
何時使用:我們明確地計劃不同條件下創建不同實例時。
如何解決:讓其子類實現工廠接口,返回的也是一個抽象的產品。
關鍵代碼:創建過程在其子類執行。
缺點:每增加一種產品,就需要增加一個對象工廠。相比簡單工廠模式,工廠方法模式需要定義更多的類。
class Tank { public: virtual void message() = 0; }; class Tank80:public Tank { public: void message() { cout << "Tank80" << endl; } }; class Tank99:public Tank { public: void message() { cout << "Tank99" << endl; } }; class TankFactory { public: virtual Tank* createTank() = 0; }; class Tank80Factory:public TankFactory { public: Tank* createTank() { return new Tank80(); } }; class Tank99Factory:public TankFactory { public: Tank* createTank() { return new Tank99(); } };
3)、抽象工廠模式:提供一個創建一系列相關或相互依賴的對象接口,而無需指定它們的具體類。
主要解決:主要解決接口選擇的問題。
何時使用:系統的產品有多於一個的產品族,而系統只消費其中某一族的產品。
如何解決:在一個產品族裏面,定義多個產品。
關鍵代碼:在一個工廠裏聚合多個同類產品。
缺點:產品族擴展非常困難,要增加一個系列的某一產品,既要在抽象的 Creator 里加代碼,又要在具體的裏面加代碼。
class Tank { public: virtual void message() = 0; }; class Tank80:public Tank { public: void message() { cout << "Tank80" << endl; } }; class Tank99:public Tank { public: void message() { cout << "Tank99" << endl; } }; class Plain { public: virtual void message() = 0; }; class Plain80: public Plain { public: void message() { cout << "Plain80" << endl; } }; class Plain99: public Plain { public: void message() { cout << "Plain99" << endl; } }; class Factory { public: virtual Tank* createTank() = 0; virtual Plain* createPlain() = 0; }; class Factory80:public Factory { public: Tank* createTank() { return new Tank80(); } Plain* createPlain() { return new Plain80(); } }; class Factory99:public Factory { public: Tank* createTank() { return new Tank99(); } Plain* createPlain() { return new Plain99(); } };
2、 策略模式:是指定義一系列的算法,把它們一個個封裝起來,並且使它們可以互相替換。使得算法可以獨立於使用它的客戶而變化,也就是說這些算法所完成的功能是一樣的,對外接口是一樣的,只是各自現實上存在差異。
主要解決:在有多種算法相似的情況下,使用 if...else 所帶來的複雜和難以維護。
何時使用:一個系統有許多許多類,而區分它們的只是他們直接的行爲。
如何解決:將這些算法封裝成一個一個的類,任意地替換。
關鍵代碼:實現同一個接口。
缺點: 1、策略類會增多。 2、所有策略類都需要對外暴露。
//傳統策略模式實現 class Hurt { public: virtual void redBuff() = 0; }; class AdcHurt:public Hurt { public: void redBuff() { cout << "Adc hurt" << endl; } }; class ApcHurt:public Hurt { public: void redBuff() { cout << "Apc hurt" << endl; } }; //方法1:傳入一個指針參數 class Soldier { public: Soldier(Hurt* hurt):m_hurt(hurt) { } ~Soldier() { } void beInjured() { m_hurt->redBuff(); } private: Hurt* m_hurt; }; //方法2:傳入一個參數標籤 typedef enum { adc, apc }HurtType; class Master { public: Master(HurtType type) { switch(type) { case adc: m_hurt = new AdcHurt; break; case apc: m_hurt = new ApcHurt; break; default: m_hurt = NULL; break; } } ~Master() { } void beInjured() { if(m_hurt != NULL) { m_hurt->redBuff(); } else { cout << "Not hurt" << endl; } } private: Hurt* m_hurt; }; //方法3:使用模板類 template <typename T> class Tank { public: void beInjured() { m_hurt.redBuff(); } private: T m_hurt; }; //END //使用函數指針實現策略模式 void adcHurt(int num) { cout << "adc hurt:" << num << endl; } void apcHurt(int num) { cout << "apc hurt:" << num << endl; } //普通函數指針 class Aid { public: typedef void (*HurtFun)(int); Aid(HurtFun fun):m_fun(fun) { } void beInjured(int num) { m_fun(num); } private: HurtFun m_fun; }; //使用std::function , 頭文件:#include<functional> class Bowman { public: typedef std::function<void(int)> HurtFunc; Bowman(HurtFunc fun):m_fun(fun) { } void beInjured(int num) { m_fun(num); } private: HurtFunc m_fun; }; //END
3、適配器模式:將一個類的接口轉換成客戶希望的另一個接口,使得原本由於接口不兼容而不能一起工作的哪些類可以一起工作。
主要解決:主要解決在軟件系統中,常常要將一些"現存的對象"放到新的環境中,而新環境要求的接口是現對象不能滿足的。
何時使用: 1、系統需要使用現有的類,而此類的接口不符合系統的需要。 2、想要建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作,這些源類不一定有一致的接口。 3、通過接口轉換,將一個類插入另一個類系中。(比如老虎和飛禽,現在多了一個飛虎,在不增加實體的需求下,增加一個適配器,在裏面包容一個虎對象,實現飛的接口。)
如何解決:繼承或依賴(推薦)。
關鍵代碼:適配器繼承或依賴已有的對象,實現想要的目標接口。
缺點:1、過多地使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是 A 接口,其實內部被適配成了 B 接口的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。
//使用複合,對象模式 class Deque //雙端隊列,被適配類 { public: void push_back(int x) { cout << "Deque push_back:" << x << endl; } void push_front(int x) { cout << "Deque push_front:" << x << endl; } void pop_back() { cout << "Deque pop_back" << endl; } void pop_front() { cout << "Deque pop_front" << endl; } }; class Sequence //順序類,目標類 { public: virtual void push(int x) = 0; virtual void pop() = 0; }; class Stack:public Sequence //棧, 適配類 { public: void push(int x) { m_deque.push_back(x); } void pop() { m_deque.pop_back(); } private: Deque m_deque; }; class Queue:public Sequence //隊列,適配類 { public: void push(int x) { m_deque.push_back(x); } void pop() { m_deque.pop_front(); } private: Deque m_deque; }; //END
//使用繼承,類模式 class Deque //雙端隊列,被適配類 { public: void push_back(int x) { cout << "Deque push_back:" << x << endl; } void push_front(int x) { cout << "Deque push_front:" << x << endl; } void pop_back() { cout << "Deque pop_back" << endl; } void pop_front() { cout << "Deque pop_front" << endl; } }; class Sequence //順序類,目標類 { public: virtual void push(int x) = 0; virtual void pop() = 0; }; class Stack:public Sequence, private Deque //棧, 適配類 { public: void push(int x) { push_back(x); } void pop() { pop_back(); } }; class Queue:public Sequence, private Deque //隊列,適配類 { public: void push(int x) { push_back(x); } void pop() { pop_front(); } }; //END
4、 單例模式:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
主要解決:一個全局使用的類頻繁地創建與銷燬。
何時使用:想控制實例數目,節省系統資源的時候。
如何解決:判斷系統是否已存在單例,如果有則返回,沒有則創建。
關鍵代碼:構造函數是私有的。
單例大約有兩種實現方法:懶漢與餓漢。
懶漢:故名思義,不到萬不得已就不會去實例化類,也就是說在第一次用到類實例的時候纔會去實例化,所以上邊的經典方法被歸爲懶漢實現;
餓漢:餓了肯定要飢不擇食。所以在單例類定義的時候就進行實例化。
特點與選擇:
由於要進行線程同步,所以在訪問量比較大,或者可能訪問的線程比較多時,採用餓漢實現,可以實現更好的性能。這是以空間換時間。
在訪問量較小時,採用懶漢實現。這是以時間換空間。
//懶漢式一般實現:非線程安全,getInstance返回的實例指針需要delete class Singleton { public: static Singleton* getInstance(); ~Singleton(){} private: static Singleton* m_pSingleton; Singleton(){} Singleton(const Singleton& obj) = delete; //明確拒絕 Singleton& operator=(const Singleton& obj) = delete; //明確拒絕 }; Singleton* Singleton::m_pSingleton = NULL; Singleton* Singleton::getInstance() { if(m_pSingleton == NULL) { m_pSingleton = new Singleton; } return m_pSingleton; } //END //懶漢式:加lock,線程安全 std::mutex mt; class Singleton { public: static Singleton* getInstance(); private: Singleton(){} Singleton(const Singleton&) = delete; //明確拒絕 Singleton& operator=(const Singleton&) = delete; //明確拒絕 static Singleton* m_pSingleton; }; Singleton* Singleton::m_pSingleton = NULL; Singleton* Singleton::getInstance() { if(m_pSingleton == NULL) { mt.lock(); m_pSingleton = new Singleton(); mt.unlock(); } return m_pSingleton; } //END //返回一個reference指向local static對象 //多線程可能存在不確定性:任何一種non-const static對象,不論它是local或non-local,在多線程環境下“等待某事發生”都會有麻煩。解決的方法:在程序的單線程啓動階段手工調用所有reference-returning函數。 class Singleton { public: static Singleton& getInstance(); private: Singleton(){} Singleton(const Singleton&) = delete; //明確拒絕 Singleton& operator=(const Singleton&) = delete; //明確拒絕 }; Singleton& Singleton::getInstance() { static Singleton singleton; return singleton; } //END //餓漢式:線程安全,注意delete class Singleton { public: static Singleton* getInstance(); private: Singleton(){} Singleton(const Singleton&) = delete; //明確拒絕 Singleton& operator=(const Singleton&) = delete; //明確拒絕 static Singleton* m_pSingleton; }; Singleton* Singleton::m_pSingleton = new Singleton(); Singleton* Singleton::getInstance() { return m_pSingleton; } //END
5、原型模式:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
主要解決:在運行期建立和刪除對象。
何時使用:1).當我們的對象類型不是開始就能確定的,而這個類型是在運行期確定的話,那麼我們通過這個類型的對象克隆出一個新的對象比較容易一些;2).有的時候,我們需要一個對象在某個狀態下的副本,此時,我們使用原型模式是最好的選擇;例如:一個對象,經過一段處理之後,其內部的狀態發生了變化;這個時候,我們需要一個這個狀態的副本,如果直接new一個新的對象的話,但是它的狀態是不對的,此時,可以使用原型模式,將原來的對象拷貝一個出來,這個對象就和之前的對象是完全一致的了;3).當我們處理一些比較簡單的對象時,並且對象之間的區別很小,可能就幾個屬性不同而已,那麼就可以使用原型模式來完成,省去了創建對象時的麻煩了;4).有的時候,創建對象時,構造函數的參數很多,而自己又不完全的知道每個參數的意義,就可以使用原型模式來創建一個新的對象,不必去理會創建的過程。
->適當的時候考慮一下原型模式,能減少對應的工作量,減少程序的複雜度,提高效率
如何解決:利用已有的一個原型對象,快速地生成和原型對象一樣的實例。
關鍵代碼:拷貝,return new className(*this);
class Clone { public: Clone() { } virtual ~Clone() { } virtual Clone* clone() = 0; virtual void show() = 0; }; class Sheep:public Clone { public: Sheep(int id, string name):Clone(),m_id(id),m_name(name) { cout << "Sheep() id add:" << &m_id << endl; cout << "Sheep() name add:" << &m_name << endl; } ~Sheep() { } Sheep(const Sheep& obj) { this->m_id = obj.m_id; this->m_name = obj.m_name; cout << "Sheep(const Sheep& obj) id add:" << &m_id << endl; cout << "Sheep(const Sheep& obj) name add:" << &m_name << endl; } Clone* clone() { return new Sheep(*this); } void show() { cout << "id :" << m_id << endl; cout << "name:" << m_name.data() << endl; } private: int m_id; string m_name; }; int main() { Clone* s1 = new Sheep(1, "abs"); s1->show(); Clone* s2 = s1->clone(); s2->show(); delete s1; delete s2; return 0; }
6、模板模式:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
主要解決:多個子類有相同的方法,並且邏輯相同,細節有差異。
如何解決:對重要,複雜的算法,將核心算法設計爲模板方法,周邊細節由子類實現,重構時,經常使用的方法,將相同的代碼抽象到父類,通過鉤子函數約束行爲。
關鍵代碼:在抽象類實現通用接口,細節變化在子類實現。
缺點:每一個不同的實現都需要一個子類來實現,導致類的個數增加,使得系統更加龐大。
class Computer { public: void product() { installCpu(); installRam(); installGraphicsCard(); } protected: virtual void installCpu() = 0; virtual void installRam() = 0; virtual void installGraphicsCard() = 0; }; class ComputerA:public Computer { protected: void installCpu() override { cout << "ComputerA install Inter Core i5" << endl; } void installRam() override { cout << "ComputerA install 2G Ram" << endl; } void installGraphicsCard() override { cout << "ComputerA install Gtx940 GraphicsCard" << endl; } }; class ComputerB:public Computer { protected: void installCpu() override { cout << "ComputerB install Inter Core i7" << endl; } void installRam() override { cout << "ComputerB install 4G Ram" << endl; } void installGraphicsCard() override { cout << "ComputerB install Gtx960 GraphicsCard" << endl; } };
7、 建造者模式:將複雜對象的構建和其表示分離,使得同樣的構建過程可以創建不同的表示。
主要解決:一個複雜對象的創建工作,由各個部分的子對象用一定的算法構成;由於需求變化,這個複雜對象的各個部分經常面臨變化,但將它們組合在一起的算法卻相對穩定。
如何解決:將變與不變分開
關鍵代碼:建造者:創建和提供實例,Director:管理建造出來的實例的依賴關係。。
缺點:1、產品必須有共同點,範圍有限制。 2、如內部變化複雜,會有很多的建造類。
typedef enum { type1, type2 }ProductType; class Product //產品 { public: void setNum(int num); void setColor(string color); void setType(ProductType type); void showProduct(); private: int m_num; string m_color; ProductType m_type; }; void Product::setNum(int num) { m_num = num; } void Product::setColor(string color) { m_color = color; } void Product::setType(ProductType type) { m_type = type; } void Product::showProduct() { cout << "Product: " << endl; cout << " num : " << m_num << endl; cout << " color: " << m_color.data() << endl; cout << " type : " << m_type << endl; } //建造者父類,定義接口 class Builder { public: Builder(){} virtual ~Builder(){} virtual void buildNum(int num) = 0; virtual void buildColor(string color) = 0; virtual void buildType(ProductType type) = 0; virtual void createProduct() = 0; virtual Product* getProduct() = 0; virtual void show() = 0; }; //建造者A class BuilderA:public Builder { public: BuilderA(){} ~BuilderA(){} void buildNum(int num) override; void buildColor(string color) override; void buildType(ProductType type) override; void createProduct() override; Product* getProduct() override; void show() override; private: Product* m_product; }; void BuilderA::buildNum(int num) { cout << "BuilderA build Num: " << num << endl; m_product->setNum(num); } void BuilderA::buildColor(string color) { cout << "BuilderA build color: " << color.data() << endl; m_product->setColor(color); } void BuilderA::buildType(ProductType type) { cout << "BuilderA build type: " << type << endl; m_product->setType(type); } void BuilderA::createProduct() { cout << "BuilderA CreateProduct: " << endl; m_product = new Product(); } Product* BuilderA::getProduct() { return m_product; } void BuilderA::show() { m_product->showProduct(); } //建造者B class BuilderB:public Builder { public: BuilderB(){} ~BuilderB(){} void buildNum(int num) override; void buildColor(string color) override; void buildType(ProductType type) override; void createProduct() override; Product* getProduct() override; void show() override; private: Product* m_product; }; void BuilderB::buildNum(int num) { cout << "BuilderB build Num: " << num << endl; m_product->setNum(num); } void BuilderB::buildColor(string color) { cout << "BuilderB build color: " << color.data() << endl; m_product->setColor(color); } void BuilderB::buildType(ProductType type) { cout << "BuilderB build type: " << type << endl; m_product->setType(type); } void BuilderB::createProduct() { cout << "BuilderB CreateProduct: " << endl; m_product = new Product(); } Product* BuilderB::getProduct() { return m_product; } void BuilderB::show() { m_product->showProduct(); } //管理類,負責安排構造的具體過程 class Director { public: Director(Builder* builder):m_builder(builder) { } void construct(int num, string color, ProductType type) { m_builder->createProduct(); m_builder->buildNum(num); m_builder->buildColor(color); m_builder->buildType(type); } private: Builder* m_builder; };
8、外觀模式:爲子系統中的一組接口定義一個一致的界面,外觀模式提供了一個高層接口,這個接口使得這一子系統更加容易被使用;對於複雜的系統,系統爲客戶提供一個簡單的接口,把複雜的實現過程封裝起來,客戶不需要了解系統內部的細節。
主要解決:客戶不需要了解系統內部複雜的細節,只需要一個接口;系統入口。
如何解決:客戶不直接與系統耦合,而是通過外觀類與系統耦合。
關鍵代碼:客戶與系統之間加一個外觀層,外觀層處理系統的調用關係、依賴關係等。
缺點:需要修改時不易繼承、不易修改。
class Cpu { public: void productCpu() { cout << "Product Cpu" << endl; } }; class Ram { public: void productRam() { cout << "Product Ram" << endl; } }; class Graphics { public: void productGraphics() { cout << "Product Graphics" << endl; } }; class Computer { public: void productComputer() { Cpu cpu; cpu.productCpu(); Ram ram; ram.productRam(); Graphics graphics; graphics.productGraphics(); } }; int main() { //客戶直接調用computer生產函數,無需關心具體部件的生產過程。也可直接單獨生產部件 Computer computer; computer.productComputer(); Cpu cpu; cpu.productCpu(); return 0; }
9、組合模式:將對象組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得用戶對單個對象和組合對象的使用具有一致性。
主要解決:它在我們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程序可以像處理簡單元素一樣處理複雜元素,從而使得客戶程序與複雜元素的內部結構解耦。
如何解決:樹枝和樹葉實現統一接口,樹枝內部組合該接口。
關鍵代碼:樹枝內部組合該接口,並且含有內部屬性list,裏面放Component。
class Company { public: Company(string name):m_name(name) {} virtual ~Company(){} virtual void add(Company* company) = 0; virtual void remove(string name) = 0; virtual void display(int depth) = 0; string getName() { return m_name; } protected: string m_name; }; //具體的公司 class ConcreteCompany:public Company //樹枝 { public: ConcreteCompany(string name):Company(name) {} ~ConcreteCompany() { cout << "~ConcreteCompany()" << endl; } void add(Company* company) override; void remove(string name) override; void display(int depth) override; private: list<shared_ptr<Company>> m_listCompany; }; void ConcreteCompany::add(Company* company) { shared_ptr<Company> temp(company); m_listCompany.push_back(temp); } void ConcreteCompany::remove(string name) { list<shared_ptr<Company>>::iterator iter = m_listCompany.begin(); for(; iter != m_listCompany.end(); iter++) { shared_ptr<Company> temp(*iter); string strName = temp.get()->getName(); if(name == strName) { m_listCompany.erase(iter); } } } void ConcreteCompany::display(int depth) { for(int i = 0; i < depth; i++) { cout << "-"; } cout << m_name.data() << endl; list<shared_ptr<Company>>::iterator iter = m_listCompany.begin(); for(; iter != m_listCompany.end(); iter++) { shared_ptr<Company> temp(*iter); temp.get()->display(depth + 2); } } //公司下的部門 class FinanceDept:public Company //樹葉 { public: FinanceDept(string name):Company(name) {} ~FinanceDept() { cout << "~FinanceDept()" << endl; } void add(Company* company) override; void remove(string name) override; void display(int depth) override; }; void FinanceDept::add(Company* company) { cout << "FinanceDept add failed" << endl; } void FinanceDept::remove(string name) { cout << "FinanceDept remove failed" << endl; } void FinanceDept::display(int depth) { for(int i = 0; i < depth; i++) { cout << "-"; } cout << m_name.data() << endl; } //公司下的部門 class HRDept:public Company //樹葉 { public: HRDept(string name):Company(name) {} ~HRDept() { cout << "~HRDept()" << endl; } void add(Company* company) override; void remove(string name) override; void display(int depth) override; }; void HRDept::add(Company* company) { cout << "HRDept add failed" << endl; } void HRDept::remove(string name) { cout << "HRDept remove failed" << endl; } void HRDept::display(int depth) { for(int i = 0; i < depth; i++) { cout << "-"; } cout << m_name.data() << endl; } int main(int argc, char *argv[]) { Company* root = new ConcreteCompany("zong"); Company* f1 = new FinanceDept("F1"); Company* h1 = new HRDept("H1"); root->add(f1); root->add(h1); Company* c1 = new ConcreteCompany("fen1"); Company* f2 = new FinanceDept("F2"); Company* h2 = new HRDept("H2"); c1->add(f2); c1->add(h2); root->add(c1); root->display(0); delete root; return 0; }
10、 代理模式:爲其它對象提供一種代理以控制對這個對象的訪問。
主要解決:在直接訪問對象時帶來的問題,比如:要訪問的對象在遠程服務器上。在面向對象系統中,有些對象由於某些原因,直接訪問會給使用者或系統帶來很多麻煩,可以在訪問此對象時加上一個對此對象的訪問層。
如何解決:增加中間代理層。
關鍵代碼:實現與被代理類組合。
class Gril { public: Gril(string name = "gril"):m_string(name){} string getName() { return m_string; } private: string m_string; }; class Profession { public: virtual ~Profession(){} virtual void profess() = 0; }; class YoungMan:public Profession { public: YoungMan(Gril gril):m_gril(gril){} void profess() { cout << "Young man love " << m_gril.getName().data() << endl; } private: Gril m_gril; }; class ManProxy:public Profession { public: ManProxy(Gril gril):m_man(new YoungMan(gril)){} void profess() { cout << "I am Proxy" << endl; m_man->profess(); } private: YoungMan* m_man; }; int main(int argc, char *argv[]) { Gril gril("hei"); Profession* proxy = new ManProxy(gril); proxy->profess(); delete proxy; return 0; }
11、享元模式:運用共享技術有效地支持大量細粒度的對象。
主要解決:在有大量對象時,把其中共同的部分抽象出來,如果有相同的業務請求,直接返回內存中已有的對象,避免重新創建。
如何解決:用唯一標識碼判斷,如果內存中有,則返回這個唯一標識碼所標識的對象。
關鍵代碼:將內部狀態作爲標識,進行共享。
//以Money的類別作爲內部標識,面值作爲外部狀態。 enum MoneyCategory //類別,內在標識,作爲標識碼 { Coin, bankNote }; enum FaceValue //面值,外部標識,需要存儲的對象 { ValueOne = 1, ValueTwo }; class Money //抽象父類 { public: Money(MoneyCategory cate):m_mCate(cate){} virtual ~Money(){ cout << "~Money() " << endl; } virtual void save() = 0; private: MoneyCategory m_mCate; }; class MoneyCoin:public Money //具體子類1 { public: MoneyCoin(MoneyCategory cate):Money(cate){} ~MoneyCoin(){ cout << "~MoneyCoin()" << endl; } void save() { cout << "Save Coin" << endl; } }; class MoneyNote:public Money //具體子類2 { public: MoneyNote(MoneyCategory cate):Money(cate){} ~MoneyNote(){ cout << "~MoneyNote()" << endl; } void save() { cout << "Save BankNote" << endl; } }; class Bank { public: Bank():m_coin(nullptr),m_note(nullptr),m_count(0){} ~Bank() { if(m_coin != nullptr) { delete m_coin; m_coin = nullptr; } if(m_note != nullptr) { delete m_note; m_note = nullptr; } } void saveMoney(MoneyCategory cate, FaceValue value) { switch(cate) //以類別作爲標識碼 { case Coin: { if(m_coin == nullptr) //內存中存在標識碼所標識的對象,則直接調用,不再創建 { m_coin = new MoneyCoin(Coin); } m_coin->save(); m_vector.push_back(value); break; } case bankNote: { if(m_note == nullptr) { m_note = new MoneyNote(bankNote); } m_note->save(); m_vector.push_back(value); break; } default: break; } } int sumSave() { auto iter = m_vector.begin(); for(; iter != m_vector.end(); iter++) { m_count += *iter; } return m_count; } private: vector<FaceValue> m_vector; Money* m_coin; Money* m_note; int m_count; }; int main() { Bank b1; b1.saveMoney(Coin, ValueOne); b1.saveMoney(Coin, ValueTwo); b1.saveMoney(Coin, ValueTwo); b1.saveMoney(bankNote, ValueOne); b1.saveMoney(bankNote, ValueTwo); cout << b1.sumSave() << endl; return 0; }
12、 橋接模式:將抽象部分與實現部分分離,使它們都可以獨立變換。
主要解決:在有很多中可能會變化的情況下,用繼承會造成類爆炸問題,不易擴展。
如何解決:把不同的分類分離出來,使它們獨立變化,減少它們之間的耦合。
關鍵代碼:將現實獨立出來,抽象類依賴現實類。
//將各種App、各種手機全部獨立分開,使其自由組合橋接 class App { public: virtual ~App(){ cout << "~App()" << endl; } virtual void run() = 0; }; class GameApp:public App { public: void run() { cout << "GameApp Running" << endl; } }; class TranslateApp:public App { public: void run() { cout << "TranslateApp Running" << endl; } }; class MobilePhone { public: virtual ~MobilePhone(){ cout << "~MobilePhone()" << endl;} virtual void appRun(App* app) = 0; //實現App與手機的橋接 }; class XiaoMi:public MobilePhone { public: void appRun(App* app) { cout << "XiaoMi: "; app->run(); } }; class HuaWei:public MobilePhone { public: void appRun(App* app) { cout << "HuaWei: "; app->run(); } }; int main() { App* gameApp = new GameApp; App* translateApp = new TranslateApp; MobilePhone* mi = new XiaoMi; MobilePhone* hua = new HuaWei; mi->appRun(gameApp); mi->appRun(translateApp); hua->appRun(gameApp); hua->appRun(translateApp); delete hua; delete mi; delete gameApp; delete translateApp; return 0; }
13、 裝飾模式:動態地給一個對象添加一些額外的功能,就新增加功能來說,裝飾器模式比生產子類更加靈活。
主要解決:通常我們爲了擴展一個類經常使用繼承的方式,由於繼承爲類引入靜態特徵,並且隨着擴展功能的增多,子類會很膨脹。
如何解決:將具體的功能劃分,同時繼承裝飾者類。
關鍵代碼:裝飾類複合和繼承組件類,具體的擴展類重寫父類的方法。
class Dumplings //抽象類 餃子 { public: virtual ~Dumplings(){} virtual void showDressing() = 0; }; class MeatDumplings:public Dumplings //現實類 肉餡餃子 { public: ~MeatDumplings(){ cout << "~MeatDumplings()" << endl; } void showDressing() { cout << "Add Meat" << endl; } }; class DecoratorDumpling:public Dumplings //裝飾類 { public: DecoratorDumpling(Dumplings* d):m_dumpling(d){} virtual ~DecoratorDumpling(){ cout << "~DecoratorDumpling()" << endl; } void showDressing() { m_dumpling->showDressing(); } private: Dumplings* m_dumpling; }; class SaltDecorator:public DecoratorDumpling // 裝飾類 加鹽 { public: SaltDecorator(Dumplings* d):DecoratorDumpling(d){} ~SaltDecorator(){ cout << "~SaltDecorator()" << endl; } void showDressing() { DecoratorDumpling::showDressing(); //注意點 addDressing(); } private: void addDressing() { cout << "Add Salt" << endl; } }; class OilDecorator:public DecoratorDumpling //裝飾類 加油 { public: OilDecorator(Dumplings* d):DecoratorDumpling(d){} ~OilDecorator(){ cout << "~OilDecorator()" << endl; } void showDressing() { DecoratorDumpling::showDressing(); //注意點 addDressing(); } private: void addDressing() { cout << "Add Oil" << endl; } }; class CabbageDecorator:public DecoratorDumpling //裝飾類 加蔬菜 { public: CabbageDecorator(Dumplings* d):DecoratorDumpling(d){} ~CabbageDecorator(){ cout << "~CabbageDecorator()" << endl; } void showDressing() { DecoratorDumpling::showDressing(); //注意點 addDressing(); } private: void addDressing() { cout << "Add Cabbage" << endl; } }; int main() { Dumplings* d = new MeatDumplings; //原始的肉餃子 Dumplings* d1 = new SaltDecorator(d); //加鹽後的餃子 Dumplings* d2 = new OilDecorator(d1); //加油後的餃子 Dumplings* d3 = new CabbageDecorator(d2); //加蔬菜後的餃子 d3->showDressing(); delete d; delete d1; delete d2; delete d3; return 0; }
14、備忘錄模式:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可以將該對象恢復到原來保存的狀態。
如何解決:通過一個備忘錄類專門存儲對象狀態。
關鍵代碼:備忘錄類、客戶類、備忘錄管理類;客戶類不與備忘錄類耦合,而是與備忘錄管理類耦合。
typedef struct //需要保存的信息 { int grade; string arm; string corps; }GameValue; class Memento //備忘錄類 { public: Memento(){} Memento(GameValue value):m_gameValue(value){} GameValue getValue() { return m_gameValue; } private: GameValue m_gameValue; }; class Game //客戶類 遊戲 { public: Game(GameValue value):m_gameValue(value) {} void addGrade() //等級增加 { m_gameValue.grade++; } void replaceArm(string arm) //更換武器 { m_gameValue.arm = arm; } void replaceCorps(string corps) //更換工會 { m_gameValue.corps = corps; } Memento saveValue() //保存當前信息 { Memento memento(m_gameValue); return memento; } void load(Memento memento) //載入信息 { m_gameValue = memento.getValue(); } void showValue() { cout << "Grade: " << m_gameValue.grade << endl; cout << "Arm : " << m_gameValue.arm.data() << endl; cout << "Corps: " << m_gameValue.corps.data() << endl; } private: GameValue m_gameValue; }; class Caretake //備忘錄管理類 { public: void save(Memento memento) //保存信息 { m_memento = memento; } Memento load() //讀已保存的信息 { return m_memento; } private: Memento m_memento; }; int main() { GameValue v1 = {0, "Ak", "3K"}; Game game(v1); //初始值 game.addGrade(); game.showValue(); cout << "----------" << endl; Caretake care; care.save(game.saveValue()); //保存當前值 game.addGrade(); //修改當前值 game.replaceArm("M16"); game.replaceCorps("123"); game.showValue(); cout << "----------" << endl; game.load(care.load()); //恢復初始值 game.showValue(); return 0; }
15、中介者模式:用一箇中介對象來封裝一系列的對象交互,中介者使各對象不需要顯示地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之前的交互。
主要解決:對象與對象之前存在大量的關聯關係,這樣勢必會造成系統變得複雜,若一個對象改變,我們常常需要跟蹤與之關聯的對象,並做出相應的處理。
如何解決:將網狀結構分離爲星型結構。
關鍵代碼:將相關對象的通信封裝到一個類中單獨處理。
class Mediator; class Person //抽象同事類 { public: virtual ~Person(){} virtual void setMediator(Mediator* mediator) { m_mediator = mediator; } virtual void sendMessage(const string& message) = 0; virtual void getMessage(const string& message) = 0; protected: Mediator* m_mediator; }; class Mediator //抽象中介類 { public: virtual ~Mediator(){} virtual void setBuyer(Person* buyer) = 0; virtual void setSeller(Person* seller) = 0; virtual void send(const string& message, Person* person) = 0; }; class Buyer:public Person //買家類 { public: void sendMessage(const string& message) { m_mediator->send(message, this); } void getMessage(const string& message) { cout << "Buyer Get: " << message.data() << endl; } }; class Seller:public Person //賣家類 { public: void sendMessage(const string& message) { m_mediator->send(message, this); } void getMessage(const string& message) { cout << "Seller Get: " << message.data() << endl; } }; class HouseMediator:public Mediator //具體的中介類 { public: HouseMediator():m_buyer(nullptr),m_seller(nullptr){} void setBuyer(Person* buyer) { m_buyer = buyer; } void setSeller(Person *seller) { m_seller = seller; } void send(const string& message, Person* person) { if(person == m_buyer) { m_seller->getMessage(message); } if(person == m_seller) { m_buyer->getMessage(message); } } private: Person* m_buyer; Person* m_seller; }; int main() { Person* buyer = new Buyer; Person* seller = new Seller; Mediator* houseMediator = new HouseMediator; buyer->setMediator(houseMediator); seller->setMediator(houseMediator); houseMediator->setBuyer(buyer); houseMediator->setSeller(seller); buyer->sendMessage("1.5?"); seller->sendMessage("2!!!"); return 0; }
16、職責鏈模式:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之前的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞請求,直到有一個對象處理它爲止。
主要解決:職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無需關心請求的處理細節和請求的傳遞,所有職責鏈將請求的發送者和請求的處理者解耦了。
如何解決:職責鏈鏈釦類都現實統一的接口。
關鍵代碼:Handler內指明其上級,handleRequest()裏判斷是否合適,不合適則傳遞給上級。
enum RequestLevel { One = 1, Two, Three }; class Leader { public: Leader(Leader* leader):m_leader(leader){} virtual ~Leader(){} virtual void handleRequest(RequestLevel level) = 0; protected: Leader* m_leader; }; class Monitor:public Leader //鏈釦1 { public: Monitor(Leader* leader):Leader(leader){} void handleRequest(RequestLevel level) { if(level < Two) { cout << "Mointor handle request : " << level << endl; } else { m_leader->handleRequest(level); } } }; class Captain:public Leader //鏈釦2 { public: Captain(Leader* leader):Leader(leader){} void handleRequest(RequestLevel level) { if(level < Three) { cout << "Captain handle request : " << level << endl; } else { m_leader->handleRequest(level); } } }; class General:public Leader //鏈釦3 { public: General(Leader* leader):Leader(leader){} void handleRequest(RequestLevel level) { cout << "General handle request : " << level << endl; } }; int main() { Leader* general = new General(nullptr); Leader* captain = new Captain(general); Leader* monitor = new Monitor(captain); monitor->handleRequest(Three); return 0; }
17、觀察者模式:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都要得到通知並自動更新。
主要解決:一個對象更新,其它對象也要更新。
如何解決:目標類通知函數通知所有觀察者自動更新。
關鍵代碼:在目標類中增加一個ArrayList來存放觀察者們。
//數據模型爲目標類,視圖爲觀察者類。當數據模型發生改變時,通知視圖類更新 class View; class DataModel //目標抽象類 數據模型 { public: virtual ~DataModel(){} virtual void add(View* view) = 0; virtual void remove(View* view) = 0; virtual void notify() = 0; //通知函數 }; class View //觀察者抽象類 視圖 { public: virtual ~View(){ cout << "~View()" << endl; } virtual void update() = 0; }; class IntModel:public DataModel //具體的目標類, 整數模型 { public: ~IntModel() { clear(); } void add(View* view) { auto iter = std::find(m_list.begin(), m_list.end(), view); //判斷是否重複添加 if(iter == m_list.end()) { m_list.push_back(view); } } void remove(View* view) { auto iter = m_list.begin(); for(;iter != m_list.end(); iter++) { if(*iter == view) { delete *iter; //釋放內存 m_list.erase(iter); //刪除元素 break; } } } void notify() //通知觀察者更新 { auto iter = m_list.begin(); for(; iter != m_list.end(); iter++) { (*iter)->update(); } } private: void clear() { if(!m_list.empty()) { auto iter = m_list.begin(); for(;iter != m_list.end(); iter++) //釋放內存 { delete *iter; } } } private: list<View*> m_list; }; class TreeView:public View //具體的觀察者類 視圖 { public: TreeView(string name):m_name(name),View(){} ~TreeView(){ cout << "~TreeView()" << endl; } void update() { cout << m_name.data() << " : Update" << endl; } private: string m_name; }; int main() { View* v1 = new TreeView("view1"); View* v2 = new TreeView("view2"); View* v3 = new TreeView("view3"); View* v4 = new TreeView("view4"); DataModel* model = new IntModel; model->add(v1); model->add(v2); model->add(v3); model->add(v2); model->add(v4); model->notify(); cout << "----------" << endl; model->remove(v2); model->notify(); delete model; return 0; }