1. 程式人生 > >第27章 結構型模式大PK

第27章 結構型模式大PK

27.1 代理模式 VS 裝飾模式

27.1.1 代理模式

(1)場景:客人找運動員代理要求安排運動員參加比賽

(2)說明:代理人有控制權,可以拒絕客人的要求,也可以答應安排,甚至自己下去跑(因為有些運動員本身就作自己的代理)

【程式設計實驗】找代理安排運動員比賽

//結構型模式大PK——代理模式和裝飾模式
//例項:找代理安排運動員比賽
#include <iostream>
#include <ctime>
using namespace std;

//抽象運動員
class IRunner
{
public:
    virtual void run() = 0;

    virtual ~IRunner() {}
};

//運動員
class Runner : public IRunner
{
public:
    void run()
    {
        cout << "運動員跑步,動作很瀟灑" << endl;
    }
};

//代理人(也與運動員一樣,實現同一介面)
class RunnerAgent : public IRunner
{
    Runner* runner;
public:
    RunnerAgent(Runner* runner)
    {
        srand((int)time(NULL));
        this->runner = runner;
    }

    void run()
    {
        if((rand() % 2) ==0)
        {
            cout << "代理人同意安排運動員跑步" << endl;
            runner->run();
        }
        else
        {
            cout << "代理人心情不好,不安排運動員跑步" << endl;
        }
    }
};

int main()
{
    //定義一個運動員
    Runner* runner = new Runner();
    //定義代理人
    IRunner* agent = new RunnerAgent(runner);

    //要求運動員跑步
    cout << "===客人找到代理要求運動員去跑步" << endl;

    //為演示,比如洽談了10次
    for(int i=0; i<10; i++)
    {
        cout <<"第" <<i+1 <<"次洽談結果:";
        agent->run();
        cout << endl;
    }

    delete runner;
    delete agent;

    return 0;
};

27.1.2 裝飾模式

(1)裝飾模式:對類的功能進行加強

【程式設計實驗】安裝噴氣裝置

//結構型模式大PK——代理模式和裝飾模式
//例項:為飛機安裝噴氣動力裝置
#include <iostream>
#include <ctime>
using namespace std;

//飛行介面
class IFly
{
public:
    virtual void fly() = 0;

    virtual ~IFly() {}
};

//飛機
class AirPlane : public IFly
{
public:
    void fly()
    {
        cout << "飛機正在飛行..." << endl;
    }
};
//JetFly
class JetFly : public IFly
{
    IFly* airPlane;
public:
    JetFly(IFly* airPlane)
    {
        this->airPlane = airPlane;
    }
    void speedUp()
    {
        cout << "為飛機加裝了噴氣動力裝置,正在加速中..." << endl;
    }
    void fly()
    {
       speedUp();
       airPlane->fly();
    }
};
int main()
{
    //定義一架飛機
    IFly* airPlane = new AirPlane();
    //定義裝飾類,對功能進行加強
    IFly* jet = new JetFly(airPlane);

    cout << "===增加功能後的飛機" << endl;

    jet->fly();

    delete jet;
    delete airPlane;

    return 0;
};

27.1.3 最佳實踐

(1)裝飾類對被裝飾的類的行為沒有決定權,只有增強作用。而代理可以控制對被代理人的訪問。

(2)代理模式是把當前的行為或功能委託給其他物件執行,代理類負責介面限定(如是否可以呼叫真實角色,一般不對被代理功能當修飾,而是保證原汁原葉的呼叫

(3)裝飾模式在保證介面不變的情況下加強類的功能,它保證的是被修飾物件功能比原來豐富(當然也可以減弱),但不做准入條件和准入引數的過濾

27.1.3 最佳實踐

(1)裝飾類對被裝飾的類的行為沒有決定權,只有增強作用。而代理可以控制對被代理人的訪問。

(2)代理模式是把當前的行為或功能委託給其他物件執行,代理類負責介面限定(如是否可以呼叫真實角色,一般不對被代理功能當修飾,而是保證原汁原葉的呼叫

(3)裝飾模式在保證介面不變的情況下加強類的功能,它保證的是被修飾物件功能比原來豐富(當然也可以減弱),但不做准入條件和准入引數的過濾

27.2 裝飾模式 VS 介面卡模式

27.2.1 用裝飾模式描述醜小鴨

(1)醜小鴨首先是一隻天鵝,具備了天鵝所有的行為和屬性(即它從天鵝繼承而來)

(2)只是小時候,又小、又髒,又不能飛行。隨著時間推移,對其屬性和行為進行加強,慢慢變成一隻白天鵝。

【程式設計實驗】醜小鴨變小天鵝的故事

//結構型模式大PK——裝飾模式和介面卡模式
//例項:醜小鴨變小天鵝
#include <iostream>
#include <ctime>
using namespace std;

//天鵝介面
class Swan
{
public:
    virtual void fly() = 0;  //會飛
    virtual void cry() = 0;  //會叫
    virtual void Appearance() = 0; //外觀

    virtual ~Swan(){}
};

//醜小鴨(本質上是天鵝)
class UglyDuckling : public Swan
{
public:
    //醜小鴨還比較小,不能飛
    void fly()
    {
        cout << "不能飛" <<endl;
    }
    //醜小鴨的叫聲
    void cry()
    {
        cout << "叫聲是克嚕——克嚕——克嚕" << endl;
    }

    //醜小鴨的外形
    void Appearance()
    {
        cout << "外形是髒兮兮的白色,毛毛茸茸的大腦袋"  <<endl;
    }
};

//裝飾類
class Decorator : public Swan
{
private:
    Swan* swan;
public:
    Decorator(Swan* swan)
    {
        this->swan = swan;
    }
    void fly()
    {
        swan->fly();
    }

    void cry()
    {
        swan->cry();
    }

    void Appearance()
    {
        swan->Appearance();
    }
};

//具體的裝飾類(外形美化)
class BeautifyAppearance : public Decorator
{
public:
    BeautifyAppearance(Swan* swan):Decorator(swan){}

    //外表美化處理(重寫該方法)
    void Appearance()
    {
        //這裡是重新實現,不再呼叫原來的Decorator::desAppearance()
        cout << "外表是純白色的,非常惹人喜愛" << endl;
    }
};

//具體裝飾類
class StrongBehavior :public Decorator
{
public:
    StrongBehavior(Swan* swan):Decorator(swan){}

    //會飛行了
    void fly()
    {
        //這裡是完全重新實現了被裝飾類飛的功能。
        cout << "會飛行了!" << endl;
    }
};

int main()
{
    cout <<"===很久很久以前,這裡有一隻醜陋的小鴨子===" << endl;
    Swan* duckling = new UglyDuckling();

    //展示一下小鴨
    duckling->Appearance();
    duckling->cry();
    duckling->fly();

    cout <<"===小鴨子終於發現自己是一隻天鵝===" << endl;
    //首先外形變化
    BeautifyAppearance duckling2(duckling);
    StrongBehavior duckling3(&duckling2);

    duckling3.Appearance();
    duckling3.cry();
    duckling3.fly();

    delete duckling;

    return 0;
};
/*輸出結果:
===很久很久以前,這裡有一隻醜陋的小鴨子===
外形是髒兮兮的白色,毛毛茸茸的大腦袋
叫聲是克嚕——克嚕——克嚕
不能飛
===小鴨子終於發現自己是一隻天鵝===
外表是純白色的,非常惹人喜愛
叫聲是克嚕——克嚕——克嚕
會飛行了!
*/

27.2.2 用介面卡模式描述醜小鴨

(1)兩個介面:鴨和天鵝。而醜小鴨就相當於介面卡

(2)為了演示介面卡的使用,醜小鴨本質上是天鵝,所以應該把鴨子的介面轉為天鵝的介面。即鴨子的源介面,目標介面為天鵝。以便讓天鵝具備小鴨的一些特點。

【程式設計實驗】一隻與眾不同的鴨子

//結構型模式大PK——裝飾模式和介面卡模式
//例項:一隻與眾不同的鴨子
#include <iostream>
#include <ctime>
using namespace std;

//天鵝介面
class Swan
{
public:
    virtual void behavior() = 0;  //其他行為,會飛
    virtual void cry() = 0;  //會叫
    virtual void Appearance() = 0; //外觀
};

//鴨子的介面
class Duck
{
public:
    virtual void behavior() = 0;  //會游泳
    virtual void cry() = 0;  //會叫
    virtual void Appearance() = 0; //外觀

    virtual ~Duck(){}
};

//白天鵝
class WhiteSwan : public Swan
{
public:
    void cry()
    {
        cout << "叫聲是克嚕——克嚕——克嚕" << endl;
    }
    void Appearance()
    {
        cout << "外形是純白色,惹人喜愛" << endl;
    }
    //其他行為
    void behavior()
    {
        cout << "能夠飛行" << endl;
    }
};

//小鴨子
class Duckling : public Duck
{
public:
    void cry()
    {
        cout << "叫聲是嘎——嘎——嘎" << endl;
    }
    void Appearance()
    {
        cout << "外形是黃白相間,嘴長" << endl;
    }

    //描述鴨子的其他行為
    void behavior()
    {
        cout << "會游泳" << endl;
    }
};

//醜小鴨相當於介面卡的角色,醜小鴨本質上是天鵝,所以應該把鴨子
//的介面轉為天鵝的介面。即鴨子的源介面,目標介面為天鵝
class UglyDuckling : public WhiteSwan
{
   Duck* duck;  //目標介面
public:
    UglyDuckling(Duck* duck)
    {
        this->duck = duck;
    }
    //醜小鴨的叫聲,直接從父類繼承

    //醜小鴨的外形,直接從父類繼承

    //其他行為
    void behavior()
    {
        //本身會飛
        WhiteSwan::behavior();
        //學會鴨子的游泳
        duck->behavior();
    }
};

int main()
{
    cout <<"===鴨媽媽有5個孩子,其中4個都是一個模樣==="<< endl;
    Duck* duck = new Duckling();
    duck->cry();
    duck->Appearance();
    duck->behavior();

    cout <<"===一隻獨特的小鴨子,模樣是這樣的==="<< endl;
    UglyDuckling uglyDuckling(duck);  //醜小鴨
    uglyDuckling.cry();
    uglyDuckling.Appearance();
    uglyDuckling.behavior();

    delete duck;

    return 0;
};
/*輸出結果:
===鴨媽媽有5個孩子,其中4個都是一個模樣===
叫聲是嘎——嘎——嘎
外形是黃白相間,嘴長
會游泳
===一隻獨特的小鴨子,模樣是這樣的===
叫聲是克嚕——克嚕——克嚕
外形是純白色,惹人喜愛
能夠飛行
會游泳
*/

27.2.3 最佳實踐

(1)裝飾模式包裝的是自己的兄弟類,隸屬於同一家族(相同的父類),而介面卡模式則修飾的是非血緣關係類。把一個非本家族偽裝成本家族的物件,注意是偽裝,因此它的本質還是不同的介面物件。

(2)意圖不同

  裝飾模式的意圖是加強物件的功能,而介面卡模式關注的是轉化,兩個物件之間的介面轉化

(3)所作用的物件不同

  ①裝飾模式裝飾的物件必須是自己的同宗,也就是相同的父類,只要在具有相同的屬性和行為情況下,才能比較行為是增加或減弱。

  ②介面卡模式則必須是兩個不物件介面物件,因為它著重於轉換

(4)場景不同

  ①裝飾模式只要是想增強功能的,都可以用。

  ②介面卡模式則是一個補救模式,一般出現在系統成熟或己經構建完畢的專案中,作為一個緊急處理手段採用。

(5)擴充套件性不同

  裝飾模式容易擴充套件,如果不需要修飾某物件了,可以隨時拿掉,而且裝飾類也可以繼續擴充套件。但介面卡模式就不同,它在兩個不同物件之間架起一座溝通的橋染,建立容易,去掉時需要從系統整體考慮是否能夠撤銷。