0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

各類設(shè)計模式及實現(xiàn)

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-11 11:45 ? 次閱讀

軟件領(lǐng)域中的設(shè)計模式為開發(fā)人員提供了一種使用專家設(shè)計經(jīng)驗的有效途徑。設(shè)計模式中運用了面向?qū)ο?a href="http://srfitnesspt.com/v/tag/1315/" target="_blank">編程語言的重要特性:封裝、繼承、多態(tài),真正領(lǐng)悟設(shè)計模式的精髓是可能一個漫長的過程,需要大量實踐經(jīng)驗的積累。最近看設(shè)計模式的書,對于每個模式,用C++寫了個小例子,加深一下理解。

一、設(shè)計模式的分類

總體來說設(shè)計模式分為三大類

創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。

結(jié)構(gòu)型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。

行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責(zé)任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式。

其實還有兩類:并發(fā)型模式和線程池模式。

二、設(shè)計模式的六大原則

總原則:開閉原則(Open Close Principle)

開閉原則就是說對擴展開放,對修改關(guān)閉。在程序需要進行拓展的時候,不能去修改原有的代碼,而是要擴展原有代碼,實現(xiàn)一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展性好,易于維護和升級。想要達(dá)到這樣的效果,我們需要使用接口和抽象類等,后面的具體設(shè)計中我們會提到這點。

1、單一職責(zé)原則

不要存在多于一個導(dǎo)致類變更的原因,也就是說每個類應(yīng)該實現(xiàn)單一的職責(zé),如若不然,就應(yīng)該把類拆分。

2、里氏替換原則(Liskov Substitution Principle)

里氏代換原則(Liskov Substitution Principle LSP)面向?qū)ο笤O(shè)計的基本原則之一。里氏代換原則中說,任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。LSP是繼承復(fù)用的基石,只有當(dāng)衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被復(fù)用,而衍生類也能夠在基類的基礎(chǔ)上增加新的行為。里氏代換原則是對“開-閉”原則的補充。實現(xiàn)“開-閉”原則的關(guān)鍵步驟就是抽象化。而基類與子類的繼承關(guān)系就是抽象化的具體實現(xiàn),所以里氏代換原則是對實現(xiàn)抽象化的具體步驟的規(guī)范。

歷史替換原則中,子類對父類的方法盡量不要重寫和重載。因為父類代表了定義好的結(jié)構(gòu),通過這個規(guī)范的接口與外界交互,子類不應(yīng)該隨便破壞它。

3、依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)

這個是開閉原則的基礎(chǔ),具體內(nèi)容:面向接口編程,依賴于抽象而不依賴于具體。寫代碼時用到具體類時,不與具體類交互,而與具體類的上層接口交互。

4、接口隔離原則(Interface Segregation Principle)

這個原則的意思是:每個接口中不存在子類用不到卻必須實現(xiàn)的方法,如果不然,就要將接口拆分。使用多個隔離的接口,比使用單個接口(多個接口方法集合到一個的接口)要好。

5、迪米特法則(最少知道原則)(Demeter Principle)

就是說:一個類對自己依賴的類知道的越少越好。也就是說無論被依賴的類多么復(fù)雜,都應(yīng)該將邏輯封裝在方法的內(nèi)部,通過public方法提供給外部。這樣當(dāng)被依賴的類變化時,才能最小的影響該類。

最少知道原則的另一個表達(dá)方式是:只與直接的朋友通信。類之間只要有耦合關(guān)系,就叫朋友關(guān)系。耦合分為依賴、關(guān)聯(lián)、聚合、組合等。我們稱出現(xiàn)為成員變量、方法參數(shù)、方法返回值中的類為直接朋友。局部變量、臨時變量則不是直接的朋友。我們要求陌生的類不要作為局部變量出現(xiàn)在類中。

6、合成復(fù)用原則(Composite Reuse Principle)

原則是盡量首先使用合成/聚合的方式,而不是使用繼承。

1.工廠模式

工廠模式屬于創(chuàng)建型模式,大致可以分為三類,簡單工廠模式、工廠方法模式、抽象工廠模式。聽上去差不多,都是工廠模式。下面一個個介紹,首先介紹簡單工廠模式,它的主要特點是需要在工廠類中做判斷,從而創(chuàng)造相應(yīng)的產(chǎn)品。當(dāng)增加新的產(chǎn)品時,就需要修改工廠類。有點抽象,舉個例子就明白了。有一家生產(chǎn)處理器核的廠家,它只有一個工廠,能夠生產(chǎn)兩種型號的處理器核??蛻粜枰裁礃拥奶幚砥骱?,一定要顯示地告訴生產(chǎn)工廠。下面給出一種實現(xiàn)方案。

enum CTYPE {COREA, COREB};     
class SingleCore    
{    
public:    
    virtual void Show() = 0;  
};    
//單核A    
class SingleCoreA: public SingleCore    
{    
public:    
    void Show() { cout< "SingleCore A"<

這樣設(shè)計的主要缺點之前也提到過,就是要增加新的核類型時,就需要修改工廠類。這就違反了開放封閉原則:軟件實體(類、模塊、函數(shù))可以擴展,但是不可修改。于是,工廠方法模式出現(xiàn)了。所謂工廠方法模式,是指定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類。Factory Method使一個類的實例化延遲到其子類。

聽起來很抽象,還是以剛才的例子解釋。這家生產(chǎn)處理器核的產(chǎn)家賺了不少錢,于是決定再開設(shè)一個工廠專門用來生產(chǎn)B型號的單核,而原來的工廠專門用來生產(chǎn)A型號的單核。這時,客戶要做的是找好工廠,比如要A型號的核,就找A工廠要;否則找B工廠要,不再需要告訴工廠具體要什么型號的處理器核了。下面給出一個實現(xiàn)方案。

class SingleCore    
{    
public:    
    virtual void Show() = 0;  
};    
//單核A    
class SingleCoreA: public SingleCore    
{    
public:    
    void Show() { cout< "SingleCore A"<

工廠方法模式也有缺點,每增加一種產(chǎn)品,就需要增加一個對象的工廠。如果這家公司發(fā)展迅速,推出了很多新的處理器核,那么就要開設(shè)相應(yīng)的新工廠。在C++實現(xiàn)中,就是要定義一個個的工廠類。顯然,相比簡單工廠模式,工廠方法模式需要更多的類定義。

既然有了簡單工廠模式和工廠方法模式,為什么還要有抽象工廠模式呢?它到底有什么作用呢?還是舉這個例子,這家公司的技術(shù)不斷進步,不僅可以生產(chǎn)單核處理器,也能生產(chǎn)多核處理器?,F(xiàn)在簡單工廠模式和工廠方法模式都鞭長莫及。抽象工廠模式登場了。它的定義為提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無需指定它們具體的類。具體這樣應(yīng)用,這家公司還是開設(shè)兩個工廠,一個專門用來生產(chǎn)A型號的單核多核處理器,而另一個工廠專門用來生產(chǎn)B型號的單核多核處理器,下面給出實現(xiàn)的代碼。

//單核    
class SingleCore     
{    
public:    
    virtual void Show() = 0;  
};    
class SingleCoreA: public SingleCore      
{    
public:    
    void Show() { cout< "Single Core A"<

至此,工廠模式介紹完了。給出三種工廠模式的UML圖,加深印象。

簡單工廠模式的UML圖:

圖片

工廠方法的UML圖:

圖片

抽象工廠模式的UML圖:

圖片

2.策略模式

策略模式是指定義一系列的算法,把它們一個個封裝起來,并且使它們可相互替換。本模式使得算法可獨立于使用它的客戶而變化。也就是說這些算法所完成的功能一樣,對外的接口一樣,只是各自實現(xiàn)上存在差異。用策略模式來封裝算法,效果比較好。下面以高速緩存(Cache)的替換算法為例,實現(xiàn)策略模式。

什么是Cache的替換算法呢?簡單解釋一下, 當(dāng)發(fā)生Cache缺失時,Cache控制器必須選擇Cache中的一行,并用欲獲得的數(shù)據(jù)來替換它。所采用的選擇策略就是Cache的替換算法。下面給出相應(yīng)的UML圖。

圖片

ReplaceAlgorithm是一個抽象類,定義了算法的接口,有三個類繼承自這個抽象類,也就是具體的算法實現(xiàn)。Cache類中需要使用替換算法,因此維護了一個 ReplaceAlgorithm的對象。這個UML圖的結(jié)構(gòu)就是策略模式的典型結(jié)構(gòu)。下面根據(jù)UML圖,給出相應(yīng)的實現(xiàn)。

首先給出替換算法的定義。

//抽象接口  
class ReplaceAlgorithm  
{  
public:  
    virtual void Replace() = 0;  
};  
//三種具體的替換算法  
class LRU_ReplaceAlgorithm : public ReplaceAlgorithm  
{  
public:  
    void Replace() { cout< "Least Recently Used replace algorithm"<

接著給出Cache的定義,這里很關(guān)鍵,Cache的實現(xiàn)方式直接影響了客戶的使用方式,其關(guān)鍵在于如何指定替換算法。

方式一:直接通過參數(shù)指定,傳入一個特定算法的指針。

//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;  
}

方式二:也是直接通過參數(shù)指定,只不過不是傳入指針,而是一個標(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(); }  
};

相比方式一,這種方式用起來方便多了。其實這種方式將簡單工廠模式與策略模式結(jié)合在一起,算法的定義使用了策略模式,而Cache的定義其實使用了簡單工廠模式。

int main()  
{  
    Cache cache(LRU); //指定標(biāo)簽即可  
    cache.Replace();  
    return 0;  
}

上面兩種方式,構(gòu)造函數(shù)都需要形參。構(gòu)造函數(shù)是否可以不用參數(shù)呢?下面給出第三種實現(xiàn)方式。

方式三:利用模板實現(xiàn)。算法通過模板的實參指定。當(dāng)然了,還是使用了參數(shù),只不過不是構(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; //模板實參  
    cache.Replace();  
    return 0;  
}

3.適配器模式

DP上的定義:適配器模式將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。它包括類適配器和對象適配器,本文針對的是對象適配器。舉個例子,在STL中就用到了適配器模式。STL實現(xiàn)了一種數(shù)據(jù)結(jié)構(gòu),稱為雙端隊列(deque),支持前后兩段的插入與刪除。STL實現(xiàn)棧和隊列時,沒有從頭開始定義它們,而是直接使用雙端隊列實現(xiàn)的。這里雙端隊列就扮演了適配器的角色。隊列用到了它的后端插入,前端刪除。而棧用到了它的后端插入,后端刪除。假設(shè)棧和隊列都是一種順序容器,有兩種操作:壓入和彈出。下面給出相應(yīng)的UML圖,與DP上的圖差不多。

圖片

根據(jù)上面的UML圖,很容易給出實現(xiàn)。

//雙端隊列  
class Deque  
{  
public:  
    void push_back(int x) { cout< "Deque push_back"<

使用方式如下:

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.單例模式

單例的一般實現(xiàn)比較簡單,下面是代碼和UML圖。由于構(gòu)造函數(shù)是私有的,因此無法通過構(gòu)造函數(shù)實例化,唯一的方法就是通過調(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;  
}

這里只有一個類,如何實現(xiàn)Singleton類的子類呢?也就說Singleton有很多子類,在一種應(yīng)用中,只選擇其中的一個。最容易就是在GetInstance函數(shù)中做判斷,比如可以傳遞一個字符串,根據(jù)字符串的內(nèi)容創(chuàng)建相應(yīng)的子類實例。這也是DP書上的一種解法,書上給的代碼不全。這里重新實現(xiàn)了一下,發(fā)現(xiàn)不是想象中的那么簡單,最后實現(xiàn)的版本看上去很怪異。在VS2008下測試通過。

//Singleton.h  
#pragma once  
#include < iostream >  
using namespace std;  

class Singleton    
{  
public:  
    static Singleton* GetInstance(const char* name);  
    virtual void Show() {}  
protected: //必須為保護,如果是私有屬性,子類無法訪問父類的構(gòu)造函數(shù)  
    Singleton() {}  
private:  
    static Singleton *singleton; //唯一實例的指針  
};  

//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; //必須為友元類,否則父類無法訪問子類的構(gòu)造函數(shù)  
public:  
    void Show() { cout< "SingletonA"<
#include "Singleton.h"  
int main()  
{  
    Singleton *st = Singleton::GetInstance("SingletonA");  
    st- >Show();  
    return 0;  
}

上面代碼有一個地方很詭異,父類為子類的友元,如果不是友元,函數(shù)GetInstance會報錯,意思就是無法調(diào)用SingletonA和SIngletonB的構(gòu)造函數(shù)。父類中調(diào)用子類的構(gòu)造函數(shù),我還是第一次碰到。當(dāng)然了把SingletonA和SIngletonB的屬性設(shè)為public,GetInstance函數(shù)就不會報錯了,但是這樣外界就可以定義這些類的對象,違反了單例模式。

看似奇怪,其實也容易解釋。在父類中構(gòu)建子類的對象,相當(dāng)于是外界調(diào)用子類的構(gòu)造函數(shù),因此當(dāng)子類構(gòu)造函數(shù)的屬性為私有或保護時,父類無法訪問。為共有時,外界就可以訪問子類的構(gòu)造函數(shù)了,此時父類當(dāng)然也能訪問了。只不過為了保證單例模式,所以子類的構(gòu)造函數(shù)不能為共有,但是又希望在父類中構(gòu)造子類的對象,即需要調(diào)用子類的構(gòu)造函數(shù),這里沒有辦法才出此下策:將父類聲明為子類的友元類。

5.原型模式、模板方法模式

DP書上的定義為:用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。其中有一個詞很重要,那就是拷貝。可以說,拷貝是原型模式的精髓所在。舉個現(xiàn)實中的例子來介紹原型模式。找工作的時候,我們需要準(zhǔn)備簡歷。假設(shè)沒有打印設(shè)備,因此需手寫簡歷,這些簡歷的內(nèi)容都是一樣的。這樣有個缺陷,如果要修改簡歷中的某項,那么所有已寫好的簡歷都要修改,工作量很大。隨著科技的進步,出現(xiàn)了打印設(shè)備。我們只需手寫一份,然后利用打印設(shè)備復(fù)印多份即可。如果要修改簡歷中的某項,那么修改原始的版本就可以了,然后再復(fù)印。原始的那份手寫稿相當(dāng)于是一個原型,有了它,就可以通過復(fù)印(拷貝)創(chuàng)造出更多的新簡歷。這就是原型模式的基本思想。下面給出原型模式的UML圖,以剛才那個例子為實例。

圖片

原型模式實現(xiàn)的關(guān)鍵就是實現(xiàn)Clone函數(shù),對于C++來說,其實就是拷貝構(gòu)造函數(shù),需實現(xiàn)深拷貝,下面給出一種實現(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] = '?';   
    }  
    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 : "<

這里只給出了ResumeA的實現(xiàn),ResumeB的實現(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;  
    //深拷貝所以對r3,r4無影響  
    r3- >Show(); r4- >Show();  
    delete r3; delete r4;  
    r3 = r4 = NULL;  
}

最近有個招聘會,可以帶上簡歷去應(yīng)聘了。但是,其中有一家公司不接受簡歷,而是給應(yīng)聘者發(fā)了一張簡歷表,上面有基本信息、教育背景、工作經(jīng)歷等欄,讓應(yīng)聘者按照要求填寫完整。每個人拿到這份表格后,就開始填寫。如果用程序?qū)崿F(xiàn)這個過程,該如何做呢?一種方案就是用模板方法模式:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。我們的例子中,操作就是填寫簡歷這一過程,我們可以在父類中定義操作的算法骨架,而具體的實現(xiàn)由子類完成。下面給出它的UML圖。

圖片

其中FillResume() 定義了操作的骨架,依次調(diào)用子類實現(xiàn)的函數(shù)。相當(dāng)于每個人填寫簡歷的實際過程。接著給出相應(yīng)的C++代碼。

//簡歷  
class Resume  
{  
protected: //保護成員  
    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"<

使用方式如下:

int main()  
{  
    Resume *r1;  
    r1 = new ResumeA();  
    r1- >FillResume();  
    delete r1;  
    r1 = new ResumeB();  
    r1- >FillResume();  
    delete r1;  
    r1 = NULL;  
    return 0;  
}

6.建造者模式

建造者模式的定義將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示(DP)?!洞笤捲O(shè)計模式》舉了一個很好的例子——建造小人,一共需建造6個部分,頭部、身體、左右手、左右腳。與工廠模式不同,建造者模式是在導(dǎo)向者的控制下一步一步構(gòu)造產(chǎn)品的。建造小人就是在控制下一步步構(gòu)造出來的。創(chuàng)建者模式可以能更精細(xì)的控制構(gòu)建過程,從而能更精細(xì)的控制所得產(chǎn)品的內(nèi)部結(jié)構(gòu)。下面給出建造者模式的UML圖,以建造小人為實例。

圖片

對于客戶來說,只需知道導(dǎo)向者就可以了,通過導(dǎo)向者,客戶就能構(gòu)造復(fù)雜的對象,而不需要知道具體的構(gòu)造過程。下面給出小人例子的代碼實現(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"<

客戶的使用方式:

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)一個系統(tǒng)很復(fù)雜時,系統(tǒng)提供給客戶的是一個簡單的對外接口,而把里面復(fù)雜的結(jié)構(gòu)都封裝了起來。客戶只需使用這些簡單接口就能使用這個系統(tǒng),而不需要關(guān)注內(nèi)部復(fù)雜的結(jié)構(gòu)。DP一書的定義:為子系統(tǒng)中的一組接口提供一個一致的界面, 外觀模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。舉個編譯器的例子,假設(shè)編譯一個程序需要經(jīng)過四個步驟:詞法分析、語法分析、中間代碼生成、機器碼生成。學(xué)過編譯都知道,每一步都很復(fù)雜。對于編譯器這個系統(tǒng),就可以使用外觀模式。可以定義一個高層接口,比如名為Compiler的類,里面有一個名為Run的函數(shù)??蛻糁恍枵{(diào)用這個函數(shù)就可以編譯程序,至于Run函數(shù)內(nèi)部的具體操作,客戶無需知道。下面給出UML圖,以編譯器為實例。

圖片

相應(yīng)的代碼實現(xiàn)為:

class Scanner  
{  
public:  
    void Scan() { cout< "詞法分析"<

客戶使用方式:

int main()  
{  
    Compiler compiler;  
    Console Compiler();  
    return 0;  
}

這就是外觀模式,它有幾個特點(摘自DP一書),(1)它對客戶屏蔽子系統(tǒng)組件,因而減少了客戶處理的對象的數(shù)目并使得子系統(tǒng)使用起來更加方便。(2)它實現(xiàn)了子系統(tǒng)與客戶之間的松耦合關(guān)系,而子系統(tǒng)內(nèi)部的功能組件往往是緊耦合的。(3)如果應(yīng)用需要,它并不限制它們使用子系統(tǒng)類。

結(jié)合上面編譯器這個例子,進一步說明。對于(1),編譯器類對客戶屏蔽了子系統(tǒng)組件,客戶只需處理編譯器的對象就可以方便的使用子系統(tǒng)。對于(2),子系統(tǒng)的變化,不會影響到客戶的使用,體現(xiàn)了子系統(tǒng)與客戶的松耦合關(guān)系。對于(3),如果客戶希望使用詞法分析器,只需定義詞法分析的類對象即可,并不受到限制。

外觀模式在構(gòu)建大型系統(tǒng)時非常有用。接下來介紹另一種模式,稱為組合模式。感覺有點像外觀模式,剛才我們實現(xiàn)外觀模式時,在Compiler這個類中包含了多個類的對象,就像把這些類組合在了一起。組合模式是不是這個意思,有點相似,其實不然。

DP書上給出的定義:將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。組合使得用戶對單個對象和組合對象的使用具有一致性。注意兩個字“樹形”。這種樹形結(jié)構(gòu)在現(xiàn)實生活中隨處可見,比如一個集團公司,它有一個母公司,下設(shè)很多家子公司。不管是母公司還是子公司,都有各自直屬的財務(wù)部、人力資源部、銷售部等。對于母公司來說,不論是子公司,還是直屬的財務(wù)部、人力資源部,都是它的部門。整個公司的部門拓?fù)鋱D就是一個樹形結(jié)構(gòu)。

下面給出組合模式的UML圖。從圖中可以看到,F(xiàn)inanceDepartment、HRDepartment兩個類作為葉結(jié)點,因此沒有定義添加函數(shù)。而ConcreteCompany類可以作為中間結(jié)點,所以可以有添加函數(shù)。那么怎么添加呢?這個類中定義了一個鏈表,用來放添加的元素。

圖片

相應(yīng)的代碼實現(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); } //位于樹的中間,可以增加子樹  
    void Show(int depth)  
{  
        for(int i = 0;i < depth; i++)  
            cout< "-";  
        cout<

客戶使用方式:

int main()  
{  
    Company *root = new ConcreteCompany("總公司");  
    Company *leaf1=new FinanceDepartment("財務(wù)部");  
    Company *leaf2=new HRDepartment("人力資源部");  
    root- >Add(leaf1);  
    root- >Add(leaf2);  

    //分公司A  
    Company *mid1 = new ConcreteCompany("分公司A");  
    Company *leaf3=new FinanceDepartment("財務(wù)部");  
    Company *leaf4=new HRDepartment("人力資源部");  
    mid1- >Add(leaf3);  
    mid1- >Add(leaf4);  
    root- >Add(mid1);  
    //分公司B  
    Company *mid2=new ConcreteCompany("分公司B");  
    FinanceDepartment *leaf5=new FinanceDepartment("財務(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;  
}

上面的實現(xiàn)方式有缺點,就是內(nèi)存的釋放不好,需要客戶自己動手,非常不方便。有待改進,比較好的做法是讓ConcreteCompany類來釋放。因為所有的指針都是存在ConcreteCompany類的鏈表中。C++的麻煩,沒有垃圾回收機制。

8.代理模式

[DP]上的定義:為其他對象提供一種代理以控制對這個對象的訪問。有四種常用的情況:(1)遠(yuǎn)程代理,(2)虛代理,(3)保護代理,(4)智能引用。本文主要介紹虛代理和智能引用兩種情況。

考慮一個可以在文檔中嵌入圖形對象的文檔編輯器。有些圖形對象的創(chuàng)建開銷很大。但是打開文檔必須很迅速,因此我們在打開文檔時應(yīng)避免一次性創(chuàng)建所有開銷很大的對象。這里就可以運用代理模式,在打開文檔時,并不打開圖形對象,而是打開圖形對象的代理以替代真實的圖形。待到真正需要打開圖形時,仍由代理負(fù)責(zé)打開。這是[DP]一書上的給的例子。下面給出代理模式的UML圖。

圖片

簡單實現(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 : "<

客戶調(diào)用:

int main()  
{  
    Image *image = new BigImageProxy("proxy.jpg"); //代理  
    image- >Show(); //需要時由代理負(fù)責(zé)打開  
    delete image;  
    return 0;  
}

在這個例子屬于虛代理的情況,下面給兩個智能引用的例子。一個是C++中的auto_ptr,另一個是smart_ptr。自己實現(xiàn)了一下。先給出auto_ptr的代碼實現(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 類就是一個代理,客戶只需操作auto_prt的對象,而不需要與被代理的指針pointee打交道。auto_ptr 的好處在于為動態(tài)分配的對象提供異常安全。因為它用一個對象存儲需要被自動釋放的資源,然后依靠對象的析構(gòu)函數(shù)來釋放資源。這樣客戶就不需要關(guān)注資源的釋放,由auto_ptr 對象自動完成。實現(xiàn)中的一個關(guān)鍵就是重載了解引用操作符和箭頭操作符,從而使得auto_ptr的使用與真實指針類似。

我們知道C++中沒有垃圾回收機制,可以通過智能指針來彌補,下面給出智能指針的一種實現(xiàn),采用了引用計數(shù)的策略。

template < typename T >  
class smart_ptr  
{  
public:  
    smart_ptr(T *p = 0): pointee(p), count(new size_t(1)) { }  //初始的計數(shù)值為1  
    smart_ptr(const smart_ptr &rhs): pointee(rhs.pointee), count(rhs.count) { ++*count; } //拷貝構(gòu)造函數(shù),計數(shù)加1  
    ~smart_ptr() { decr_count(); }              //析構(gòu),計數(shù)減1,減到0時進行垃圾回收,即釋放空間  
    smart_ptr& operator= (const smart_ptr& rhs) //重載賦值操作符  
    {  
        //給自身賦值也對,因為如果自身賦值,計數(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; } //獲得引用計數(shù)器值  
private:   
    T *pointee;       //實際指針,被代理    
    size_t *count;    //引用計數(shù)器  
    void decr_count() //計數(shù)器減1  
{  
        if(--*count == 0)   
        {  
            delete pointee;  
            delete count;  
        }  
    }  
};

9.享元模式

舉個圍棋的例子,圍棋的棋盤共有361格,即可放361個棋子?,F(xiàn)在要實現(xiàn)一個圍棋程序,該怎么辦呢?首先要考慮的是棋子棋盤的實現(xiàn),可以定義一個棋子的類,成員變量包括棋子的顏色、形狀、位置等信息,另外再定義一個棋盤的類,成員變量中有個容器,用于存放棋子的對象。下面給出代碼表示:

棋子的定義,當(dāng)然棋子的屬性除了顏色和位置,還有其他的,這里略去。這兩個屬性足以說明問題。

//棋子顏色  
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< "繪制一顆黑棋"<

棋盤的定義:

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<

客戶的使用方式如下:

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),棋盤的容器中存放了已下的棋子,而每個棋子包含棋子的所有屬性。一盤棋往往需要含上百顆棋子,采用上面這種實現(xiàn),占用的空間太大了。如何改進呢?用享元模式。其定義為:運用共享技術(shù)有效地支持大量細(xì)粒度的對象。

在圍棋中,棋子就是大量細(xì)粒度的對象。其屬性有內(nèi)在的,比如顏色、形狀等,也有外在的,比如在棋盤上的位置。內(nèi)在的屬性是可以共享的,區(qū)分在于外在屬性。因此,可以這樣設(shè)計,只需定義兩個棋子的對象,一顆黑棋和一顆白棋,這兩個對象含棋子的內(nèi)在屬性;棋子的外在屬性,即在棋盤上的位置可以提取出來,存放在單獨的容器中。相比之前的方案,現(xiàn)在容器中僅僅存放了位置屬性,而原來則是棋子對象。顯然,現(xiàn)在的方案大大減少了對于空間的需求。

關(guān)注PieceBoard 的容器,之前是vector m_vecPiece,現(xiàn)在是vector 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<

客戶的使用方式一樣,這里不重復(fù)給出,現(xiàn)在給出享元模式的UML圖,以圍棋為例。棋盤中含兩個共享的對象,黑棋子和白棋子,所有棋子的外在屬性都存放在單獨的容器中。

圖片

10、橋接模式

[DP]書上定義:將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化??紤]裝操作系統(tǒng),有多種配置的計算機,同樣也有多款操作系統(tǒng)。如何運用橋接模式呢?可以將操作系統(tǒng)和計算機分別抽象出來,讓它們各自發(fā)展,減少它們的耦合度。當(dāng)然了,兩者之間有標(biāo)準(zhǔn)的接口。這樣設(shè)計,不論是對于計算機,還是操作系統(tǒng)都是非常有利的。下面給出這種設(shè)計的UML圖,其實就是橋接模式的UML圖。

圖片

給出C++的一種實現(xiàn):

//操作系統(tǒng)  
class OS  
{  
public:  
    virtual void InstallOS_Imp() {}  
};  
class WindowOS: public OS  
{  
public:  
    void InstallOS_Imp() { cout< "安裝Window操作系統(tǒng)"<

客戶使用方式:

int main()  
{  
    OS *os1 = new WindowOS();  
    OS *os2 = new LinuxOS();  
    Computer *computer1 = new AppleComputer();  
    computer1- >InstallOS(os1);  
    computer1- >InstallOS(os2);  
}

11.裝飾模式

裝飾模式:動態(tài)地給一個對象添加一些額外的職責(zé)。就增加功能來說,裝飾模式相比生成子類更為靈活。有時我們希望給某個對象而不是整個類添加一些功能。比如有一個手機,允許你為手機添加特性,比如增加掛件、屏幕貼膜等。一種靈活的設(shè)計方式是,將手機嵌入到另一對象中,由這個對象完成特性的添加,我們稱這個嵌入的對象為裝飾。這個裝飾與它所裝飾的組件接口一致,因此它對使用該組件的客戶透明。下面給出裝飾模式的UML圖。

圖片

在這種設(shè)計中,手機的裝飾功能被獨立出來,可以單獨發(fā)展,進而簡化了具體手機類的設(shè)計。下面給出Phone類的實現(xiàn):

//公共抽象類  
class Phone  
{  
public:  
    Phone() {}  
    virtual ~Phone() {}  
    virtual void ShowDecorate() {}  
};

具體的手機類的定義:

//具體的手機類  
class iPhone : public Phone  
{  
private:  
    string m_name; //手機名稱  
public:  
    iPhone(string name): m_name(name){}  
    ~iPhone() {}  
    void ShowDecorate() { cout<

裝飾類的實現(xiàn):

//裝飾類  
class DecoratorPhone : public Phone  
{  
private:  
    Phone *m_phone;  //要裝飾的手機  
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< "增加掛件"<

客戶使用方式:

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é)的方式??梢杂锰砑雍头蛛x的方法,用裝飾在運行時刻增加和刪除職責(zé)。裝飾模式提供了一種“即用即付”的方法來添加職責(zé)。它并不試圖在一個復(fù)雜的可定制的類中支持所有可預(yù)見的特征,相反,你可以定義一個簡單的類,并且用裝飾類給它逐漸地添加功能??梢詮暮唵蔚牟考M合出復(fù)雜的功能

12.備忘錄模式

備忘錄模式:在不破壞封裝性的前提下,捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài)。這樣以后就可將該對象恢復(fù)到原先保存的狀態(tài)[DP]。舉個簡單的例子,我們玩游戲時都會保存進度,所保存的進度以文件的形式存在。這樣下次就可以繼續(xù)玩,而不用從頭開始。這里的進度其實就是游戲的內(nèi)部狀態(tài),而這里的文件相當(dāng)于是在游戲之外保存狀態(tài)。這樣,下次就可以從文件中讀入保存的進度,從而恢復(fù)到原來的狀態(tài)。這就是備忘錄模式。

給出備忘錄模式的UML圖,以保存游戲的進度為例。

圖片

Memento類定義了內(nèi)部的狀態(tài),而Caretake類是一個保存進度的管理者,GameRole類是游戲角色類??梢钥吹紾ameRole的對象依賴于Memento對象,而與Caretake對象無關(guān)。下面給出一個簡單的是實現(xiàn)。

//需保存的信息  
class Memento    
{  
public:  
    int m_vitality; //生命值  
    int m_attack;   //進攻值  
    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()  //保存進度,只與Memento對象交互,并不牽涉到Caretake  
{   
        Memento memento(m_vitality, m_attack, m_defense);  
        return memento;  
    }  
    void Load(Memento memento)  //載入進度,只與Memento對象交互,并不牽涉到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<

客戶使用方式:

//測試案例  
int main()  
{     
    Caretake caretake;  
    GameRole role;   
    role.Show();   //初始值  
    caretake.Save(role.Save()); //保存狀態(tài)  
    role.Attack();     
    role.Show();  //進攻后  
    role.Load(caretake.Load(0)); //載入狀態(tài)   
    role.Show();  //恢復(fù)到狀態(tài)0  
    return 0;  
}

13.中介者模式

中介者模式:用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。中介者模式的例子很多,大到聯(lián)合國安理會,小到房屋中介,都扮演了中間者的角色,協(xié)調(diào)各方利益。

本文就以租房為例子,如果沒有房屋中介,那么房客要自己找房東,而房東也要自己找房客,非常不方便。有了房屋中介機構(gòu)就方便了,房東可以把要出租的房屋信息放到中介機構(gòu),而房客可以去中介機構(gòu)咨詢。在軟件中,就是多個對象之間需要通信,如果沒有中介,對象就需要知道其他對象,最壞情況下,可能需要知道所有其他對象,而有了中介對象就方便多了,對象只需與中介對象通信,而不用知道其他的對象。這就是中介者模式,下面以租房為例,給出中介者模式的UML圖。

圖片

實現(xiàn)不難,下面給出C++的實現(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) {}     //從中介獲取信息  
};  
//抽象中介機構(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< "租房者收到信息"<

客戶使用方式如下:

//測試案例  
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("我想在南京路附近租套房子,價格800元一個月n");  
    person2- >SendMessage("出租房子:南京路100號,70平米,1000元一個月n");  
    delete person1; delete person2; delete mediator;  
    return 0;  
}

14.職責(zé)鏈模式

職責(zé)鏈模式:使多個對象都有機會處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止。其思想很簡單,考慮員工要求加薪。公司的管理者一共有三級,總經(jīng)理、總監(jiān)、經(jīng)理,如果一個員工要求加薪,應(yīng)該向主管的經(jīng)理申請,如果加薪的數(shù)量在經(jīng)理的職權(quán)內(nèi),那么經(jīng)理可以直接批準(zhǔn),否則將申請上交給總監(jiān)。總監(jiān)的處理方式也一樣,總經(jīng)理可以處理所有請求。這就是典型的職責(zé)鏈模式,請求的處理形成了一條鏈,直到有一個對象處理請求。給出這個例子的UML圖。

圖片

代碼的實現(xià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)理"<

客戶調(diào)用方式為:

//測試案例  
int main()  
{     
    Manager *general = new GeneralManager(NULL, "A"); //設(shè)置上級,總經(jīng)理沒有上級  
    Manager *majordomo = new Majordomo(general, "B"); //設(shè)置上級  
    Manager *common = new CommonManager(majordomo, "C"); //設(shè)置上級  
    common- >DealWithRequest("D",300);   //員工D要求加薪  
    common- >DealWithRequest("E", 600);  
    common- >DealWithRequest("F", 1000);  
    delete common; delete majordomo; delete general;  
    return 0;  
}

15.觀察者模式

觀察者模式:定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。它還有兩個別名,依賴(Dependents),發(fā)布-訂閱(Publish-Subsrcibe)??梢耘e個博客訂閱的例子,當(dāng)博主發(fā)表新文章的時候,即博主狀態(tài)發(fā)生了改變,那些訂閱的讀者就會收到通知,然后進行相應(yīng)的動作,比如去看文章,或者收藏起來。博主與讀者之間存在種一對多的依賴關(guān)系。下面給出相應(yīng)的UML圖設(shè)計。

圖片

可以看到博客類中有一個觀察者鏈表(即訂閱者),當(dāng)博客的狀態(tài)發(fā)生變化時,通過Notify成員函數(shù)通知所有的觀察者,告訴他們博客的狀態(tài)更新了。而觀察者通過Update成員函數(shù)獲取博客的狀態(tài)信息。代碼實現(xiàn)不難,下面給出C++的一種實現(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)僅僅是一個默認(rèn)實現(xiàn)。下面給出它們相應(yīng)的子類實現(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)然以鏈表形式更好,就可以觀察多個博客  
public:   
    ObserverBlog(string name,Blog *blog): m_name(name), m_blog(blog) {}  
    ~ObserverBlog() {}  
    void Update()  //獲得更新狀態(tài)  
{   
        string status = m_blog- >GetStatus();  
        cout<

客戶的使用方式:

//測試案例  
int main()  
{  
    Blog *blog = new BlogCSDN("wuzhekai1985");  
    Observer *observer1 = new ObserverBlog("tutupig", blog);  
    blog- >Attach(observer1);  
    blog- >SetStatus("發(fā)表設(shè)計模式C++實現(xiàn)(15)——觀察者模式");  
    blog- >Notify();  
    delete blog; delete observer1;  
    return 0;  
}

16.狀態(tài)模式

狀態(tài)模式:允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為。對象看起來似乎修改了它的類。它有兩種使用情況:(1)一個對象的行為取決于它的狀態(tài), 并且它必須在運行時刻根據(jù)狀態(tài)改變它的行為。(2)一個操作中含有龐大的多分支的條件語句,且這些分支依賴于該對象的狀態(tài)。本文的例子為第一種情況,以戰(zhàn)爭為例,假設(shè)一場戰(zhàn)爭需經(jīng)歷四個階段:前期、中期、后期、結(jié)束。當(dāng)戰(zhàn)爭處于不同的階段,戰(zhàn)爭的行為是不一樣的,也就說戰(zhàn)爭的行為取決于所處的階段,而且隨著時間的推進是動態(tài)變化的。下面給出相應(yīng)的UML圖。

圖片

實現(xiàn)的代碼比較簡單,給出War類和State類,War類中含State對象(指針形式)。

class War;  
class State   
{  
public:  
    virtual void Prophase() {}  
    virtual void Metaphase() {}  
    virtual void Anaphase() {}  
    virtual void End() {}  
    virtual void CurrentState(War *war) {}  
};  
//戰(zhàn)爭  
class War  
{  
private:  
    State *m_state;  //目前狀態(tài)  
    int m_days;      //戰(zhàn)爭持續(xù)時間  
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)爭結(jié)束  
class EndState: public State  
{  
public:  
    void End(War *war) //結(jié)束階段的具體行為  
{  
        cout< "戰(zhàn)爭結(jié)束"<

使用方式:

//測試案例  
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;  
}
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 適配器
    +關(guān)注

    關(guān)注

    8

    文章

    1900

    瀏覽量

    67769
  • 軟件
    +關(guān)注

    關(guān)注

    69

    文章

    4613

    瀏覽量

    86991
  • 編程語言
    +關(guān)注

    關(guān)注

    10

    文章

    1921

    瀏覽量

    34502
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4697

    瀏覽量

    68083
  • 設(shè)計模式
    +關(guān)注

    關(guān)注

    0

    文章

    53

    瀏覽量

    8618
收藏 人收藏

    評論

    相關(guān)推薦

    分類細(xì)敘各類電子元器件的失效模式與機理

    所以掌握各類電子元器件的實效機理與特性是硬件工程師比不可少的知識。下面分類細(xì)敘一下各類電子元器件的失效模式與機理。
    的頭像 發(fā)表于 02-01 10:32 ?1601次閱讀

    怎么通過五種運動檢測模式實現(xiàn)應(yīng)用產(chǎn)品的變革?

    通過審視五種運動檢測模式——加速度(包括平移運動,如位置和方向)、振動、沖擊、傾斜和旋轉(zhuǎn),各自的可能性,可以超越當(dāng)今大量MEMS應(yīng)用的范圍,極大地擴大應(yīng)用選項。MEMS加速度計和陀螺儀如何通過五類運動檢測實現(xiàn)各類最終產(chǎn)品的變革?
    發(fā)表于 07-30 07:47

    GPIO模式與GPIO配置代碼實現(xiàn)

    寫在前面:本文章旨在總結(jié)備份、方便以后查詢,由于是個人總結(jié),如有不對,歡迎指正;另外,內(nèi)容大部分來自網(wǎng)絡(luò)、書籍、和各類手冊,如若侵權(quán)請告知,馬上刪帖致歉。目錄一、GPIO模式二、外設(shè) I/O配置模式
    發(fā)表于 08-24 07:20

    適配器模式實現(xiàn)

    目錄第一章 適配器模式介紹第二章 適配器模式實現(xiàn)(類適配器)2.1、關(guān)系依賴圖2.2、創(chuàng)建交流電2.3、定義直流電2.4、創(chuàng)建充電器2.5、創(chuàng)建手機類2.6、測試充電器2.7、點評該模式
    發(fā)表于 09-15 07:11

    LED燈各類程序【匯編版】

    LED燈各類程序【匯編版】LED燈各類程序【匯編版】LED燈各類程序【匯編版】LED燈各類程序【匯編版】
    發(fā)表于 12-29 11:04 ?0次下載

    LED燈各類程序【C語言】

    LED燈各類程序【C語言】LED燈各類程序【C語言】LED燈各類程序【C語言】LED燈各類程序【C語言】
    發(fā)表于 12-29 11:03 ?23次下載

    各類電源技術(shù)原理資料

    各類電源技術(shù)原理資料。
    發(fā)表于 05-05 11:37 ?10次下載

    ARM Linux:usr模式轉(zhuǎn)為svc模式實現(xiàn)原理

    大家都知道linux的應(yīng)用程序要想訪問內(nèi)核必須使用系統(tǒng)調(diào)用從而實現(xiàn)從usr模式轉(zhuǎn)到svc模式。下面咱們看看它的實現(xiàn)過程。
    的頭像 發(fā)表于 06-20 10:23 ?3654次閱讀

    基于VxWorks操作系統(tǒng)實現(xiàn)控制應(yīng)用的復(fù)合通信模式設(shè)計

    在嵌入式系統(tǒng)與VxWorks實時操作系統(tǒng)應(yīng)用愈加廣泛的背景下,結(jié)合嵌入式系統(tǒng)對于各類實時通信方式的不同需要,提出基于控制應(yīng)用的復(fù)合通信模式;以Radstone公司的PPCx系列單板機為例,給出實現(xiàn)方法,并對數(shù)據(jù)通信的產(chǎn)時性與安全
    的頭像 發(fā)表于 10-09 09:54 ?2230次閱讀
    基于VxWorks操作系統(tǒng)<b class='flag-5'>實現(xiàn)</b>控制應(yīng)用的復(fù)合通信<b class='flag-5'>模式</b>設(shè)計

    十種不同模式實現(xiàn)簡單的計算案例

    labview作為一種開發(fā)語言其實有許多不同的設(shè)計模式,大家在學(xué)習(xí)工作中接觸最多當(dāng)屬狀態(tài)機,今天我們就通過一個簡單的計算案例用十種不同的模式實現(xiàn),主要帶領(lǐng)大家了解不同編程模式之間的區(qū)
    的頭像 發(fā)表于 10-22 11:47 ?2188次閱讀
    十種不同<b class='flag-5'>模式</b><b class='flag-5'>實現(xiàn)</b>簡單的計算案例

    分類細(xì)敘各類電子元器件的失效模式與機理資料下載

    電子發(fā)燒友網(wǎng)為你提供分類細(xì)敘各類電子元器件的失效模式與機理資料下載的電子資料下載,更有其他相關(guān)的電路圖、源代碼、課件教程、中文資料、英文資料、參考設(shè)計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
    發(fā)表于 04-05 08:53 ?33次下載
    分類細(xì)敘<b class='flag-5'>各類</b>電子元器件的失效<b class='flag-5'>模式</b>與機理資料下載

    C++常見設(shè)計模式解析與實現(xiàn)

    C++常見設(shè)計模式解析與實現(xiàn)說明。
    發(fā)表于 06-01 15:44 ?11次下載

    基于ERBAC模式的在線考試模型研究與實現(xiàn)_何利雪

    基于ERBAC模式的在線考試模型研究與實現(xiàn)_何利雪(康佳電視副電源鍵)-基于ERBAC模式的在線考試模型研究與實現(xiàn)_何利雪這是一份非常不錯的資料,歡迎下載,希望對您有幫助!
    發(fā)表于 07-26 13:04 ?14次下載
    基于ERBAC<b class='flag-5'>模式</b>的在線考試模型研究與<b class='flag-5'>實現(xiàn)</b>_何利雪

    多樣化低功耗模式符合您的各類系統(tǒng)需求

    ) NuMicro M480 高效能系列 多樣化低功耗模式符合您的各類系統(tǒng)需求 近年來,各類工業(yè)互聯(lián)網(wǎng)、大數(shù)據(jù)、云端應(yīng)用、IoT無線模塊、移動式裝置等相關(guān)市場蓬勃發(fā)展。對于微控制器的規(guī)格需求,除了效能之外
    的頭像 發(fā)表于 10-09 15:40 ?1910次閱讀

    基于B-S模式遠(yuǎn)程測控的實現(xiàn)

    電子發(fā)燒友網(wǎng)站提供《基于B-S模式遠(yuǎn)程測控的實現(xiàn).pdf》資料免費下載
    發(fā)表于 10-23 09:29 ?0次下載
    基于B-S<b class='flag-5'>模式</b>遠(yuǎn)程測控的<b class='flag-5'>實現(xiàn)</b>