設計模式系列——裝飾者模式(DecoratorPattern)

所謂的設計模式,其實是對面向對象編程思想中的一個轉變,是在繁重需求任務中做到可擴展,高度靈活,并且適應業(yè)務開發(fā)而產生的一種思想。
今天我們說的修飾者模式,是一種動態(tài)地往一個類中添加新的行為的設計模式。就功能而言,修飾模式相比生成子類更為靈活,這樣可以給某個對象而不是整個類添加一些功能。

當有幾個相互獨立的功能需要擴充時,這個區(qū)別就變得很重要。在有些面向對象的編程語言中,類不能在運行時被創(chuàng)建,通常在設計的時候也不能預測到有哪幾種功能組合。這就意味著要為每一種組合都得創(chuàng)建一個新類。

好吧,現(xiàn)在把我當做一個小偷,我需要對大師的畫進行臨摹,我臨摹的畫有很多類型的 可能是油畫,可能是水墨畫,也可能是沙畫等等。我臨摹完了這些畫之后,需要對畫進行裝飾一下才能賣個好價錢,比如對畫添加一個畫框,這個畫框可能是木框,可能是鉆石框(好吧,我承認我比較奢侈)。但是添加完了畫框之后,我可能又想為其添加一些簽名,例如齊白石老先生的簽名,例如梵高的簽名,這樣才能賣個好價錢呀!反正我會想盡一切辦法去添加畫本身內容外的裝飾。那么我們傳統(tǒng)手法怎么做呢?
我們學過OOP思想——類的繼承的話,就會想著不斷繼承下去,例如我的油畫:class OilPicture,然后添加了木框:class WoodFrameOilPicture : public OilPicture, 然后添加了齊白石老先生的簽名:class BaiShiWoodFrameOilPicture : public WoodFrameOilPicture。想一想,不同的組合竟然會產生各種不同的子類,這種不斷繼承來添加新特性的繼承地獄是在是太愚蠢了。
現(xiàn)在我們可不可以想出一個新方法,讓我的油畫可以自由組合,又可以脫離這種繼承地獄的方法呢?這就是我們今天要說的主題——修飾者模式。
修飾模式是類繼承的另外一種選擇。類繼承在編譯時候增加行為,而裝飾模式是在運行時增加行為。
首先我們定義圖畫類:

class Picture {
public:
    Picture() {}
    virtual ~Picture() {};
    
    virtual int32_t getWorth() = 0; // 
    virtual std::string Description() = 0;
    virtual bool Contain(const std::type_info&) = 0;
private:
    Picture(const Picture&) = delete;
    void operator=(const Picture&) = delete;
};

這個圖畫類我們定義了四個接口:
int32_t getWorth(): 通過添加不同的裝飾品后獲取圖畫的價值.
std::string Description(): 這幅圖畫的描述文字.
bool Contain(const std::type_info&): 用于判斷圖畫是否添加了某種裝飾品.

我們臨摹的圖畫主要有兩種,一種是油畫,一種是水墨畫

class OilPicture : public Picture {
public:
    ~OilPicture() {}
    
    int32_t getWorth() override {
        return 500; 
    };
    
    std::string Description() override {
        return "一副好看油畫!";
    }
    
    bool Contain(const std::type_info& info) override {
        return typeid(OilPicture).hash_code() == info.hash_code();
    }
};
class InkPicture : public Picture {
public:
    ~InkPicture() {}
    
    int32_t getWorth() override {
        return 500; 
    }
    
    std::string Description() override {
        return "一副好看的水墨畫!";
    }
   
    bool Contain(const std::type_info& info) override {
        return typeid(InkPicture).hash_code() == info.hash_code();
    }
};

我們首先模仿的油畫和水墨畫都定價為500塊錢,然后添加他們的描述。好了現(xiàn)在這樣我們也可以去賣了,但是這個價值不高,我們需要添加一點裝飾品,讓畫看上去高大上一點。

這里我們可能需要為畫添加一個簽名,這個簽名統(tǒng)一命名為簽名裝飾品,它是圖畫的一部分

class AuthorPictureDecorator : public Picture {
public:
    AuthorPictureDecorator(Picture* p, std::string author) : picture_(p), author_(author) {}
    
    virtual ~AuthorPictureDecorator() {
        delete picture_;
    }
    
    std::string Description() override {
        return picture_->Description() + PetPhrase();
    }
    
    int32_t getWorth() override {
        return picture_->getWorth() + AuthorWorth();
    };
    
    virtual int32_t AuthorWorth() = 0;
    virtual std::string PetPhrase() = 0;
protected:
    Picture* picture_;
    std::string author_; // 作者的名字
};

std::string PetPhrase(): 作者的口頭禪
Picture* picture_: 這個變量用于記住我們要修飾的圖畫對象.
int32_t getWorth(): 圖畫的價值加上簽名的價值,就是我們的圖畫真實的價值了.
int32_t AuthorWorth() 作者簽名的價值

好了,但是我只會模仿齊白石老先生跟梵高的簽名:

class AuthorQiBaishi : public AuthorPictureDecorator {
public:
    AuthorQiBaishi(Picture* p) : AuthorPictureDecorator(p, "QiBaiShi") {
    }
    
    int32_t AuthorWorth() override { 
        if (Contain(typeid(InkPicture))) { // 齊白石老先生只會畫水墨畫
            return 1000;
        }
        else {
            return -500; // 假的畫
        }
    }
    
    bool Contain(const std::type_info& info) override {

        if (typeid(AuthorQiBaishi).hash_code() != info.hash_code()) {
            return picture_->Contain(info);
        }
        return true;
    }

    std::string PetPhrase() override {
        return "我是" + author_ + "皮皮蝦我們走!";
    }
};
class AuthorFanGao : public AuthorPictureDecorator {
public:
    AuthorFanGao(Picture* p) : AuthorPictureDecorator(p, "FanGao") {
    }
    
    int32_t AuthorWorth() override {
        if (Contain(typeid(OilPicture))) { // 梵高大石只會畫油畫呀
            return 2000;
        }
        else {
            return -500; // 假的畫
        }
    }
    
    bool Contain(const std::type_info& info) override {
        
        if (typeid(AuthorFanGao).hash_code() != info.hash_code()) {
            return picture_->Contain(info);
        }
        return true;
    }
    
    std::string PetPhrase() override {
        return "I am " + author_ + " 不要跟我談錢,我就是窮!";
    }
};

恩,很好,添加了簽名貌似更值錢了,不過添加一個畫框那就更完美了,順便掙一波畫框的錢,哈哈哈,我真是生意天才!
這里我們可能需要為畫添加一個畫框,這個畫框統(tǒng)一命名為畫框裝飾品,它是圖畫的一部分:

class PictureFrameDecorator : public Picture {
public:
    PictureFrameDecorator(Picture* p) : picture_(p) {}
    virtual ~PictureFrameDecorator() {
        delete picture_;
    }
    
    int32_t getWorth() override {
        return picture_->getWorth() + FrameWorth();
    }
    
    std::string Description() override {
        return picture_->Description() + Features();
    } 
    
    virtual int32_t FrameWorth() = 0;
    virtual std::string Features() const = 0;
protected:
    Picture* picture_;
};

std::string Features(): 畫框的描述
Picture* picture_: 這個變量用于記住我們要修飾的圖畫對象.
int32_t getWorth(): 圖畫目前的價值加上畫框的價值,就是我們的圖畫真實的價值了.
int32_t FrameWorth() 畫框的真實的價值

但是我這邊材料只有兩種,一種是木頭,一種是鉆石,額確實有點奢侈,有什么辦法呢~:

class DiamondFrame : public PictureFrameDecorator {
public:
    DiamondFrame(Picture* p) : PictureFrameDecorator(p) {}
    ~DiamondFrame() {}
    
    int32_t FrameWorth() override {
        return 30000; // 鉆石框定價三萬塊錢,恩,這肯定能好好掙一波
    }
    
    bool Contain(const std::type_info& info) override {
        
        if (typeid(DiamondFrame).hash_code() != info.hash_code()) {
            return picture_->Contain(info);
        }
        return true;
    }
    
    std::string Features() const override {
        return "閃閃發(fā)光!";
    }
};
class WoodFrame : public PictureFrameDecorator {
public:
    WoodFrame(Picture* p) : PictureFrameDecorator(p) {}
    ~WoodFrame() {}
    
    int32_t FrameWorth() override {
        return 5000; // 木頭我也要定價為 5000 塊錢,我不管!
    }
    
    std::string Features() const override {
        return "久遠大自然的味道!";
    }
    
    bool Contain(const std::type_info& info) override {
        if (typeid(WoodFrame).hash_code() != info.hash_code()) {
            return picture_->Contain(info);
        }
        return true;
    }
};

好了,先定這兩個裝飾品吧,后面根據(jù)用戶的要求,再去添加其他裝飾品吧。

先看看客戶A的要求:"你好,我要一副油畫,有梵高大師的簽名的"
我:好的,沒問題老板,嘻嘻嘻!

Picture* oil_pic = new OilPicture();
oil_pic = new AuthorFanGao(oil_pic);

我:好了,老板,你要的作品~oil_pic,價值為2500塊錢
客戶A:嗷,看上去加個圖框貌似更好,我要個鉆石框吧.
我:好的,老板,老板大氣!

Picture* oil_pic = new OilPicture();
oil_pic = new AuthorFanGao(oil_pic);
oil_pic = new DiamondFrame(oil_pic);

我:好了,老板,你要的作品~oil_pic,32500塊錢
客戶A:嗷,完美~

先看看客戶B的要求:"你好,我要一副水墨畫,有梵高大師和齊白石老先生的簽名的"
我:老板,你的口味比較獨特呀,不過沒問題~

Picture* ink_pic = new InkPicture();
ink_pic = new AuthorFanGao(ink_pic);
ink_pic = new AuthorFanGao(ink_pic);

我:好了,老板,你要的作品`ink_pic`,價值為1000塊錢,唉,往水墨畫上加梵高大石的名字這種品位必須給你打折
客戶B:不錯不錯~

通過這樣的裝飾,我們可以隨意添加各種各樣并且不同種類的裝飾品與此同時又不會影響到油畫類和水墨畫類本身的特性。

優(yōu)點:通過使用修飾模式,可以在運行時擴充一個類的功能,例如簽名功能,畫框功能。原理是:增加一個修飾類包裹原來的類,包裹的方式一般是通過在將原來的對象作為修飾類的構造函數(shù)的參數(shù)。裝飾類實現(xiàn)新的功能,但是,在不需要用到新功能的地方,它可以直接調用原來的類中的方法。修飾類必須和原來的類有相同的接口。

具體結構如下圖:


UML類圖
  • Component:抽象類的接口,例如我們的圖畫類Picture
  • ConcreteComponent:是 Component 的子類,具體的需要使用類,實現(xiàn)了相應的方法,它充當了“被裝飾者”的角色。例如我們的油畫類水墨畫類
  • Decorator:也是 Component 的子類,抽象裝飾者類的接口,內部有一個 Component 對象被持有用于被裝飾,例如我們的畫框裝飾品類
  • ConcreteDecorator:是Decorator的子類,是具體的裝飾者。由于它同時也是Component的子類,因此它能方便地拓展Component的狀態(tài)(比如添加新的方法)。每個裝飾者都應該有一個實例變量用以保存某個Component的引用,這也是利用了組合的特性。在持有Component的引用后,由于其自身也是Component的子類,那么,相當于ConcreteDecorator包裹了Component,不但有Component的特性,同時自身也可以有別的特性,也就是所謂的裝飾。相當于我們的木頭類鉆石類。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 0x01 前言 ??裝飾器模式(Decorator Pattern)允許向一個現(xiàn)有的對象添加新的功能,同時又不改變...
    菩提樹下成魔閱讀 477評論 0 0
  • 概念:標準的裝飾模式有包括一個抽象的Component父類,它聲明了一些操作,它具體的類講進行重載以實現(xiàn)自己特定的...
    平頭僧閱讀 366評論 0 0
  • 真誠的,TNANKS。 個人Github-23種設計模式案例鏈接 創(chuàng)建型模式 工廠模式 工廠模式(Factory ...
    水清_木秀閱讀 26,624評論 11 204
  • 簡寧回來了。 帶著戰(zhàn)敗的消息和萬民的唾罵回來的。一起回來的,還有弟弟簡昌的浸透了血水的腰牌,和一支所剩無幾的軍隊。...
    倪兒閱讀 702評論 0 2
  • 愿以此功德。莊嚴佛凈土。 上報四重恩。下濟三途苦。 若有見聞者。悉發(fā)菩提心。 盡此一報身。同生極樂國。 南無大悲觀...
    牛龍兄弟成長記閱讀 166評論 0 1

友情鏈接更多精彩內容