1. 程式人生 > >第28章 行為型模式大PK

第28章 行為型模式大PK

27.1 策略模式 VS 命令模式

27.1.1 策略模式實現壓縮演算法

//行為型模式大PK——策略模式和命令模式
//例項:用策略模式實現壓縮演算法
#include <iostream>
#include <string>

using namespace std;

//抽象壓縮演算法
class Algorithm
{
public:
    //壓縮演算法
    virtual bool compress(string source, string to) = 0;
    //解壓演算法
    virtual bool uncompress(string source, string to) = 0;

    virtual ~Algorithm(){}
};

//具體演算法:zip演算法
class Zip : public Algorithm
{
public:
    //zip格式的壓縮演算法
    bool compress(string source, string to)
    {
        cout << source << " --> " << to <<" ZIP壓縮成功" <<endl;
        return true;
    }
    //zip格式的解壓縮演算法
    bool uncompress(string source, string to)
    {
        cout << source <<" --> " << to <<" ZIP解壓縮成功" <<endl;
        return true;
    }
};

//具體演算法:Gzip
class Gzip : public Algorithm
{
public:
    //gzip格式的壓縮演算法
    bool compress(string source, string to)
    {
        cout << source << " --> " << to <<" GZIP壓縮成功" <<endl;
        return true;
    }
    //gzip格式的解壓縮演算法
    bool uncompress(string source, string to)
    {
        cout << source <<" --> " << to <<" GZIP解壓縮成功" <<endl;
        return true;
    }
};

//環境角色
class Context
{
private:
    Algorithm* al; //指向抽象演算法
public:
    Context(Algorithm* al)
    {
        this->al = al;
    }

    //設定演算法策略
    void setAlgorithm(Algorithm* al)
    {
        this->al = al;
    }

    //執行壓縮演算法
    bool compress(string source, string to)
    {
        return al->compress(source ,to);
    }

    //執行解壓縮演算法
    bool uncompress(string source, string to)
    {
        return al->uncompress(source ,to);
    }
};

int main()
{
    //建立兩種演算法策略
    Algorithm* alZip = new Zip();
    Algorithm* alGzip = new Gzip();

    //建立環境角色,並執行zip演算法
    Context ctx(alZip);

    cout <<"========執算zip演算法========" << endl;
    //執行壓縮演算法
    ctx.compress("c:\\window", "d:\\windows.zip");
    //執行解壓縮演算法
    ctx.compress("c:\\window.zip", "d:\\windows");

    //"========執算gzip演算法========"
    cout <<"========執算gzip演算法========" << endl;
    //更換演算法
    ctx.setAlgorithm(alGzip);

    //執行壓縮演算法
    ctx.compress("c:\\window", "d:\\windows.zip");
    //執行解壓縮演算法
    ctx.compress("c:\\window.zip", "d:\\windows");

    delete alZip;
    delete alGzip;
    return 0;
};
/*輸出結果:
========執算zip演算法========
c:\window --> d:\windows.zip ZIP壓縮成功
c:\window.zip --> d:\windows ZIP壓縮成功
========執算gzip演算法========
c:\window --> d:\windows.zip GZIP壓縮成功
c:\window.zip --> d:\windows GZIP壓縮成功
*/

27.1.2 命令模式實現壓縮演算法

//行為型模式大PK——策略模式和命令模式
//例項:用命令模式實現壓縮演算法
#include <iostream>
#include <string>

using namespace std;

//抽象接收者
class IReceiver
{
public:
    //壓縮
    virtual bool compress(string source, string to) = 0;
    //解壓
    virtual bool uncompress(string source, string to) = 0;

    virtual ~IReceiver(){}
};

//zip接收者
class ZipReceiver : public IReceiver
{
public:
    //壓縮
    bool compress(string source, string to)
    {
        cout << source << " --> " << to <<" ZIP壓縮成功" <<endl;
        return true;
    }
    //解壓
    bool uncompress(string source, string to)
    {
        cout << source << " --> " << to <<" ZIP解壓縮成功" <<endl;
        return true;
    }
};

//Gzip接收者
class GzipReceiver : public IReceiver
{
public:
    //壓縮
    bool compress(string source, string to)
    {
        cout << source << " --> " << to <<" GZIP壓縮成功" <<endl;
        return true;
    }
    //解壓
    bool uncompress(string source, string to)
    {
        cout << source << " --> " << to <<" GZIP解壓縮成功" <<endl;
        return true;
    }
};

//抽象壓縮命令
class AbstractCmd
{
protected:
    //持有接收者的引用
    IReceiver* zip;
    IReceiver* gzip;
public:
    AbstractCmd()
    {
        zip = new ZipReceiver();
        gzip = new GzipReceiver();
    }
    virtual ~AbstractCmd()
    {
        delete zip;
        delete gzip;
    }

    //抽象方法,命令的具體單元
    virtual bool execute(string source, string to) = 0;
};

//zip壓縮命令
class ZipCompressCmd : public AbstractCmd
{
public:
    bool execute(string source, string to)
    {
        return  zip->compress(source, to);
    }
};

//zip解壓縮命令
class ZipUncompressCmd : public AbstractCmd
{
public:
    bool execute(string source, string to)
    {
       return  zip->uncompress(source, to);
    }
};

//gzip壓縮命令
class GzipCompressCmd : public AbstractCmd
{
public:
    bool execute(string source, string to)
    {
       return  gzip->compress(source, to);
    }
};

//gzip解壓縮命令
class GzipUncompressCmd : public AbstractCmd
{
public:
    bool execute(string source, string to)
    {
       return  gzip->uncompress(source, to);
    }
};

//呼叫者
class Invoker
{
private:
    //持有抽象命令的引用
    AbstractCmd* cmd;
public:
    Invoker(AbstractCmd* cmd)
    {
        this->cmd = cmd;
    }

    //執行命令
    bool execute(string source, string to)
    {
        return cmd->execute(source, to);
    }

    void setCommand(AbstractCmd* cmd)
    {
        this->cmd = cmd;
    }
};

int main()
{
    //定義一個命令,壓縮一個檔案
    AbstractCmd* cmd = new ZipCompressCmd();
    //AbstractCmd* cmd = new ZipUncompressCmd();//換命令

    //定義呼叫者
    Invoker* invoker = new Invoker(cmd);
    //執行壓縮命令
    cout <<"========執行壓縮命令========"<<endl;

    invoker->execute("c:\\windows", "d:\\windows.zip");

    return 0;
};
/*輸出結果:
========執行壓縮命令========
c:\windows --> d:\windows.zip ZIP壓縮成功
*/

//另一種實現(只提供類圖)

注意:圖中的接收者是按物件設計的

27.1.3 小結

(1)當命令模式退化時,比如無接收者(接收者非常簡單,無需專門編寫一個接收者),在這種情況下,命令模式和策略模式的類圖完全一樣,程式碼實現也比較類似

(2)關注點不同

  ①策略模式關注的是演算法替換的問題,一個新的演算法投產,舊演算法退體,或者提供多種演算法由呼叫者自己選擇使用,演算法的自由更替是它實現的要點。換句話說,策略模式關注的是演算法的完整性、封裝性,只有具備了這兩個條件才能保證其可以自由切換。

  ②命令模式則關注的是解耦問題,如何讓請求者和執行者解耦是它需要首先解決的,解耦的要求就是把請求的內容封裝為一個個命令,由接收者執行。由於封裝成命令,就同時可以對命令進行多種處理,例如撤銷、記錄等。

(3)角色功能不同

  ①策略模式中的具體演算法是負責一個完整演算法邏輯,它是不可再拆分的原子業務,一旦變更就是對演算法整體的變更。

  ②命令模式關注命令的實現,也就是功能的實現。接收者對命令負責,而與請求者無關。

(4)使用場景不同

  策略模式適用於演算法要求變換的場景,而命令模式適用於解耦兩個有緊耦合的物件。

27.2 策略模式 VS 狀態模式

27.2.1 策略模式實現人生

(1)孩童時期的工作就是玩耍;成人時期的主要工作是養活自己,然後為社會做貢獻;老年時期的主要工作是享受天倫之樂。

(2)策略模式,把三種不同的工作方式看作三個不同時期的演算法,隨著時間推移工作內容隨之更替。

【程式設計實驗】用策略模式實現人生

//行為型模式大PK——策略模式和狀態模式
//例項:用策略模式實現人生
#include <iostream>
#include <string>

using namespace std;

//抽象工作演算法
class WorkAlgorithm
{
public:
    //每個年齡段都必須完成的工作
    virtual void work() = 0;

    virtual ~WorkAlgorithm(){}
};

//孩童工作
class ChildWork : public WorkAlgorithm
{
public:
    void work()
    {
        cout << "兒童的工作是玩耍!" << endl;
    }
};

//成人工作
class AdultWork : public WorkAlgorithm
{
public:
    void work()
    {
        cout << "成人的工作就是養活自己,然後為社會貢獻!" << endl;
    }
};

//老人工作
class OldWork : public WorkAlgorithm
{
public:
    void work()
    {
        cout << "老年人的工作就是享受天倫之樂!" << endl;
    }
};

//環境角色
class Context
{
private:
    WorkAlgorithm* workAlgorithm;
public:
    void setWork(WorkAlgorithm* work)
    {
        workAlgorithm = work;
    }

    //每個演算法都必須具有的功能
    void work()
    {
        workAlgorithm->work();
    }
};

int main()
{
    //定義一個環境角色
    Context ctx;
    cout <<"=====兒童的主要工作====="<< endl;
    WorkAlgorithm* child= new ChildWork();
    ctx.setWork(child);
    ctx.work();

    cout <<"=====成年人的主要工作====="<< endl;
    WorkAlgorithm* adult= new AdultWork();
    ctx.setWork(adult);
    ctx.work();

    cout <<"=====老人的主要工作====="<< endl;
    WorkAlgorithm* oldman= new OldWork();
    ctx.setWork(oldman);
    ctx.work();

    delete child;
    delete adult;
    delete oldman;

    return 0;
};
/*輸出結果:
=====兒童的主要工作=====
兒童的工作是玩耍!
=====成年人的主要工作=====
成人的工作就是養活自己,然後為社會貢獻!
=====老人的主要工作=====
老年人的工作就是享受天倫之樂!
*/

27.2.2 狀態模式實現人生

(1)孩童時期的工作就是玩耍;成人時期的主要工作是養活自己,然後為社會做貢獻;老年時期的主要工作是享受天倫之樂。

(2)狀態模式,認為人的狀態(孩童、成人、老人)產生了不同的行為結果,這裡的行為都相同,都是工作,但是它們的實現方式不同。

【程式設計實驗】用狀態模式實現人生

//行為型模式大PK——策略模式和狀態模式
//例項:用狀態模式實現人生
#include <iostream>
#include <string>

using namespace std;

//**************************************介面宣告******************************
class Human;  //前置宣告,相當於環境角色類

//人的抽象狀態
class HumanState
{
protected:
    Human* human;
public:
    void setHuman(Human* human)
    {
        this->human = human;
    }

    //每一種狀態對應一種行為
    virtual void work() = 0;
};

//環境角色
class Human
{
private:
    HumanState* state; //當前狀態
public:
    static HumanState* CHILD_STATE;
    static HumanState* ADULT_STATE;
    static HumanState* OLD_STATE;
public:
    void setState(HumanState* state)
    {
        this->state = state;
        state->setHuman(this);
    }

    //人類的工作
    void work()
    {
        state->work();
    }
};

//*********************************具體的狀態類*************************************
//孩童狀態
class ChildState : public HumanState
{
public:
    void work()
    {
        cout << "兒童的工作是玩耍!" << endl;
        //設定下一個狀態
        human->setState(Human::ADULT_STATE);
    }
};

//成人狀態
class AdultState : public HumanState
{
public:
    void work()
    {
        cout << "成人的工作就是養活自己,然後為社會貢獻!" << endl;
        //設定下一個狀態
        human->setState(Human::OLD_STATE);
    }
};

//老人狀態
class OldState : public HumanState
{
public:
    void work()
    {
        cout << "老年人的工作就是享受天倫之樂!" << endl;
    }
};

HumanState* Human::CHILD_STATE = new ChildState();
HumanState* Human::ADULT_STATE = new AdultState();
HumanState* Human::OLD_STATE   = new OldState();

int main()
{
    //定義一個環境角色
    Human human;

    //狀態的切換由環境角色和狀態共同決定,是由內部引起的狀態變化
    //而策略模式引起策略的變化是外部選擇的結果,這兩者存在很大差別

    //設定一個人的初始狀態
    human.setState(Human::CHILD_STATE);
    cout <<"=====兒童的主要工作====="<< endl;
    human.work();

    cout <<"=====成年人的主要工作====="<< endl;
    human.work();

    cout <<"=====老人的主要工作====="<< endl;
    human.work();

    return 0;
};
/*輸出結果:
=====兒童的主要工作=====
兒童的工作是玩耍!
=====成年人的主要工作=====
成人的工作就是養活自己,然後為社會貢獻!
=====老人的主要工作=====
老年人的工作就是享受天倫之樂!
*/

27.2.3 小結

(1)解決問題的重點不同

  ①策略模式封裝演算法,並保證演算法可以自由地切換。但是演算法之間是沒有互動的

  ②狀態模式旨在指解決內在的狀態的改變而引起的行為改變的問題,它的出發點是事物的狀態,封裝狀態而暴露行為,一個物件的狀態改變,從外界來看就好象是行為改變。

(2)解決問題的方法不同

  ①策略模式只是確保演算法可自由切換,但是什麼時候用什麼演算法它決定不了

  ②而狀態模式對外暴露的是行為,狀態的變化一般是由環境角色和具體狀態共同完成的,也就是說狀態模式封裝了狀態的變化而暴露了不同的行為或行為結果。

(3)環境角色的職責不同

  ①策略模式的環境角色只是一個委託作用,負責演算法的替換。

  ②狀態模式的環境角色不僅僅是委託行為,它還具有登記狀態變化的功能,與具體的狀態類協作,共同完成狀態切換行為隨之切換的任務。

27.3 觀察者模式和責任鏈模式

27.3.1 責任鏈模式實現DNS解析過程

(1)首先定義3個DNS伺服器:上海DNS伺服器(區域伺服器)、中國頂級DNS伺服器、全球頂級DNS伺服器。

(2)假設請求者發出請求,由上海DNS進行解析,如果能夠解析,則返回結果,若不能解析,則提交給父伺服器(中國頂級DNS)進行解析,若還不能解析,則提交到全球頂級DNS進行解析,若還不能解析,那就返回該域名無法解析。

【程式設計實驗】責任鏈模式實現DNS解析的類圖

 

  ①Recorder物件,它記錄DNS伺服器解析後的結果,包括域名、IP地址、屬主(即由誰解析的)。

  ②DnsServer抽象類中的resolve方法是一個基本方法,每個DNS伺服器都必須擁有該方法,它對DNS進行解析。具體是由echo方法來實現的,每個DNS伺服器獨自實現。

//行為型模式大PK——觀察者模式和責任鏈模式
//例項:用責任鏈模式實現DNS解析過程
#include <iostream>
#include <string>
#include <ctime>
#include <sstream> //for ostreamstring

using namespace std;

//********************************輔助類***************************************
//解析記錄
class Recorder
{
private:
    string domain; //域名,如www.baidu.com
    string ip;     //ip
    string owner;  //屬主,即解析者
public:
    string getDomain(){return domain;}
    void setDomain(string value)
    {
        domain = value;
    }

    string getIp(){return ip;}
    void setIp(string value)
    {
        ip = value;
    }

    string getOwner(){return owner;}
    void setOwner(string value)
    {
        owner = value;
    }

    string toString()
    {
        string str;

        str  = "域名:" + domain;
        str += "\nIP地址:" + ip;
        str += "\n解析者:" + owner;

        return str;
    }
};

//抽象域名伺服器
class DnsServer
{
private:
    //上級DNS是誰
    DnsServer* upperServer;  //即一下個伺服器
    //隨機產生一個IP地址,工具類
    string genIpAddress()
    {
        string ip;
        ostringstream oss;

        oss << rand() % 256 <<"." << rand() % 256 <<"."
            << rand() % 256 <<"." << rand() % 256 ;

        ip =oss.str();
        return ip;
    }
protected:
    //每個域名都在一個Zone內,判斷是否在本區中。
    virtual bool isLocal(string domain) = 0;

    //每個伺服器都必須實現解析任務
    virtual Recorder echo(string domain)
    {
        Recorder rec;
        //獲得IP地址
        rec.setIp(genIpAddress());
        rec.setDomain(domain);

        return rec;
    }

    //輔助函式,判斷字串是否以另一個字串結尾
    bool endWith(string source, string tail)
    {
        if (source.size() < tail.size())
            return false;

        return (0 == source.compare(source.size() - tail.size(), tail.size(), tail));
    }
public:
    DnsServer()
    {
        srand((int)time(NULL));
    }

    void setUpperServer(DnsServer* upperServer)
    {
        this->upperServer = upperServer;
    }

    //解析域名
    Recorder resolve(string domain)
    {
        Recorder rec;

        if(isLocal(domain))
        {
            rec = echo(domain);  //在本區,直接返回
        }
        else
        {
            //本伺服器不能解析,則提交上級伺服器
            rec = upperServer->resolve(domain);
        }

        return rec;
    }
    virtual ~DnsServer(){}
};

//具體的伺服器:上海DNS伺服器
class SHDnsServer : public  DnsServer
{
public:
    Recorder echo(string domain)
    {
        Recorder rec = DnsServer::echo(domain);
        rec.setOwner("上海DNS伺服器");
        return rec;
    }

    //定義上海的伺服器能處理的級別,即域名字尾為.sh.cn
    bool isLocal(string domain)
    {
        return endWith(domain, ".sh.cn");
    }
};

//具體的伺服器:中國頂級DNS伺服器
class CNDnsServer : public  DnsServer
{
public:
    Recorder echo(string domain)
    {
        Recorder rec = DnsServer::echo(domain);
        rec.setOwner("中國頂級DNS伺服器");
        return rec;
    }

    //定義中國頂級DNS伺服器能處理的級別,即域名字尾為.cn
    bool isLocal(string domain)
    {
        return endWith(domain, ".cn");
    }
};

//具體的伺服器:全球頂級DNS伺服器
class TopDnsServer : public  DnsServer
{
public:
    Recorder echo(string domain)
    {
        Recorder rec = DnsServer::echo(domain);
        rec.setOwner("全球頂級DNS伺服器");
        return rec;
    }

    //定義中國頂級DNS伺服器能處理的級別,即域名字尾為.cn
    bool isLocal(string domain)
    {
        return true; //所有的域名最終的解析地點
    }
};

int main()
{
    //上海域名伺服器
    DnsServer* sh = new SHDnsServer();
    //中國頂級域名伺服器
    DnsServer* cn = new CNDnsServer();
    //全球頂級域名伺服器
    DnsServer* top = new TopDnsServer();
    //定義查詢路徑
    cn->setUpperServer(top);
    sh->setUpperServer(cn);

    //解析域名
    cout << "=====域名解析模擬器=====" << endl;

    string domain = "www.abc.com";
    Recorder rec = sh->resolve(domain);
    cout << rec.toString() << endl;
    cout << "=====域名解析結束=====" << endl;

    delete sh;
    delete cn;
    delete top;

    return 0;
};
/*輸出結果:
=====域名解析模擬器=====
域名:www.abc.com
IP地址:4.40.34.78
解析者:全球頂級DNS伺服器
=====域名解析結束=====
*/

27.3.2 觀察者模式實現DNS解析過程

(1)責任鏈模式模擬DNS解析過程的缺陷責任鏈實現的DNS解析其返回的最終解析者是不同的。但實際中的DNS解析,其解析者都是相同的,準確地說都是由本機配置的DNS伺服器來做的解析,即返回的解析者是一樣的。

(2)真實DNS的解析過程

  A.首先由請求者傳送一個請求,然後由上海DNS伺服器嘗試解析,若不能解析再通過路徑②轉發給中國頂級DNS進行解析,解析結果通過路徑⑤返回給上海DNS,然後由上海DNS伺服器通過路徑⑥返回給請求者。

  B.同樣,若中國頂級DNS不能解析,則通過路徑③轉由全球頂級DNS進行解析,通過路徑④把結果返給中國頂級DNS,然後再通過路徑⑤返回給上海DNS。

  C.注意標號⑥,不管一個域名最終由誰解析,最終反饋給請求者的還是第一個節點,也就是說首節點負責請求者應答,其他節點都不與請求者互動,而只與自己的左右節點互動。

  D.實際的DNS伺服器就是如此處理的。

【程式設計實驗】觀察者模式模擬DNS的解析過程

  ①每個DNS可以看成即是被觀察者,又是觀察者。以中國DNS為例,當作為被觀察者時,其觀察者是上一級的全球DNS,他要實現的功能是當中國DNS出現了不能解析的域名,就通知觀察者(全球DNS)。如果作為觀察者時,其觀察的是上海DNS,當上海DNS出現不能處理的問題時,就交由他來處理。即所有的DNS伺服器都具備雙重身份:即是觀察者也是被觀察者。

  ②setUpperServer的作用是設定父DNS,也就是設定自己的觀察者

  ③SubmitToUppererServer:向父DNS請求解析,也就是通知觀察者。handleRequest是函式本DNS伺服器用來處理請求,也就是接到事件後的處理。

//行為型模式大PK——觀察者模式和責任鏈模式
//例項:用觀察者模式實現DNS解析過程
#include <iostream>
#include <string>
#include <ctime>
#include <sstream> //for ostreamstring

using namespace std;

//********************************輔助類***************************************
//解析記錄
class Recorder
{
private:
    string domain; //域名,如www.baidu.com
    string ip;     //ip
    string owner;  //屬主,即解析者
public:
    string getDomain(){return domain;}
    void setDomain(string value)
    {
        domain = value;
    }

    string getIp(){return ip;}
    void setIp(string value)
    {
        ip = value;
    }

    string getOwner(){return owner;}
    void setOwner(string value)
    {
        owner = value;
    }

    string toString()
    {
        string str;

        str  = "域名:" + domain;
        str += "\nIP地址:" + ip;
        str += "\n解析者:" + owner;

        return str;
    }
};

//觀察者介面
class Observer
{
public:
    virtual void handleRequest(Recorder* recorder) = 0;
};

//主題物件,被觀察者
class Observable
{
protected:
    //當該物件行為變更時,要通知的觀察者
    Observer* observer;
    void setObserver(Observer* obs)
    {
        observer = obs;
    }
public:
    //通知觀察者
    void notifyObserver(Recorder* rec)
    {
        observer->handleRequest(rec);
    }
};

//抽象域名伺服器(從被觀察者繼承,並實現觀察介面)
//即每個伺服器即做為觀察者,觀察來自前一級DNS的請求,
//同時被觀察者,在不能處理時向後一級DNS提交請求
class DnsServer : public Observable, public Observer
{
private:
    //隨機產生一個IP地址,工具類
    string genIpAddress()
    {
        string ip;
        ostringstream oss;

        oss << rand() % 256 <<"." << rand() % 256 <<"."
            << rand() % 256 <<"." << rand() % 256 ;

        ip =oss.str();
        return ip;
    }
protected:
    //每個DNS伺服器簽名上自己的名字
    virtual void sign(Recorder* recorder) = 0;

    //每個域名都在一個Zone內,判斷是否在本區中。每個伺服器都必須定義自己的處理級別
    virtual bool isLocal(Recorder* recorder) = 0;

    //輔助函式,判斷字串是否以另一個字串結尾
    bool endWith(string source, string tail)
    {
        if (source.size() < tail.size())
            return false;

        return (0 == source.compare(source.size() - tail.size(), tail.size(), tail));
    }

public:
    //作為被觀察者,允許增加觀察者,這裡是上一級DNS,一般只有一個
    void setUpperServer(DnsServer* dnsServer)
    {
        setObserver(dnsServer);
    }

    //處理請求,也就是接到事件後的處理
    void handleRequest(Recorder* recorder)
    {
        //如果並本能解析
        if(isLocal(recorder))
        {
            recorder->setIp(genIpAddress());
        }
        else //本伺服器不能解析,則提交到上級DNS
        {
            SubmitToUpperServer(recorder); //通知觀察者(上一級DNS)處理
        }

        //處理完後,簽上自己的名字
        sign(recorder);
    }
    //向父DNS請求解析,也就是通知觀察者
    void SubmitToUpperServer(Recorder* recorder)
    {
        notifyObserver(recorder);
    }

    virtual ~DnsServer(){}
};

//上海DNS伺服器
class SHDnsServer : public DnsServer
{
protected:
    void sign(Recorder* recorder)
    {
        recorder->setOwner("上海DNS伺服器");
    }

    //定義上海的DNS伺服器的處理級別
    bool isLocal(Recorder* recorder)
    {
        return endWith(recorder->getDomain(),".sh.cn");
    }
};

//中國頂級DNS伺服器
class CNDnsServer : public DnsServer
{
protected:
    void sign(Recorder* recorder)
    {
        recorder->setOwner("中國頂級DNS伺服器");
    }

    //定義上海的DNS伺服器的處理級別
    bool isLocal(Recorder* recorder)
    {
        return endWith(recorder->getDomain(),".cn");
    }
};

//全球頂級DNS伺服器
class TopDnsServer : public DnsServer
{
protected:
    void sign(Recorder* recorder)
    {
        recorder->setOwner("全球頂級DNS伺服器");
    }

    //定義上海的DNS伺服器的處理級別
    bool isLocal(Recorder* recorder)
    {
        return true; //所有的域名最終的解析地點
    }
};

int main()
{
    //上海域名伺服器
    DnsServer* sh = new SHDnsServer();
    //中國頂級域名伺服器
    DnsServer* cn = new CNDnsServer();
    //全球頂級域名伺服器
    DnsServer* top = new TopDnsServer();
    //定義查詢路徑
    cn->setUpperServer(top);
    sh->setUpperServer(cn);

    //解析域名
    cout << "=====域名解析模擬器=====" << endl;

    string domain = "www.abc.com";
    Recorder rec;
    rec.setDomain(domain);
    sh->handleRequest(&rec);
    cout << rec.toString() << endl;

    cout << "=====域名解析結束=====" << endl;

    delete sh;
    delete cn;
    delete top;

    return 0;
};
/*輸出結果:
=====域名解析模擬器=====
域名:www.abc.com
IP地址:132.190.35.41
解析者:上海DNS伺服器    //注意這裡,永遠都是由上海DNS伺服器返回結果
=====域名解析結束=====
*/

27.3.3 小結:

(1)相同:都是鏈結構。觀察者模式是可看作是一個觸發鏈。

(2)鏈中的訊息物件不同

  ①兩個鏈中傳遞的訊息物件不同。責任鏈模式基本上不改變訊息物件的結構,雖然每個節點都可以參與消費(一般是不參與消費),但是它的結構不會改變。

  ②而觀察者模式中鏈中傳遞的物件可以自由變化只要上下級節點對傳遞的物件瞭解即可

(3)上下節點的關係不同

  ①在責任鏈中,上下節點沒有關係,都是接收同樣的物件有,所有傳遞的物件都是從鏈首傳遞過來

  ②觸發鏈模式它的上下級關係很親密,鏈中的任意兩個相鄰節點都是一個牢固的獨立團體

(4)訊息的分銷渠道不同

  ①責任鏈模式中,一個訊息從鏈首傳遞進來後,就開始沿著鏈條向鏈尾運動,方向是單一的、固定的

  ②觸發鏈模式則不同,由於它採用的是觀察者模式,所以很靈活,一個訊息傳遞到鏈首後,具體怎麼傳遞是不固定的,可以以廣播方式傳遞,也可以以跳躍方式傳遞,這取決於處理訊息的邏輯。