網(wǎng)站建設(shè)服務(wù)好公司排名google瀏覽器官網(wǎng)下載
軟件領(lǐng)域中的設(shè)計(jì)模式為開(kāi)發(fā)人員提供了一種使用專家設(shè)計(jì)經(jīng)驗(yàn)的有效途徑。設(shè)計(jì)模式中運(yùn)用了面向?qū)ο缶幊陶Z(yǔ)言的重要特性:封裝、繼承、多態(tài),真正領(lǐng)悟設(shè)計(jì)模式的精髓是可能一個(gè)漫長(zhǎng)的過(guò)程,需要大量實(shí)踐經(jīng)驗(yàn)的積累。最近看設(shè)計(jì)模式的書(shū),對(duì)于每個(gè)模式,用C++寫(xiě)了個(gè)小例子,加深一下理解。
一、設(shè)計(jì)模式的分類
總體來(lái)說(shuō)設(shè)計(jì)模式分為三大類
創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
結(jié)構(gòu)型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責(zé)任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問(wèn)者模式、中介者模式、解釋器模式。
其實(shí)還有兩類:并發(fā)型模式和線程池模式。
二、設(shè)計(jì)模式的六大原則
總原則:開(kāi)閉原則(Open Close Principle)
開(kāi)閉原則就是說(shuō)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。在程序需要進(jìn)行拓展的時(shí)候,不能去修改原有的代碼,而是要擴(kuò)展原有代碼,實(shí)現(xiàn)一個(gè)熱插拔的效果。所以一句話概括就是:為了使程序的擴(kuò)展性好,易于維護(hù)和升級(jí)。想要達(dá)到這樣的效果,我們需要使用接口和抽象類等,后面的具體設(shè)計(jì)中我們會(huì)提到這點(diǎn)。
1、單一職責(zé)原則
不要存在多于一個(gè)導(dǎo)致類變更的原因,也就是說(shuō)每個(gè)類應(yīng)該實(shí)現(xiàn)單一的職責(zé),如若不然,就應(yīng)該把類拆分。
2、里氏替換原則(Liskov Substitution Principle)
里氏代換原則(Liskov Substitution Principle LSP)面向?qū)ο笤O(shè)計(jì)的基本原則之一。 里氏代換原則中說(shuō),任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。 LSP是繼承復(fù)用的基石,只有當(dāng)衍生類可以替換掉基類,軟件單位的功能不受到影響時(shí),基類才能真正被復(fù)用,而衍生類也能夠在基類的基礎(chǔ)上增加新的行為。里氏代換原則是對(duì)“開(kāi)-閉”原則的補(bǔ)充。實(shí)現(xiàn)“開(kāi)-閉”原則的關(guān)鍵步驟就是抽象化。而基類與子類的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn),所以里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。
歷史替換原則中,子類對(duì)父類的方法盡量不要重寫(xiě)和重載。因?yàn)楦割惔砹硕x好的結(jié)構(gòu),通過(guò)這個(gè)規(guī)范的接口與外界交互,子類不應(yīng)該隨便破壞它。
3、依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)
這個(gè)是開(kāi)閉原則的基礎(chǔ),具體內(nèi)容:面向接口編程,依賴于抽象而不依賴于具體。寫(xiě)代碼時(shí)用到具體類時(shí),不與具體類交互,而與具體類的上層接口交互。
4、接口隔離原則(Interface Segregation Principle)
這個(gè)原則的意思是:每個(gè)接口中不存在子類用不到卻必須實(shí)現(xiàn)的方法,如果不然,就要將接口拆分。使用多個(gè)隔離的接口,比使用單個(gè)接口(多個(gè)接口方法集合到一個(gè)的接口)要好。
5、迪米特法則(最少知道原則)(Demeter Principle)
就是說(shuō):一個(gè)類對(duì)自己依賴的類知道的越少越好。也就是說(shuō)無(wú)論被依賴的類多么復(fù)雜,都應(yīng)該將邏輯封裝在方法的內(nèi)部,通過(guò)public方法提供給外部。這樣當(dāng)被依賴的類變化時(shí),才能最小的影響該類。
最少知道原則的另一個(gè)表達(dá)方式是:只與直接的朋友通信。類之間只要有耦合關(guān)系,就叫朋友關(guān)系。耦合分為依賴、關(guān)聯(lián)、聚合、組合等。我們稱出現(xiàn)為成員變量、方法參數(shù)、方法返回值中的類為直接朋友。局部變量、臨時(shí)變量則不是直接的朋友。我們要求陌生的類不要作為局部變量出現(xiàn)在類中。
6、合成復(fù)用原則(Composite Reuse Principle)
原則是盡量首先使用合成/聚合的方式,而不是使用繼承。
1.工廠模式
工廠模式屬于創(chuàng)建型模式,大致可以分為三類,簡(jiǎn)單工廠模式、工廠方法模式、抽象工廠模式。聽(tīng)上去差不多,都是工廠模式。下面一個(gè)個(gè)介紹,首先介紹簡(jiǎn)單工廠模式,它的主要特點(diǎn)是需要在工廠類中做判斷,從而創(chuàng)造相應(yīng)的產(chǎn)品。當(dāng)增加新的產(chǎn)品時(shí),就需要修改工廠類。有點(diǎn)抽象,舉個(gè)例子就明白了。有一家生產(chǎn)處理器核的廠家,它只有一個(gè)工廠,能夠生產(chǎn)兩種型號(hào)的處理器核??蛻粜枰裁礃拥奶幚砥骱?#xff0c;一定要顯示地告訴生產(chǎn)工廠。下面給出一種實(shí)現(xiàn)方案。
enum CTYPE {COREA, COREB};
class SingleCore
{
public: virtual void Show() = 0;
};
//單核A
class SingleCoreA: public SingleCore
{
public: void Show() { cout<<"SingleCore A"<<endl; }
};
//單核B
class SingleCoreB: public SingleCore
{
public: void Show() { cout<<"SingleCore B"<<endl; }
};
//唯一的工廠,可以生產(chǎn)兩種型號(hào)的處理器核,在內(nèi)部判斷
class Factory
{
public: SingleCore* CreateSingleCore(enum CTYPE ctype) { if(ctype == COREA) //工廠內(nèi)部判斷 return new SingleCoreA(); //生產(chǎn)核A else if(ctype == COREB) return new SingleCoreB(); //生產(chǎn)核B else return NULL; }
};
這樣設(shè)計(jì)的主要缺點(diǎn)之前也提到過(guò),就是要增加新的核類型時(shí),就需要修改工廠類。這就違反了開(kāi)放封閉原則:軟件實(shí)體(類、模塊、函數(shù))可以擴(kuò)展,但是不可修改。于是,工廠方法模式出現(xiàn)了。所謂工廠方法模式,是指定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類。Factory Method使一個(gè)類的實(shí)例化延遲到其子類。
相關(guān)視頻推薦
C++后端開(kāi)發(fā)常用設(shè)計(jì)模式,策略模式、觀察者模式、責(zé)任鏈模式的共同點(diǎn)和區(qū)別
C++設(shè)計(jì)模式:模板模式、責(zé)任鏈模式、組合模式
2023年最新技術(shù)圖譜,c++后端的8個(gè)技術(shù)維度,助力你快速成為大牛
免費(fèi)學(xué)習(xí)地址:c/c++ linux服務(wù)器開(kāi)發(fā)/后臺(tái)架構(gòu)師
需要C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加qun812855908獲取(資料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費(fèi)分享
?聽(tīng)起來(lái)很抽象,還是以剛才的例子解釋。這家生產(chǎn)處理器核的產(chǎn)家賺了不少錢,于是決定再開(kāi)設(shè)一個(gè)工廠專門用來(lái)生產(chǎn)B型號(hào)的單核,而原來(lái)的工廠專門用來(lái)生產(chǎn)A型號(hào)的單核。這時(shí),客戶要做的是找好工廠,比如要A型號(hào)的核,就找A工廠要;否則找B工廠要,不再需要告訴工廠具體要什么型號(hào)的處理器核了。下面給出一個(gè)實(shí)現(xiàn)方案。
class SingleCore
{
public: virtual void Show() = 0;
};
//單核A
class SingleCoreA: public SingleCore
{
public: void Show() { cout<<"SingleCore A"<<endl; }
};
//單核B
class SingleCoreB: public SingleCore
{
public: void Show() { cout<<"SingleCore B"<<endl; }
};
class Factory
{
public: virtual SingleCore* CreateSingleCore() = 0;
};
//生產(chǎn)A核的工廠
class FactoryA: public Factory
{
public: SingleCoreA* CreateSingleCore() { return new SingleCoreA; }
};
//生產(chǎn)B核的工廠
class FactoryB: public Factory
{
public: SingleCoreB* CreateSingleCore() { return new SingleCoreB; }
};
工廠方法模式也有缺點(diǎn),每增加一種產(chǎn)品,就需要增加一個(gè)對(duì)象的工廠。如果這家公司發(fā)展迅速,推出了很多新的處理器核,那么就要開(kāi)設(shè)相應(yīng)的新工廠。在C++實(shí)現(xiàn)中,就是要定義一個(gè)個(gè)的工廠類。顯然,相比簡(jiǎn)單工廠模式,工廠方法模式需要更多的類定義。
既然有了簡(jiǎn)單工廠模式和工廠方法模式,為什么還要有抽象工廠模式呢?它到底有什么作用呢?還是舉這個(gè)例子,這家公司的技術(shù)不斷進(jìn)步,不僅可以生產(chǎn)單核處理器,也能生產(chǎn)多核處理器?,F(xiàn)在簡(jiǎn)單工廠模式和工廠方法模式都鞭長(zhǎng)莫及。抽象工廠模式登場(chǎng)了。它的定義為提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,而無(wú)需指定它們具體的類。具體這樣應(yīng)用,這家公司還是開(kāi)設(shè)兩個(gè)工廠,一個(gè)專門用來(lái)生產(chǎn)A型號(hào)的單核多核處理器,而另一個(gè)工廠專門用來(lái)生產(chǎn)B型號(hào)的單核多核處理器,下面給出實(shí)現(xiàn)的代碼。
//單核
class SingleCore
{
public: virtual void Show() = 0;
};
class SingleCoreA: public SingleCore
{
public: void Show() { cout<<"Single Core A"<<endl; }
};
class SingleCoreB :public SingleCore
{
public: void Show() { cout<<"Single Core B"<<endl; }
};
//多核
class MultiCore
{
public: virtual void Show() = 0;
};
class MultiCoreA : public MultiCore
{
public: void Show() { cout<<"Multi Core A"<<endl; } };
class MultiCoreB : public MultiCore
{
public: void Show() { cout<<"Multi Core B"<<endl; }
};
//工廠
class CoreFactory
{
public: virtual SingleCore* CreateSingleCore() = 0; virtual MultiCore* CreateMultiCore() = 0;
};
//工廠A,專門用來(lái)生產(chǎn)A型號(hào)的處理器
class FactoryA :public CoreFactory
{
public: SingleCore* CreateSingleCore() { return new SingleCoreA(); } MultiCore* CreateMultiCore() { return new MultiCoreA(); }
};
//工廠B,專門用來(lái)生產(chǎn)B型號(hào)的處理器
class FactoryB : public CoreFactory
{
public: SingleCore* CreateSingleCore() { return new SingleCoreB(); } MultiCore* CreateMultiCore() { return new MultiCoreB(); }
};
至此,工廠模式介紹完了。給出三種工廠模式的UML圖,加深印象。
簡(jiǎn)單工廠模式的UML圖:
?工廠方法的UML圖:
?抽象工廠模式的UML圖:
2.策略模式
策略模式是指定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái),并且使它們可相互替換。本模式使得算法可獨(dú)立于使用它的客戶而變化。也就是說(shuō)這些算法所完成的功能一樣,對(duì)外的接口一樣,只是各自實(shí)現(xiàn)上存在差異。用策略模式來(lái)封裝算法,效果比較好。下面以高速緩存(Cache)的替換算法為例,實(shí)現(xiàn)策略模式。
什么是Cache的替換算法呢?簡(jiǎn)單解釋一下, 當(dāng)發(fā)生Cache缺失時(shí),Cache控制器必須選擇Cache中的一行,并用欲獲得的數(shù)據(jù)來(lái)替換它。所采用的選擇策略就是Cache的替換算法。下面給出相應(yīng)的UML圖。
?ReplaceAlgorithm是一個(gè)抽象類,定義了算法的接口,有三個(gè)類繼承自這個(gè)抽象類,也就是具體的算法實(shí)現(xiàn)。Cache類中需要使用替換算法,因此維護(hù)了一個(gè) ReplaceAlgorithm的對(duì)象。這個(gè)UML圖的結(jié)構(gòu)就是策略模式的典型結(jié)構(gòu)。下面根據(jù)UML圖,給出相應(yīng)的實(shí)現(xiàn)。
首先給出替換算法的定義。
//抽象接口
class ReplaceAlgorithm
{
public: virtual void Replace() = 0;
};
//三種具體的替換算法
class LRU_ReplaceAlgorithm : public ReplaceAlgorithm
{
public: void Replace() { cout<<"Least Recently Used replace algorithm"<<endl; }
}; class FIFO_ReplaceAlgorithm : public ReplaceAlgorithm
{
public: void Replace() { cout<<"First in First out replace algorithm"<<endl; }
};
class Random_ReplaceAlgorithm: public ReplaceAlgorithm
{
public: void Replace() { cout<<"Random replace algorithm"<<endl; }
};
接著給出Cache的定義,這里很關(guān)鍵,Cache的實(shí)現(xiàn)方式直接影響了客戶的使用方式,其關(guān)鍵在于如何指定替換算法。
方式一:直接通過(guò)參數(shù)指定,傳入一個(gè)特定算法的指針。
//Cache需要用到替換算法
class Cache
{
private: ReplaceAlgorithm *m_ra;
public: Cache(ReplaceAlgorithm *ra) { m_ra = ra; } ~Cache() { delete m_ra; } void Replace() { m_ra->Replace(); }
};
如果用這種方式,客戶就需要知道這些算法的具體定義。只能以下面這種方式使用,可以看到暴露了太多的細(xì)節(jié)。
int main()
{ Cache cache(new LRU_ReplaceAlgorithm()); //暴露了算法的定義 cache.Replace(); return 0;
}
方式二:也是直接通過(guò)參數(shù)指定,只不過(guò)不是傳入指針,而是一個(gè)標(biāo)簽。這樣客戶只要知道算法的相應(yīng)標(biāo)簽即可,而不需要知道算法的具體定義。
//Cache需要用到替換算法
enum RA {LRU, FIFO, RANDOM}; //標(biāo)簽
class Cache
{
private: ReplaceAlgorithm *m_ra;
public: Cache(enum RA ra) { if(ra == LRU) m_ra = new LRU_ReplaceAlgorithm(); else if(ra == FIFO) m_ra = new FIFO_ReplaceAlgorithm(); else if(ra == RANDOM) m_ra = new Random_ReplaceAlgorithm(); else m_ra = NULL; } ~Cache() { delete m_ra; } void Replace() { m_ra->Replace(); }
};
相比方式一,這種方式用起來(lái)方便多了。其實(shí)這種方式將簡(jiǎn)單工廠模式與策略模式結(jié)合在一起,算法的定義使用了策略模式,而Cache的定義其實(shí)使用了簡(jiǎn)單工廠模式。
int main()
{ Cache cache(LRU); //指定標(biāo)簽即可 cache.Replace(); return 0;
}
上面兩種方式,構(gòu)造函數(shù)都需要形參。構(gòu)造函數(shù)是否可以不用參數(shù)呢?下面給出第三種實(shí)現(xiàn)方式。
方式三:利用模板實(shí)現(xiàn)。算法通過(guò)模板的實(shí)參指定。當(dāng)然了,還是使用了參數(shù),只不過(guò)不是構(gòu)造函數(shù)的參數(shù)。在策略模式中,參數(shù)的傳遞難以避免,客戶必須指定某種算法。
//Cache需要用到替換算法
template <class RA>
class Cache
{
private: RA m_ra;
public: Cache() { } ~Cache() { } void Replace() { m_ra.Replace(); }
};
使用方式如下:
int main()
{ Cache<Random_ReplaceAlgorithm> cache; //模板實(shí)參 cache.Replace(); return 0;
}
3.適配器模式
DP上的定義:適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口,使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。它包括類適配器和對(duì)象適配器,本文針對(duì)的是對(duì)象適配器。舉個(gè)例子,在STL中就用到了適配器模式。STL實(shí)現(xiàn)了一種數(shù)據(jù)結(jié)構(gòu),稱為雙端隊(duì)列(deque),支持前后兩段的插入與刪除。STL實(shí)現(xiàn)棧和隊(duì)列時(shí),沒(méi)有從頭開(kāi)始定義它們,而是直接使用雙端隊(duì)列實(shí)現(xiàn)的。這里雙端隊(duì)列就扮演了適配器的角色。隊(duì)列用到了它的后端插入,前端刪除。而棧用到了它的后端插入,后端刪除。假設(shè)棧和隊(duì)列都是一種順序容器,有兩種操作:壓入和彈出。下面給出相應(yīng)的UML圖,與DP上的圖差不多。
?根據(jù)上面的UML圖,很容易給出實(shí)現(xiàn)。
//雙端隊(duì)列
class Deque
{
public: void push_back(int x) { cout<<"Deque push_back"<<endl; } void push_front(int x) { cout<<"Deque push_front"<<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) { deque.push_back(x); } void pop() { deque.pop_back(); }
private: Deque deque; //雙端隊(duì)列
};
//隊(duì)列
class Queue: public Sequence
{
public: void push(int x) { deque.push_back(x); } void pop() { deque.pop_front(); }
private: Deque deque; //雙端隊(duì)列
};
使用方式如下:
int main()
{ Sequence *s1 = new Stack(); Sequence *s2 = new Queue(); s1->push(1); s1->pop(); s2->push(1); s2->pop(); delete s1; delete s2; return 0;
}
4.單例模式
單例的一般實(shí)現(xiàn)比較簡(jiǎn)單,下面是代碼和UML圖。由于構(gòu)造函數(shù)是私有的,因此無(wú)法通過(guò)構(gòu)造函數(shù)實(shí)例化,唯一的方法就是通過(guò)調(diào)用靜態(tài)函數(shù)GetInstance。
UML圖:
?代碼:
//Singleton.h
class Singleton
{
public: static Singleton* GetInstance();
private: Singleton() {} static Singleton *singleton;
};
//Singleton.cpp
Singleton* Singleton::singleton = NULL;
Singleton* Singleton::GetInstance()
{ if(singleton == NULL) singleton = new Singleton(); return singleton;
}
這里只有一個(gè)類,如何實(shí)現(xiàn)Singleton類的子類呢?也就說(shuō)Singleton有很多子類,在一種應(yīng)用中,只選擇其中的一個(gè)。最容易就是在GetInstance函數(shù)中做判斷,比如可以傳遞一個(gè)字符串,根據(jù)字符串的內(nèi)容創(chuàng)建相應(yīng)的子類實(shí)例。這也是DP書(shū)上的一種解法,書(shū)上給的代碼不全。這里重新實(shí)現(xiàn)了一下,發(fā)現(xiàn)不是想象中的那么簡(jiǎn)單,最后實(shí)現(xiàn)的版本看上去很怪異。在VS2008下測(cè)試通過(guò)。
//Singleton.h
#pragma once
#include <iostream>
using namespace std; class Singleton
{
public: static Singleton* GetInstance(const char* name); virtual void Show() {}
protected: //必須為保護(hù),如果是私有屬性,子類無(wú)法訪問(wèn)父類的構(gòu)造函數(shù) Singleton() {}
private: static Singleton *singleton; //唯一實(shí)例的指針
}; //Singleton.cpp
#include "Singleton.h"
#include "SingletonA.h"
#include "SingletonB.h"
Singleton* Singleton::singleton = NULL;
Singleton* Singleton::GetInstance(const char* name)
{ if(singleton == NULL) { if(strcmp(name, "SingletonA") == 0) singleton = new SingletonA(); else if(strcmp(name,"SingletonB") == 0) singleton = new SingletonB(); else singleton = new Singleton(); } return singleton;
}
//SingletonA.h
#pragma once
#include "Singleton.h"
class SingletonA: public Singleton
{ friend class Singleton; //必須為友元類,否則父類無(wú)法訪問(wèn)子類的構(gòu)造函數(shù)
public: void Show() { cout<<"SingletonA"<<endl; }
private: //為保護(hù)屬性,這樣外界無(wú)法通過(guò)構(gòu)造函數(shù)進(jìn)行實(shí)例化 SingletonA() {}
};
//SingletonB.h
#pragma once
#include "Singleton.h"
class SingletonB: public Singleton
{ friend class Singleton; //必須為友元類,否則父類無(wú)法訪問(wèn)子類的構(gòu)造函數(shù)
public: void Show(){ cout<<"SingletonB"<<endl; }
private: //為保護(hù)屬性,這樣外界無(wú)法通過(guò)構(gòu)造函數(shù)進(jìn)行實(shí)例化 SingletonB() {}
};
#include "Singleton.h"
int main()
{ Singleton *st = Singleton::GetInstance("SingletonA"); st->Show(); return 0;
}
上面代碼有一個(gè)地方很詭異,父類為子類的友元,如果不是友元,函數(shù)GetInstance會(huì)報(bào)錯(cuò),意思就是無(wú)法調(diào)用SingletonA和SIngletonB的構(gòu)造函數(shù)。父類中調(diào)用子類的構(gòu)造函數(shù),我還是第一次碰到。當(dāng)然了把SingletonA和SIngletonB的屬性設(shè)為public,GetInstance函數(shù)就不會(huì)報(bào)錯(cuò)了,但是這樣外界就可以定義這些類的對(duì)象,違反了單例模式。
看似奇怪,其實(shí)也容易解釋。在父類中構(gòu)建子類的對(duì)象,相當(dāng)于是外界調(diào)用子類的構(gòu)造函數(shù),因此當(dāng)子類構(gòu)造函數(shù)的屬性為私有或保護(hù)時(shí),父類無(wú)法訪問(wèn)。為共有時(shí),外界就可以訪問(wèn)子類的構(gòu)造函數(shù)了,此時(shí)父類當(dāng)然也能訪問(wèn)了。只不過(guò)為了保證單例模式,所以子類的構(gòu)造函數(shù)不能為共有,但是又希望在父類中構(gòu)造子類的對(duì)象,即需要調(diào)用子類的構(gòu)造函數(shù),這里沒(méi)有辦法才出此下策:將父類聲明為子類的友元類。
5.原型模式、模板方法模式
DP書(shū)上的定義為:用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象。其中有一個(gè)詞很重要,那就是拷貝??梢哉f(shuō),拷貝是原型模式的精髓所在。舉個(gè)現(xiàn)實(shí)中的例子來(lái)介紹原型模式。找工作的時(shí)候,我們需要準(zhǔn)備簡(jiǎn)歷。假設(shè)沒(méi)有打印設(shè)備,因此需手寫(xiě)簡(jiǎn)歷,這些簡(jiǎn)歷的內(nèi)容都是一樣的。這樣有個(gè)缺陷,如果要修改簡(jiǎn)歷中的某項(xiàng),那么所有已寫(xiě)好的簡(jiǎn)歷都要修改,工作量很大。隨著科技的進(jìn)步,出現(xiàn)了打印設(shè)備。我們只需手寫(xiě)一份,然后利用打印設(shè)備復(fù)印多份即可。如果要修改簡(jiǎn)歷中的某項(xiàng),那么修改原始的版本就可以了,然后再?gòu)?fù)印。原始的那份手寫(xiě)稿相當(dāng)于是一個(gè)原型,有了它,就可以通過(guò)復(fù)印(拷貝)創(chuàng)造出更多的新簡(jiǎn)歷。這就是原型模式的基本思想。下面給出原型模式的UML圖,以剛才那個(gè)例子為實(shí)例。
?原型模式實(shí)現(xiàn)的關(guān)鍵就是實(shí)現(xiàn)Clone函數(shù),對(duì)于C++來(lái)說(shuō),其實(shí)就是拷貝構(gòu)造函數(shù),需實(shí)現(xiàn)深拷貝,下面給出一種實(shí)現(xiàn)。
//父類
class Resume
{
protected: char *name;
public: Resume() {} virtual ~Resume() {} virtual Resume* Clone() { return NULL; } virtual void Set(char *n) {} virtual void Show() {}
};
class ResumeA : public Resume
{
public: ResumeA(const char *str); //構(gòu)造函數(shù) ResumeA(const ResumeA &r); //拷貝構(gòu)造函數(shù) ~ResumeA(); //析構(gòu)函數(shù) ResumeA* Clone(); //克隆,關(guān)鍵所在 void Show(); //顯示內(nèi)容
};
ResumeA::ResumeA(const char *str)
{ if(str == NULL) { name = new char[1]; name[0] = '\0'; } else { name = new char[strlen(str)+1]; strcpy(name, str); }
}
ResumeA::~ResumeA() { delete [] name;}
ResumeA::ResumeA(const ResumeA &r) { name = new char[strlen(r.name)+1]; strcpy(name, r.name);
}
ResumeA* ResumeA::Clone() { return new ResumeA(*this);
}
void ResumeA::Show() { cout<<"ResumeA name : "<<name<<endl;
}
這里只給出了ResumeA的實(shí)現(xiàn),ResumeB的實(shí)現(xiàn)類似。使用的方式如下:
int main()
{ Resume *r1 = new ResumeA("A"); Resume *r2 = new ResumeB("B"); Resume *r3 = r1->Clone(); Resume *r4 = r2->Clone(); r1->Show(); r2->Show(); //刪除r1,r2 delete r1; delete r2; r1 = r2 = NULL; //深拷貝所以對(duì)r3,r4無(wú)影響 r3->Show(); r4->Show(); delete r3; delete r4; r3 = r4 = NULL;
}
最近有個(gè)招聘會(huì),可以帶上簡(jiǎn)歷去應(yīng)聘了。但是,其中有一家公司不接受簡(jiǎn)歷,而是給應(yīng)聘者發(fā)了一張簡(jiǎn)歷表,上面有基本信息、教育背景、工作經(jīng)歷等欄,讓?xiě)?yīng)聘者按照要求填寫(xiě)完整。每個(gè)人拿到這份表格后,就開(kāi)始填寫(xiě)。如果用程序?qū)崿F(xiàn)這個(gè)過(guò)程,該如何做呢?一種方案就是用模板方法模式:定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。我們的例子中,操作就是填寫(xiě)簡(jiǎn)歷這一過(guò)程,我們可以在父類中定義操作的算法骨架,而具體的實(shí)現(xiàn)由子類完成。下面給出它的UML圖。
?其中FillResume() 定義了操作的骨架,依次調(diào)用子類實(shí)現(xiàn)的函數(shù)。相當(dāng)于每個(gè)人填寫(xiě)簡(jiǎn)歷的實(shí)際過(guò)程。接著給出相應(yīng)的C++代碼。
//簡(jiǎn)歷
class Resume
{
protected: //保護(hù)成員 virtual void SetPersonalInfo() {} virtual void SetEducation() {} virtual void SetWorkExp() {}
public: void FillResume() { SetPersonalInfo(); SetEducation(); SetWorkExp(); }
};
class ResumeA: public Resume
{
protected: void SetPersonalInfo() { cout<<"A's PersonalInfo"<<endl; } void SetEducation() { cout<<"A's Education"<<endl; } void SetWorkExp() { cout<<"A's Work Experience"<<endl; }
};
class ResumeB: public Resume
{
protected: void SetPersonalInfo() { cout<<"B's PersonalInfo"<<endl; } void SetEducation() { cout<<"B's Education"<<endl; } void SetWorkExp() { cout<<"B's Work Experience"<<endl; }
};
使用方式如下:
int main()
{ Resume *r1; r1 = new ResumeA(); r1->FillResume(); delete r1; r1 = new ResumeB(); r1->FillResume(); delete r1; r1 = NULL; return 0;
}
6.建造者模式
建造者模式的定義將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示(DP)?!洞笤捲O(shè)計(jì)模式》舉了一個(gè)很好的例子——建造小人,一共需建造6個(gè)部分,頭部、身體、左右手、左右腳。與工廠模式不同,建造者模式是在導(dǎo)向者的控制下一步一步構(gòu)造產(chǎn)品的。建造小人就是在控制下一步步構(gòu)造出來(lái)的。創(chuàng)建者模式可以能更精細(xì)的控制構(gòu)建過(guò)程,從而能更精細(xì)的控制所得產(chǎn)品的內(nèi)部結(jié)構(gòu)。下面給出建造者模式的UML圖,以建造小人為實(shí)例。
?對(duì)于客戶來(lái)說(shuō),只需知道導(dǎo)向者就可以了,通過(guò)導(dǎo)向者,客戶就能構(gòu)造復(fù)雜的對(duì)象,而不需要知道具體的構(gòu)造過(guò)程。下面給出小人例子的代碼實(shí)現(xiàn)。
class Builder
{
public: virtual void BuildHead() {} virtual void BuildBody() {} virtual void BuildLeftArm(){} virtual void BuildRightArm() {} virtual void BuildLeftLeg() {} virtual void BuildRightLeg() {}
};
//構(gòu)造瘦人
class ThinBuilder : public Builder
{
public: void BuildHead() { cout<<"build thin body"<<endl; } void BuildBody() { cout<<"build thin head"<<endl; } void BuildLeftArm() { cout<<"build thin leftarm"<<endl; } void BuildRightArm() { cout<<"build thin rightarm"<<endl; } void BuildLeftLeg() { cout<<"build thin leftleg"<<endl; } void BuildRightLeg() { cout<<"build thin rightleg"<<endl; }
};
//構(gòu)造胖人
class FatBuilder : public Builder
{
public: void BuildHead() { cout<<"build fat body"<<endl; } void BuildBody() { cout<<"build fat head"<<endl; } void BuildLeftArm() { cout<<"build fat leftarm"<<endl; } void BuildRightArm() { cout<<"build fat rightarm"<<endl; } void BuildLeftLeg() { cout<<"build fat leftleg"<<endl; } void BuildRightLeg() { cout<<"build fat rightleg"<<endl; }
};
//構(gòu)造的指揮官
class Director
{
private: Builder *m_pBuilder;
public: Director(Builder *builder) { m_pBuilder = builder; } void Create(){ m_pBuilder->BuildHead(); m_pBuilder->BuildBody(); m_pBuilder->BuildLeftArm(); m_pBuilder->BuildRightArm(); m_pBuilder->BuildLeftLeg(); m_pBuilder->BuildRightLeg(); }
};
客戶的使用方式:
int main() int main()
{ FatBuilder thin; Director director(&thin); director.Create(); return 0;
}
{ FatBuilder thin; Director director(&thin); director.Create(); return 0;
}
7.外觀模式、組合模式
外觀模式應(yīng)該是用的很多的一種模式,特別是當(dāng)一個(gè)系統(tǒng)很復(fù)雜時(shí),系統(tǒng)提供給客戶的是一個(gè)簡(jiǎn)單的對(duì)外接口,而把里面復(fù)雜的結(jié)構(gòu)都封裝了起來(lái)。客戶只需使用這些簡(jiǎn)單接口就能使用這個(gè)系統(tǒng),而不需要關(guān)注內(nèi)部復(fù)雜的結(jié)構(gòu)。DP一書(shū)的定義:為子系統(tǒng)中的一組接口提供一個(gè)一致的界面, 外觀模式定義了一個(gè)高層接口,這個(gè)接口使得這一子系統(tǒng)更加容易使用。舉個(gè)編譯器的例子,假設(shè)編譯一個(gè)程序需要經(jīng)過(guò)四個(gè)步驟:詞法分析、語(yǔ)法分析、中間代碼生成、機(jī)器碼生成。學(xué)過(guò)編譯都知道,每一步都很復(fù)雜。對(duì)于編譯器這個(gè)系統(tǒng),就可以使用外觀模式??梢远x一個(gè)高層接口,比如名為Compiler的類,里面有一個(gè)名為Run的函數(shù)。客戶只需調(diào)用這個(gè)函數(shù)就可以編譯程序,至于Run函數(shù)內(nèi)部的具體操作,客戶無(wú)需知道。下面給出UML圖,以編譯器為實(shí)例。
?相應(yīng)的代碼實(shí)現(xiàn)為:
class Scanner
{
public: void Scan() { cout<<"詞法分析"<<endl; }
};
class Parser
{
public: void Parse() { cout<<"語(yǔ)法分析"<<endl; }
};
class GenMidCode
{
public: void GenCode() { cout<<"產(chǎn)生中間代碼"<<endl; }
};
class GenMachineCode
{
public: void GenCode() { cout<<"產(chǎn)生機(jī)器碼"<<endl;}
};
//高層接口
class Compiler
{
public: void Run() { Scanner scanner; Parser parser; GenMidCode genMidCode; GenMachineCode genMacCode; scanner.Scan(); parser.Parse(); genMidCode.GenCode(); genMacCode.GenCode(); }
};
客戶使用方式:
int main()
{ Compiler compiler; Console Compiler(); return 0;
}
這就是外觀模式,它有幾個(gè)特點(diǎn)(摘自DP一書(shū)),(1)它對(duì)客戶屏蔽子系統(tǒng)組件,因而減少了客戶處理的對(duì)象的數(shù)目并使得子系統(tǒng)使用起來(lái)更加方便。(2)它實(shí)現(xiàn)了子系統(tǒng)與客戶之間的松耦合關(guān)系,而子系統(tǒng)內(nèi)部的功能組件往往是緊耦合的。(3)如果應(yīng)用需要,它并不限制它們使用子系統(tǒng)類。
結(jié)合上面編譯器這個(gè)例子,進(jìn)一步說(shuō)明。對(duì)于(1),編譯器類對(duì)客戶屏蔽了子系統(tǒng)組件,客戶只需處理編譯器的對(duì)象就可以方便的使用子系統(tǒng)。對(duì)于(2),子系統(tǒng)的變化,不會(huì)影響到客戶的使用,體現(xiàn)了子系統(tǒng)與客戶的松耦合關(guān)系。對(duì)于(3),如果客戶希望使用詞法分析器,只需定義詞法分析的類對(duì)象即可,并不受到限制。
外觀模式在構(gòu)建大型系統(tǒng)時(shí)非常有用。接下來(lái)介紹另一種模式,稱為組合模式。感覺(jué)有點(diǎn)像外觀模式,剛才我們實(shí)現(xiàn)外觀模式時(shí),在Compiler這個(gè)類中包含了多個(gè)類的對(duì)象,就像把這些類組合在了一起。組合模式是不是這個(gè)意思,有點(diǎn)相似,其實(shí)不然。
DP書(shū)上給出的定義:將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。組合使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。注意兩個(gè)字“樹(shù)形”。這種樹(shù)形結(jié)構(gòu)在現(xiàn)實(shí)生活中隨處可見(jiàn),比如一個(gè)集團(tuán)公司,它有一個(gè)母公司,下設(shè)很多家子公司。不管是母公司還是子公司,都有各自直屬的財(cái)務(wù)部、人力資源部、銷售部等。對(duì)于母公司來(lái)說(shuō),不論是子公司,還是直屬的財(cái)務(wù)部、人力資源部,都是它的部門。整個(gè)公司的部門拓?fù)鋱D就是一個(gè)樹(shù)形結(jié)構(gòu)。
下面給出組合模式的UML圖。從圖中可以看到,FinanceDepartment、HRDepartment兩個(gè)類作為葉結(jié)點(diǎn),因此沒(méi)有定義添加函數(shù)。而ConcreteCompany類可以作為中間結(jié)點(diǎn),所以可以有添加函數(shù)。那么怎么添加呢?這個(gè)類中定義了一個(gè)鏈表,用來(lái)放添加的元素。
?相應(yīng)的代碼實(shí)現(xiàn)為:
class Company
{
public: Company(string name) { m_name = name; } virtual ~Company(){} virtual void Add(Company *pCom){} virtual void Show(int depth) {}
protected: string m_name;
};
//具體公司
class ConcreteCompany : public Company
{
public: ConcreteCompany(string name): Company(name) {} virtual ~ConcreteCompany() {} void Add(Company *pCom) { m_listCompany.push_back(pCom); } //位于樹(shù)的中間,可以增加子樹(shù) void Show(int depth) { for(int i = 0;i < depth; i++) cout<<"-"; cout<<m_name<<endl; list<Company *>::iterator iter=m_listCompany.begin(); for(; iter != m_listCompany.end(); iter++) //顯示下層結(jié)點(diǎn) (*iter)->Show(depth + 2); }
private: list<Company *> m_listCompany;
};
//具體的部門,財(cái)務(wù)部
class FinanceDepartment : public Company
{
public: FinanceDepartment(string name):Company(name){} virtual ~FinanceDepartment() {} virtual void Show(int depth) //只需顯示,無(wú)限添加函數(shù),因?yàn)橐咽侨~結(jié)點(diǎn) { for(int i = 0; i < depth; i++) cout<<"-"; cout<<m_name<<endl; }
};
//具體的部門,人力資源部
class HRDepartment :public Company
{
public: HRDepartment(string name):Company(name){} virtual ~HRDepartment() {} virtual void Show(int depth) //只需顯示,無(wú)限添加函數(shù),因?yàn)橐咽侨~結(jié)點(diǎn) { for(int i = 0; i < depth; i++) cout<<"-"; cout<<m_name<<endl; }
};
客戶使用方式:
int main()
{ Company *root = new ConcreteCompany("總公司"); Company *leaf1=new FinanceDepartment("財(cái)務(wù)部"); Company *leaf2=new HRDepartment("人力資源部"); root->Add(leaf1); root->Add(leaf2); //分公司A Company *mid1 = new ConcreteCompany("分公司A"); Company *leaf3=new FinanceDepartment("財(cái)務(wù)部"); Company *leaf4=new HRDepartment("人力資源部"); mid1->Add(leaf3); mid1->Add(leaf4); root->Add(mid1); //分公司B Company *mid2=new ConcreteCompany("分公司B"); FinanceDepartment *leaf5=new FinanceDepartment("財(cái)務(wù)部"); HRDepartment *leaf6=new HRDepartment("人力資源部"); mid2->Add(leaf5); mid2->Add(leaf6); root->Add(mid2); root->Show(0); delete leaf1; delete leaf2; delete leaf3; delete leaf4; delete leaf5; delete leaf6; delete mid1; delete mid2; delete root; return 0;
}
上面的實(shí)現(xiàn)方式有缺點(diǎn),就是內(nèi)存的釋放不好,需要客戶自己動(dòng)手,非常不方便。有待改進(jìn),比較好的做法是讓ConcreteCompany類來(lái)釋放。因?yàn)樗械闹羔樁际谴嬖贑oncreteCompany類的鏈表中。C++的麻煩,沒(méi)有垃圾回收機(jī)制。
8.代理模式
[DP]上的定義:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。有四種常用的情況:(1)遠(yuǎn)程代理,(2)虛代理,(3)保護(hù)代理,(4)智能引用。本文主要介紹虛代理和智能引用兩種情況。
考慮一個(gè)可以在文檔中嵌入圖形對(duì)象的文檔編輯器。有些圖形對(duì)象的創(chuàng)建開(kāi)銷很大。但是打開(kāi)文檔必須很迅速,因此我們?cè)诖蜷_(kāi)文檔時(shí)應(yīng)避免一次性創(chuàng)建所有開(kāi)銷很大的對(duì)象。這里就可以運(yùn)用代理模式,在打開(kāi)文檔時(shí),并不打開(kāi)圖形對(duì)象,而是打開(kāi)圖形對(duì)象的代理以替代真實(shí)的圖形。待到真正需要打開(kāi)圖形時(shí),仍由代理負(fù)責(zé)打開(kāi)。這是[DP]一書(shū)上的給的例子。下面給出代理模式的UML圖。
?簡(jiǎn)單實(shí)現(xiàn)如下:
class Image
{
public: Image(string name): m_imageName(name) {} virtual ~Image() {} virtual void Show() {}
protected: string m_imageName;
};
class BigImage: public Image
{
public: BigImage(string name):Image(name) {} ~BigImage() {} void Show() { cout<<"Show big image : "<<m_imageName<<endl; }
};
class BigImageProxy: public Image
{
private: BigImage *m_bigImage;
public: BigImageProxy(string name):Image(name),m_bigImage(0) {} ~BigImageProxy() { delete m_bigImage; } void Show() { if(m_bigImage == NULL) m_bigImage = new BigImage(m_imageName); m_bigImage->Show(); }
};
客戶調(diào)用:
int main()
{ Image *image = new BigImageProxy("proxy.jpg"); //代理 image->Show(); //需要時(shí)由代理負(fù)責(zé)打開(kāi) delete image; return 0;
}
在這個(gè)例子屬于虛代理的情況,下面給兩個(gè)智能引用的例子。一個(gè)是C++中的auto_ptr,另一個(gè)是smart_ptr。自己實(shí)現(xiàn)了一下。先給出auto_ptr的代碼實(shí)現(xiàn):
template<class T>
class auto_ptr {
public: explicit auto_ptr(T *p = 0): pointee(p) {} auto_ptr(auto_ptr<T>& rhs): pointee(rhs.release()) {} ~auto_ptr() { delete pointee; } auto_ptr<T>& operator=(auto_ptr<T>& rhs) { if (this != &rhs) reset(rhs.release()); return *this; } T& operator*() const { return *pointee; } T* operator->() const { return pointee; } T* get() const { return pointee; } T* release() { T *oldPointee = pointee; pointee = 0; return oldPointee; } void reset(T *p = 0) { if (pointee != p) { delete pointee; pointee = p; } }
private: T *pointee;
};
閱讀上面的代碼,我們可以發(fā)現(xiàn) auto_ptr 類就是一個(gè)代理,客戶只需操作auto_prt的對(duì)象,而不需要與被代理的指針pointee打交道。auto_ptr 的好處在于為動(dòng)態(tài)分配的對(duì)象提供異常安全。因?yàn)樗靡粋€(gè)對(duì)象存儲(chǔ)需要被自動(dòng)釋放的資源,然后依靠對(duì)象的析構(gòu)函數(shù)來(lái)釋放資源。這樣客戶就不需要關(guān)注資源的釋放,由auto_ptr 對(duì)象自動(dòng)完成。實(shí)現(xiàn)中的一個(gè)關(guān)鍵就是重載了解引用操作符和箭頭操作符,從而使得auto_ptr的使用與真實(shí)指針類似。
我們知道C++中沒(méi)有垃圾回收機(jī)制,可以通過(guò)智能指針來(lái)彌補(bǔ),下面給出智能指針的一種實(shí)現(xiàn),采用了引用計(jì)數(shù)的策略。
template <typename T>
class smart_ptr
{
public: smart_ptr(T *p = 0): pointee(p), count(new size_t(1)) { } //初始的計(jì)數(shù)值為1 smart_ptr(const smart_ptr &rhs): pointee(rhs.pointee), count(rhs.count) { ++*count; } //拷貝構(gòu)造函數(shù),計(jì)數(shù)加1 ~smart_ptr() { decr_count(); } //析構(gòu),計(jì)數(shù)減1,減到0時(shí)進(jìn)行垃圾回收,即釋放空間 smart_ptr& operator= (const smart_ptr& rhs) //重載賦值操作符 { //給自身賦值也對(duì),因?yàn)槿绻陨碣x值,計(jì)數(shù)器先減1,再加1,并未發(fā)生改變 ++*count; decr_count(); pointee = rhs.pointee; count = rhs.count; return *this; } //重載箭頭操作符和解引用操作符,未提供指針的檢查 T *operator->() { return pointee; } const T *operator->() const { return pointee; } T &operator*() { return *pointee; } const T &operator*() const { return *pointee; } size_t get_refcount() { return *count; } //獲得引用計(jì)數(shù)器值
private: T *pointee; //實(shí)際指針,被代理 size_t *count; //引用計(jì)數(shù)器 void decr_count() //計(jì)數(shù)器減1 { if(--*count == 0) { delete pointee; delete count; } }
};
9.享元模式
舉個(gè)圍棋的例子,圍棋的棋盤共有361格,即可放361個(gè)棋子。現(xiàn)在要實(shí)現(xiàn)一個(gè)圍棋程序,該怎么辦呢?首先要考慮的是棋子棋盤的實(shí)現(xiàn),可以定義一個(gè)棋子的類,成員變量包括棋子的顏色、形狀、位置等信息,另外再定義一個(gè)棋盤的類,成員變量中有個(gè)容器,用于存放棋子的對(duì)象。下面給出代碼表示:
棋子的定義,當(dāng)然棋子的屬性除了顏色和位置,還有其他的,這里略去。這兩個(gè)屬性足以說(shuō)明問(wèn)題。
//棋子顏色
enum PieceColor {BLACK, WHITE};
//棋子位置
struct PiecePos
{ int x; int y; PiecePos(int a, int b): x(a), y(b) {}
};
//棋子定義
class Piece
{
protected: PieceColor m_color; //顏色 PiecePos m_pos; //位置
public: Piece(PieceColor color, PiecePos pos): m_color(color), m_pos(pos) {} ~Piece() {} virtual void Draw() {}
};
class BlackPiece: public Piece
{
public: BlackPiece(PieceColor color, PiecePos pos): Piece(color, pos) {} ~BlackPiece() {} void Draw() { cout<<"繪制一顆黑棋"<<endl;}
};
class WhitePiece: public Piece
{
public: WhitePiece(PieceColor color, PiecePos pos): Piece(color, pos) {} ~WhitePiece() {} void Draw() { cout<<"繪制一顆白棋"<<endl;}
};
棋盤的定義:
class PieceBoard
{
private: vector<Piece*> m_vecPiece; //棋盤上已有的棋子 string m_blackName; //黑方名稱 string m_whiteName; //白方名稱
public: PieceBoard(string black, string white): m_blackName(black), m_whiteName(white){} ~PieceBoard() { Clear(); } void SetPiece(PieceColor color, PiecePos pos) //一步棋,在棋盤上放一顆棋子 { Piece * piece = NULL; if(color == BLACK) //黑方下的 { piece = new BlackPiece(color, pos); //獲取一顆黑棋 cout<<m_blackName<<"在位置("<<pos.x<<','<<pos.y<<")"; piece->Draw(); //在棋盤上繪制出棋子 } else { piece = new WhitePiece(color, pos); cout<<m_whiteName<<"在位置("<<pos.x<<','<<pos.y<<")"; piece->Draw(); } m_vecPiece.push_back(piece); //加入容器中 } void Clear() //釋放內(nèi)存 { int size = m_vecPiece.size(); for(int i = 0; i < size; i++) delete m_vecPiece[i]; }
};
客戶的使用方式如下:
int main()
{ PieceBoard pieceBoard("A","B"); pieceBoard.SetPiece(BLACK, PiecePos(4, 4)); pieceBoard.SetPiece(WHITE, PiecePos(4, 16)); pieceBoard.SetPiece(BLACK, PiecePos(16, 4)); pieceBoard.SetPiece(WHITE, PiecePos(16, 16));
}
可以發(fā)現(xiàn),棋盤的容器中存放了已下的棋子,而每個(gè)棋子包含棋子的所有屬性。一盤棋往往需要含上百顆棋子,采用上面這種實(shí)現(xiàn),占用的空間太大了。如何改進(jìn)呢?用享元模式。其定義為:運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度的對(duì)象。
在圍棋中,棋子就是大量細(xì)粒度的對(duì)象。其屬性有內(nèi)在的,比如顏色、形狀等,也有外在的,比如在棋盤上的位置。內(nèi)在的屬性是可以共享的,區(qū)分在于外在屬性。因此,可以這樣設(shè)計(jì),只需定義兩個(gè)棋子的對(duì)象,一顆黑棋和一顆白棋,這兩個(gè)對(duì)象含棋子的內(nèi)在屬性;棋子的外在屬性,即在棋盤上的位置可以提取出來(lái),存放在單獨(dú)的容器中。相比之前的方案,現(xiàn)在容器中僅僅存放了位置屬性,而原來(lái)則是棋子對(duì)象。顯然,現(xiàn)在的方案大大減少了對(duì)于空間的需求。
關(guān)注PieceBoard 的容器,之前是vector<Piece*> m_vecPiece,現(xiàn)在是vector<PiecePos> m_vecPos。這里是關(guān)鍵。
棋子的新定義,只包含內(nèi)在屬性:
//棋子顏色
enum PieceColor {BLACK, WHITE};
//棋子位置
struct PiecePos
{ int x; int y; PiecePos(int a, int b): x(a), y(b) {}
};
//棋子定義
class Piece
{
protected: PieceColor m_color; //顏色
public: Piece(PieceColor color): m_color(color) {} ~Piece() {} virtual void Draw() {}
};
class BlackPiece: public Piece
{
public: BlackPiece(PieceColor color): Piece(color) {} ~BlackPiece() {} void Draw() { cout<<"繪制一顆黑棋\n"; }
};
class WhitePiece: public Piece
{
public: WhitePiece(PieceColor color): Piece(color) {} ~WhitePiece() {} void Draw() { cout<<"繪制一顆白棋\n";}
};
相應(yīng)棋盤的定義為:
class PieceBoard
{
private: vector<PiecePos> m_vecPos; //存放棋子的位置 Piece *m_blackPiece; //黑棋棋子 Piece *m_whitePiece; //白棋棋子 string m_blackName; string m_whiteName;
public: PieceBoard(string black, string white): m_blackName(black), m_whiteName(white) { m_blackPiece = NULL; m_whitePiece = NULL; } ~PieceBoard() { delete m_blackPiece; delete m_whitePiece;} void SetPiece(PieceColor color, PiecePos pos) { if(color == BLACK) { if(m_blackPiece == NULL) //只有一顆黑棋 m_blackPiece = new BlackPiece(color); cout<<m_blackName<<"在位置("<<pos.x<<','<<pos.y<<")"; m_blackPiece->Draw(); } else { if(m_whitePiece == NULL) m_whitePiece = new WhitePiece(color); cout<<m_whiteName<<"在位置("<<pos.x<<','<<pos.y<<")"; m_whitePiece->Draw(); } m_vecPos.push_back(pos); }
};
客戶的使用方式一樣,這里不重復(fù)給出,現(xiàn)在給出享元模式的UML圖,以圍棋為例。棋盤中含兩個(gè)共享的對(duì)象,黑棋子和白棋子,所有棋子的外在屬性都存放在單獨(dú)的容器中。
?
10、橋接模式
[DP]書(shū)上定義:將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化??紤]裝操作系統(tǒng),有多種配置的計(jì)算機(jī),同樣也有多款操作系統(tǒng)。如何運(yùn)用橋接模式呢?可以將操作系統(tǒng)和計(jì)算機(jī)分別抽象出來(lái),讓它們各自發(fā)展,減少它們的耦合度。當(dāng)然了,兩者之間有標(biāo)準(zhǔn)的接口。這樣設(shè)計(jì),不論是對(duì)于計(jì)算機(jī),還是操作系統(tǒng)都是非常有利的。下面給出這種設(shè)計(jì)的UML圖,其實(shí)就是橋接模式的UML圖。
?給出C++的一種實(shí)現(xiàn):
//操作系統(tǒng)
class OS
{
public: virtual void InstallOS_Imp() {}
};
class WindowOS: public OS
{
public: void InstallOS_Imp() { cout<<"安裝Window操作系統(tǒng)"<<endl; }
};
class LinuxOS: public OS
{
public: void InstallOS_Imp() { cout<<"安裝Linux操作系統(tǒng)"<<endl; }
};
class UnixOS: public OS
{
public: void InstallOS_Imp() { cout<<"安裝Unix操作系統(tǒng)"<<endl; }
};
//計(jì)算機(jī)
class Computer
{
public: virtual void InstallOS(OS *os) {}
};
class DellComputer: public Computer
{
public: void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
class AppleComputer: public Computer
{
public: void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
class HPComputer: public Computer
{
public: void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
客戶使用方式:
int main()
{ OS *os1 = new WindowOS(); OS *os2 = new LinuxOS(); Computer *computer1 = new AppleComputer(); computer1->InstallOS(os1); computer1->InstallOS(os2);
}
11.裝飾模式
裝飾模式:動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。就增加功能來(lái)說(shuō),裝飾模式相比生成子類更為靈活。有時(shí)我們希望給某個(gè)對(duì)象而不是整個(gè)類添加一些功能。比如有一個(gè)手機(jī),允許你為手機(jī)添加特性,比如增加掛件、屏幕貼膜等。一種靈活的設(shè)計(jì)方式是,將手機(jī)嵌入到另一對(duì)象中,由這個(gè)對(duì)象完成特性的添加,我們稱這個(gè)嵌入的對(duì)象為裝飾。這個(gè)裝飾與它所裝飾的組件接口一致,因此它對(duì)使用該組件的客戶透明。下面給出裝飾模式的UML圖。
?在這種設(shè)計(jì)中,手機(jī)的裝飾功能被獨(dú)立出來(lái),可以單獨(dú)發(fā)展,進(jìn)而簡(jiǎn)化了具體手機(jī)類的設(shè)計(jì)。下面給出Phone類的實(shí)現(xiàn):
//公共抽象類
class Phone
{
public: Phone() {} virtual ~Phone() {} virtual void ShowDecorate() {}
};
具體的手機(jī)類的定義:
//具體的手機(jī)類
class iPhone : public Phone
{
private: string m_name; //手機(jī)名稱
public: iPhone(string name): m_name(name){} ~iPhone() {} void ShowDecorate() { cout<<m_name<<"的裝飾"<<endl;}
};
//具體的手機(jī)類
class NokiaPhone : public Phone
{
private: string m_name;
public: NokiaPhone(string name): m_name(name){} ~NokiaPhone() {} void ShowDecorate() { cout<<m_name<<"的裝飾"<<endl;}
};
裝飾類的實(shí)現(xiàn):
//裝飾類
class DecoratorPhone : public Phone
{
private: Phone *m_phone; //要裝飾的手機(jī)
public: DecoratorPhone(Phone *phone): m_phone(phone) {} virtual void ShowDecorate() { m_phone->ShowDecorate(); }
};
//具體的裝飾類
class DecoratorPhoneA : public DecoratorPhone
{
public: DecoratorPhoneA(Phone *phone) : DecoratorPhone(phone) {} void ShowDecorate() { DecoratorPhone::ShowDecorate(); AddDecorate(); }
private: void AddDecorate() { cout<<"增加掛件"<<endl; } //增加的裝飾
};
//具體的裝飾類
class DecoratorPhoneB : public DecoratorPhone
{
public: DecoratorPhoneB(Phone *phone) : DecoratorPhone(phone) {} void ShowDecorate() { DecoratorPhone::ShowDecorate(); AddDecorate(); }
private: void AddDecorate() { cout<<"屏幕貼膜"<<endl; } //增加的裝飾
};
客戶使用方式:
int main()
{ Phone *iphone = new NokiaPhone("6300"); Phone *dpa = new DecoratorPhoneA(iphone); //裝飾,增加掛件 Phone *dpb = new DecoratorPhoneB(dpa); //裝飾,屏幕貼膜 dpb->ShowDecorate(); delete dpa; delete dpb; delete iphone; return 0;
}
裝飾模式提供了更加靈活的向?qū)ο筇砑勇氊?zé)的方式。可以用添加和分離的方法,用裝飾在運(yùn)行時(shí)刻增加和刪除職責(zé)。裝飾模式提供了一種“即用即付”的方法來(lái)添加職責(zé)。它并不試圖在一個(gè)復(fù)雜的可定制的類中支持所有可預(yù)見(jiàn)的特征,相反,你可以定義一個(gè)簡(jiǎn)單的類,并且用裝飾類給它逐漸地添加功能。可以從簡(jiǎn)單的部件組合出復(fù)雜的功能
12.備忘錄模式
備忘錄模式:在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài)。這樣以后就可將該對(duì)象恢復(fù)到原先保存的狀態(tài)[DP]。舉個(gè)簡(jiǎn)單的例子,我們玩游戲時(shí)都會(huì)保存進(jìn)度,所保存的進(jìn)度以文件的形式存在。這樣下次就可以繼續(xù)玩,而不用從頭開(kāi)始。這里的進(jìn)度其實(shí)就是游戲的內(nèi)部狀態(tài),而這里的文件相當(dāng)于是在游戲之外保存狀態(tài)。這樣,下次就可以從文件中讀入保存的進(jìn)度,從而恢復(fù)到原來(lái)的狀態(tài)。這就是備忘錄模式。
給出備忘錄模式的UML圖,以保存游戲的進(jìn)度為例。
?Memento類定義了內(nèi)部的狀態(tài),而Caretake類是一個(gè)保存進(jìn)度的管理者,GameRole類是游戲角色類。可以看到GameRole的對(duì)象依賴于Memento對(duì)象,而與Caretake對(duì)象無(wú)關(guān)。下面給出一個(gè)簡(jiǎn)單的是實(shí)現(xiàn)。
//需保存的信息
class Memento
{
public: int m_vitality; //生命值 int m_attack; //進(jìn)攻值 int m_defense; //防守值
public: Memento(int vitality, int attack, int defense): m_vitality(vitality),m_attack(attack),m_defense(defense){} Memento& operator=(const Memento &memento) { m_vitality = memento.m_vitality; m_attack = memento.m_attack; m_defense = memento.m_defense; return *this; }
};
//游戲角色
class GameRole
{
private: int m_vitality; int m_attack; int m_defense;
public: GameRole(): m_vitality(100),m_attack(100),m_defense(100) {} Memento Save() //保存進(jìn)度,只與Memento對(duì)象交互,并不牽涉到Caretake { Memento memento(m_vitality, m_attack, m_defense); return memento; } void Load(Memento memento) //載入進(jìn)度,只與Memento對(duì)象交互,并不牽涉到Caretake { m_vitality = memento.m_vitality; m_attack = memento.m_attack; m_defense = memento.m_defense; } void Show() { cout<<"vitality : "<< m_vitality<<", attack : "<< m_attack<<", defense : "<< m_defense<<endl; } void Attack() { m_vitality -= 10; m_attack -= 10; m_defense -= 10; }
};
//保存的進(jìn)度庫(kù)
class Caretake
{
public: Caretake() {} void Save(Memento menento) { m_vecMemento.push_back(menento); } Memento Load(int state) { return m_vecMemento[state]; }
private: vector<Memento> m_vecMemento;
};
客戶使用方式:
//測(cè)試案例
int main()
{ Caretake caretake; GameRole role; role.Show(); //初始值 caretake.Save(role.Save()); //保存狀態(tài) role.Attack(); role.Show(); //進(jìn)攻后 role.Load(caretake.Load(0)); //載入狀態(tài) role.Show(); //恢復(fù)到狀態(tài)0 return 0;
}
13.中介者模式
中介者模式:用一個(gè)中介對(duì)象來(lái)封裝一系列的對(duì)象交互。中介者使各對(duì)象不需要顯式地相互引用,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互。中介者模式的例子很多,大到聯(lián)合國(guó)安理會(huì),小到房屋中介,都扮演了中間者的角色,協(xié)調(diào)各方利益。
本文就以租房為例子,如果沒(méi)有房屋中介,那么房客要自己找房東,而房東也要自己找房客,非常不方便。有了房屋中介機(jī)構(gòu)就方便了,房東可以把要出租的房屋信息放到中介機(jī)構(gòu),而房客可以去中介機(jī)構(gòu)咨詢。在軟件中,就是多個(gè)對(duì)象之間需要通信,如果沒(méi)有中介,對(duì)象就需要知道其他對(duì)象,最壞情況下,可能需要知道所有其他對(duì)象,而有了中介對(duì)象就方便多了,對(duì)象只需與中介對(duì)象通信,而不用知道其他的對(duì)象。這就是中介者模式,下面以租房為例,給出中介者模式的UML圖。
?實(shí)現(xiàn)不難,下面給出C++的實(shí)現(xiàn):
class Mediator;
//抽象人
class Person
{
protected: Mediator *m_mediator; //中介
public: virtual void SetMediator(Mediator *mediator){} //設(shè)置中介 virtual void SendMessage(string message) {} //向中介發(fā)送信息 virtual void GetMessage(string message) {} //從中介獲取信息
};
//抽象中介機(jī)構(gòu)
class Mediator
{
public: virtual void Send(string message, Person *person) {} virtual void SetA(Person *A) {} //設(shè)置其中一方 virtual void SetB(Person *B) {}
};
//租房者
class Renter: public Person
{
public: void SetMediator(Mediator *mediator) { m_mediator = mediator; } void SendMessage(string message) { m_mediator->Send(message, this); } void GetMessage(string message) { cout<<"租房者收到信息"<<message; }
};
//房東
class Landlord: public Person
{
public: void SetMediator(Mediator *mediator) { m_mediator = mediator; } void SendMessage(string message) { m_mediator->Send(message, this); } void GetMessage(string message) { cout<<"房東收到信息:"<<message; }
};
//房屋中介
class HouseMediator : public Mediator
{
private: Person *m_A; //租房者 Person *m_B; //房東
public: HouseMediator(): m_A(0), m_B(0) {} void SetA(Person *A) { m_A = A; } void SetB(Person *B) { m_B = B; } void Send(string message, Person *person) { if(person == m_A) //租房者給房東發(fā)信息 m_B->GetMessage(message); //房東收到信息 else m_A->GetMessage(message); }
};
客戶使用方式如下:
//測(cè)試案例
int main()
{ Mediator *mediator = new HouseMediator(); Person *person1 = new Renter(); //租房者 Person *person2 = new Landlord(); //房東 mediator->SetA(person1); mediator->SetB(person2); person1->SetMediator(mediator); person2->SetMediator(mediator); person1->SendMessage("我想在南京路附近租套房子,價(jià)格800元一個(gè)月\n"); person2->SendMessage("出租房子:南京路100號(hào),70平米,1000元一個(gè)月\n"); delete person1; delete person2; delete mediator; return 0;
}
14.職責(zé)鏈模式
職責(zé)鏈模式:使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理它為止。其思想很簡(jiǎn)單,考慮員工要求加薪。公司的管理者一共有三級(jí),總經(jīng)理、總監(jiān)、經(jīng)理,如果一個(gè)員工要求加薪,應(yīng)該向主管的經(jīng)理申請(qǐng),如果加薪的數(shù)量在經(jīng)理的職權(quán)內(nèi),那么經(jīng)理可以直接批準(zhǔn),否則將申請(qǐng)上交給總監(jiān)??偙O(jiān)的處理方式也一樣,總經(jīng)理可以處理所有請(qǐng)求。這就是典型的職責(zé)鏈模式,請(qǐng)求的處理形成了一條鏈,直到有一個(gè)對(duì)象處理請(qǐng)求。給出這個(gè)例子的UML圖。
?代碼的實(shí)現(xiàn)比較簡(jiǎn)單,如下所示:
//抽象管理者
class Manager
{
protected: Manager *m_manager; string m_name;
public: Manager(Manager *manager, string name):m_manager(manager), m_name(name){} virtual void DealWithRequest(string name, int num) {}
};
//經(jīng)理
class CommonManager: public Manager
{
public: CommonManager(Manager *manager, string name):Manager(manager,name) {} void DealWithRequest(string name, int num) { if(num < 500) //經(jīng)理職權(quán)之內(nèi) { cout<<"經(jīng)理"<<m_name<<"批準(zhǔn)"<<name<<"加薪"<<num<<"元"<<endl<<endl; } else { cout<<"經(jīng)理"<<m_name<<"無(wú)法處理,交由總監(jiān)處理"<<endl; m_manager->DealWithRequest(name, num); } }
};
//總監(jiān)
class Majordomo: public Manager
{
public: Majordomo(Manager *manager, string name):Manager(manager,name) {} void DealWithRequest(string name, int num) { if(num < 1000) //總監(jiān)職權(quán)之內(nèi) { cout<<"總監(jiān)"<<m_name<<"批準(zhǔn)"<<name<<"加薪"<<num<<"元"<<endl<<endl; } else { cout<<"總監(jiān)"<<m_name<<"無(wú)法處理,交由總經(jīng)理處理"<<endl; m_manager->DealWithRequest(name, num); } }
};
//總經(jīng)理
class GeneralManager: public Manager
{
public: GeneralManager(Manager *manager, string name):Manager(manager,name) {} void DealWithRequest(string name, int num) //總經(jīng)理可以處理所有請(qǐng)求 { cout<<"總經(jīng)理"<<m_name<<"批準(zhǔn)"<<name<<"加薪"<<num<<"元"<<endl<<endl; }
};
客戶調(diào)用方式為:
//測(cè)試案例
int main()
{ Manager *general = new GeneralManager(NULL, "A"); //設(shè)置上級(jí),總經(jīng)理沒(méi)有上級(jí) Manager *majordomo = new Majordomo(general, "B"); //設(shè)置上級(jí) Manager *common = new CommonManager(majordomo, "C"); //設(shè)置上級(jí) common->DealWithRequest("D",300); //員工D要求加薪 common->DealWithRequest("E", 600); common->DealWithRequest("F", 1000); delete common; delete majordomo; delete general; return 0;
}
15.觀察者模式
觀察者模式:定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。它還有兩個(gè)別名,依賴(Dependents),發(fā)布-訂閱(Publish-Subsrcibe)??梢耘e個(gè)博客訂閱的例子,當(dāng)博主發(fā)表新文章的時(shí)候,即博主狀態(tài)發(fā)生了改變,那些訂閱的讀者就會(huì)收到通知,然后進(jìn)行相應(yīng)的動(dòng)作,比如去看文章,或者收藏起來(lái)。博主與讀者之間存在種一對(duì)多的依賴關(guān)系。下面給出相應(yīng)的UML圖設(shè)計(jì)。
?可以看到博客類中有一個(gè)觀察者鏈表(即訂閱者),當(dāng)博客的狀態(tài)發(fā)生變化時(shí),通過(guò)Notify成員函數(shù)通知所有的觀察者,告訴他們博客的狀態(tài)更新了。而觀察者通過(guò)Update成員函數(shù)獲取博客的狀態(tài)信息。代碼實(shí)現(xiàn)不難,下面給出C++的一種實(shí)現(xiàn)。
//觀察者
class Observer
{
public: Observer() {} virtual ~Observer() {} virtual void Update() {}
};
//博客
class Blog
{
public: Blog() {} virtual ~Blog() {} void Attach(Observer *observer) { m_observers.push_back(observer); } //添加觀察者 void Remove(Observer *observer) { m_observers.remove(observer); } //移除觀察者 void Notify() //通知觀察者 { list<Observer*>::iterator iter = m_observers.begin(); for(; iter != m_observers.end(); iter++) (*iter)->Update(); } virtual void SetStatus(string s) { m_status = s; } //設(shè)置狀態(tài) virtual string GetStatus() { return m_status; } //獲得狀態(tài)
private: list<Observer* > m_observers; //觀察者鏈表
protected: string m_status; //狀態(tài)
};
以上是觀察者和博客的基類,定義了通用接口。博客類主要完成觀察者的添加、移除、通知操作,設(shè)置和獲得狀態(tài)僅僅是一個(gè)默認(rèn)實(shí)現(xiàn)。下面給出它們相應(yīng)的子類實(shí)現(xiàn)。
//具體博客類
class BlogCSDN : public Blog
{
private: string m_name; //博主名稱
public: BlogCSDN(string name): m_name(name) {} ~BlogCSDN() {} void SetStatus(string s) { m_status = "CSDN通知 : " + m_name + s; } //具體設(shè)置狀態(tài)信息 string GetStatus() { return m_status; }
};
//具體觀察者
class ObserverBlog : public Observer
{
private: string m_name; //觀察者名稱 Blog *m_blog; //觀察的博客,當(dāng)然以鏈表形式更好,就可以觀察多個(gè)博客
public: ObserverBlog(string name,Blog *blog): m_name(name), m_blog(blog) {} ~ObserverBlog() {} void Update() //獲得更新?tīng)顟B(tài) { string status = m_blog->GetStatus(); cout<<m_name<<"-------"<<status<<endl; }
};
客戶的使用方式:
//測(cè)試案例
int main()
{ Blog *blog = new BlogCSDN("wuzhekai1985"); Observer *observer1 = new ObserverBlog("tutupig", blog); blog->Attach(observer1); blog->SetStatus("發(fā)表設(shè)計(jì)模式C++實(shí)現(xiàn)(15)——觀察者模式"); blog->Notify(); delete blog; delete observer1; return 0;
}
16.狀態(tài)模式
狀態(tài)模式:允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為。對(duì)象看起來(lái)似乎修改了它的類。它有兩種使用情況:(1)一個(gè)對(duì)象的行為取決于它的狀態(tài), 并且它必須在運(yùn)行時(shí)刻根據(jù)狀態(tài)改變它的行為。(2)一個(gè)操作中含有龐大的多分支的條件語(yǔ)句,且這些分支依賴于該對(duì)象的狀態(tài)。本文的例子為第一種情況,以戰(zhàn)爭(zhēng)為例,假設(shè)一場(chǎng)戰(zhàn)爭(zhēng)需經(jīng)歷四個(gè)階段:前期、中期、后期、結(jié)束。當(dāng)戰(zhàn)爭(zhēng)處于不同的階段,戰(zhàn)爭(zhēng)的行為是不一樣的,也就說(shuō)戰(zhàn)爭(zhēng)的行為取決于所處的階段,而且隨著時(shí)間的推進(jìn)是動(dòng)態(tài)變化的。下面給出相應(yīng)的UML圖。
?實(shí)現(xiàn)的代碼比較簡(jiǎn)單,給出War類和State類,War類中含State對(duì)象(指針形式)。
class War;
class State
{
public: virtual void Prophase() {} virtual void Metaphase() {} virtual void Anaphase() {} virtual void End() {} virtual void CurrentState(War *war) {}
};
//戰(zhàn)爭(zhēng)
class War
{
private: State *m_state; //目前狀態(tài) int m_days; //戰(zhàn)爭(zhēng)持續(xù)時(shí)間
public: War(State *state): m_state(state), m_days(0) {} ~War() { delete m_state; } int GetDays() { return m_days; } void SetDays(int days) { m_days = days; } void SetState(State *state) { delete m_state; m_state = state; } void GetState() { m_state->CurrentState(this); }
};
給出具體的狀態(tài)類:
//戰(zhàn)爭(zhēng)結(jié)束
class EndState: public State
{
public: void End(War *war) //結(jié)束階段的具體行為 { cout<<"戰(zhàn)爭(zhēng)結(jié)束"<<endl; } void CurrentState(War *war) { End(war); }
};
//后期
class AnaphaseState: public State
{
public: void Anaphase(War *war) //后期的具體行為 { if(war->GetDays() < 30) cout<<"第"<<war->GetDays()<<"天:戰(zhàn)爭(zhēng)后期,雙方拼死一搏"<<endl; else { war->SetState(new EndState()); war->GetState(); } } void CurrentState(War *war) { Anaphase(war); }
};
//中期
class MetaphaseState: public State
{
public: void Metaphase(War *war) //中期的具體行為 { if(war->GetDays() < 20) cout<<"第"<<war->GetDays()<<"天:戰(zhàn)爭(zhēng)中期,進(jìn)入相持階段,雙發(fā)各有損耗"<<endl; else { war->SetState(new AnaphaseState()); war->GetState(); } } void CurrentState(War *war) { Metaphase(war); }
};
//前期
class ProphaseState: public State
{
public: void Prophase(War *war) //前期的具體行為 { if(war->GetDays() < 10) cout<<"第"<<war->GetDays()<<"天:戰(zhàn)爭(zhēng)初期,雙方你來(lái)我往,互相試探對(duì)方"<<endl; else { war->SetState(new MetaphaseState()); war->GetState(); } } void CurrentState(War *war) { Prophase(war); }
};
使用方式:
//測(cè)試案例
int main()
{ War *war = new War(new ProphaseState()); for(int i = 1; i < 40;i += 5) { war->SetDays(i); war->GetState(); } delete war; return 0;
}