設(shè)計(jì)模式系列——裝飾者模式(DecoratorPattern)

所謂的設(shè)計(jì)模式篮撑,其實(shí)是對(duì)面向?qū)ο缶幊趟枷胫械囊粋€(gè)轉(zhuǎn)變,是在繁重需求任務(wù)中做到可擴(kuò)展流济,高度靈活,并且適應(yīng)業(yè)務(wù)開(kāi)發(fā)而產(chǎn)生的一種思想猾担。
今天我們說(shuō)的修飾者模式袭灯,是一種動(dòng)態(tài)地往一個(gè)類(lèi)中添加新的行為的設(shè)計(jì)模式。就功能而言绑嘹,修飾模式相比生成子類(lèi)更為靈活稽荧,這樣可以給某個(gè)對(duì)象而不是整個(gè)類(lèi)添加一些功能。

當(dāng)有幾個(gè)相互獨(dú)立的功能需要擴(kuò)充時(shí)工腋,這個(gè)區(qū)別就變得很重要姨丈。在有些面向?qū)ο蟮木幊陶Z(yǔ)言中,類(lèi)不能在運(yùn)行時(shí)被創(chuàng)建擅腰,通常在設(shè)計(jì)的時(shí)候也不能預(yù)測(cè)到有哪幾種功能組合蟋恬。這就意味著要為每一種組合都得創(chuàng)建一個(gè)新類(lèi)。

好吧趁冈,現(xiàn)在把我當(dāng)做一個(gè)小偷歼争,我需要對(duì)大師的畫(huà)進(jìn)行臨摹拜马,我臨摹的畫(huà)有很多類(lèi)型的 可能是油畫(huà),可能是水墨畫(huà)沐绒,也可能是沙畫(huà)等等俩莽。我臨摹完了這些畫(huà)之后,需要對(duì)畫(huà)進(jìn)行裝飾一下才能賣(mài)個(gè)好價(jià)錢(qián)乔遮,比如對(duì)畫(huà)添加一個(gè)畫(huà)框扮超,這個(gè)畫(huà)框可能是木框,可能是鉆石框(好吧蹋肮,我承認(rèn)我比較奢侈)出刷。但是添加完了畫(huà)框之后,我可能又想為其添加一些簽名坯辩,例如齊白石老先生的簽名馁龟,例如梵高的簽名,這樣才能賣(mài)個(gè)好價(jià)錢(qián)呀濒翻!反正我會(huì)想盡一切辦法去添加畫(huà)本身內(nèi)容外的裝飾屁柏。那么我們傳統(tǒng)手法怎么做呢啦膜?
我們學(xué)過(guò)OOP思想——類(lèi)的繼承的話有送,就會(huì)想著不斷繼承下去,例如我的油畫(huà):class OilPicture僧家,然后添加了木框:class WoodFrameOilPicture : public OilPicture, 然后添加了齊白石老先生的簽名:class BaiShiWoodFrameOilPicture : public WoodFrameOilPicture雀摘。想一想,不同的組合竟然會(huì)產(chǎn)生各種不同的子類(lèi)八拱,這種不斷繼承來(lái)添加新特性的繼承地獄是在是太愚蠢了阵赠。
現(xiàn)在我們可不可以想出一個(gè)新方法,讓我的油畫(huà)可以自由組合肌稻,又可以脫離這種繼承地獄的方法呢清蚀?這就是我們今天要說(shuō)的主題——修飾者模式。
修飾模式是類(lèi)繼承的另外一種選擇爹谭。類(lèi)繼承在編譯時(shí)候增加行為枷邪,而裝飾模式是在運(yùn)行時(shí)增加行為。
首先我們定義圖畫(huà)類(lèi):

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

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

我們臨摹的圖畫(huà)主要有兩種诺凡,一種是油畫(huà)东揣,一種是水墨畫(huà)

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

我們首先模仿的油畫(huà)和水墨畫(huà)都定價(jià)為500塊錢(qián),然后添加他們的描述腹泌。好了現(xiàn)在這樣我們也可以去賣(mài)了嘶卧,但是這個(gè)價(jià)值不高,我們需要添加一點(diǎn)裝飾品凉袱,讓畫(huà)看上去高大上一點(diǎn)芥吟。

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

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_: 這個(gè)變量用于記住我們要修飾的圖畫(huà)對(duì)象.
int32_t getWorth(): 圖畫(huà)的價(jià)值加上簽名的價(jià)值钟鸵,就是我們的圖畫(huà)真實(shí)的價(jià)值了.
int32_t AuthorWorth() 作者簽名的價(jià)值

好了泵额,但是我只會(huì)模仿齊白石老先生跟梵高的簽名:

class AuthorQiBaishi : public AuthorPictureDecorator {
public:
    AuthorQiBaishi(Picture* p) : AuthorPictureDecorator(p, "QiBaiShi") {
    }
    
    int32_t AuthorWorth() override { 
        if (Contain(typeid(InkPicture))) { // 齊白石老先生只會(huì)畫(huà)水墨畫(huà)
            return 1000;
        }
        else {
            return -500; // 假的畫(huà)
        }
    }
    
    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))) { // 梵高大石只會(huì)畫(huà)油畫(huà)呀
            return 2000;
        }
        else {
            return -500; // 假的畫(huà)
        }
    }
    
    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_ + " 不要跟我談錢(qián)携添,我就是窮!";
    }
};

恩嫁盲,很好,添加了簽名貌似更值錢(qián)了烈掠,不過(guò)添加一個(gè)畫(huà)框那就更完美了羞秤,順便掙一波畫(huà)框的錢(qián),哈哈哈左敌,我真是生意天才!
這里我們可能需要為畫(huà)添加一個(gè)畫(huà)框瘾蛋,這個(gè)畫(huà)框統(tǒng)一命名為畫(huà)框裝飾品,它是圖畫(huà)的一部分:

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(): 畫(huà)框的描述
Picture* picture_: 這個(gè)變量用于記住我們要修飾的圖畫(huà)對(duì)象.
int32_t getWorth(): 圖畫(huà)目前的價(jià)值加上畫(huà)框的價(jià)值矫限,就是我們的圖畫(huà)真實(shí)的價(jià)值了.
int32_t FrameWorth() 畫(huà)框的真實(shí)的價(jià)值

但是我這邊材料只有兩種哺哼,一種是木頭,一種是鉆石叼风,額確實(shí)有點(diǎn)奢侈取董,有什么辦法呢~:

class DiamondFrame : public PictureFrameDecorator {
public:
    DiamondFrame(Picture* p) : PictureFrameDecorator(p) {}
    ~DiamondFrame() {}
    
    int32_t FrameWorth() override {
        return 30000; // 鉆石框定價(jià)三萬(wàn)塊錢(qián),恩无宿,這肯定能好好掙一波
    }
    
    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; // 木頭我也要定價(jià)為 5000 塊錢(qián)茵汰,我不管!
    }
    
    std::string Features() const override {
        return "久遠(yuǎn)大自然的味道!";
    }
    
    bool Contain(const std::type_info& info) override {
        if (typeid(WoodFrame).hash_code() != info.hash_code()) {
            return picture_->Contain(info);
        }
        return true;
    }
};

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

先看看客戶A的要求:"你好豆胸,我要一副油畫(huà),有梵高大師的簽名的"
我:好的巷疼,沒(méi)問(wèn)題老板晚胡,嘻嘻嘻!

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

我:好了皮迟,老板搬泥,你要的作品~oil_pic,價(jià)值為2500塊錢(qián)
客戶A:嗷伏尼,看上去加個(gè)圖框貌似更好忿檩,我要個(gè)鉆石框吧.
我:好的,老板爆阶,老板大氣燥透!

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

我:好了沙咏,老板,你要的作品~oil_pic班套,32500塊錢(qián)
客戶A:嗷肢藐,完美~

先看看客戶B的要求:"你好,我要一副水墨畫(huà)吱韭,有梵高大師和齊白石老先生的簽名的"
我:老板吆豹,你的口味比較獨(dú)特呀,不過(guò)沒(méi)問(wèn)題~

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

我:好了理盆,老板痘煤,你要的作品`ink_pic`,價(jià)值為1000塊錢(qián)猿规,唉衷快,往水墨畫(huà)上加梵高大石的名字這種品位必須給你打折
客戶B:不錯(cuò)不錯(cuò)~

通過(guò)這樣的裝飾,我們可以隨意添加各種各樣并且不同種類(lèi)的裝飾品與此同時(shí)又不會(huì)影響到油畫(huà)類(lèi)和水墨畫(huà)類(lèi)本身的特性姨俩。

優(yōu)點(diǎn):通過(guò)使用修飾模式蘸拔,可以在運(yùn)行時(shí)擴(kuò)充一個(gè)類(lèi)的功能,例如簽名功能环葵,畫(huà)框功能调窍。原理是:增加一個(gè)修飾類(lèi)包裹原來(lái)的類(lèi),包裹的方式一般是通過(guò)在將原來(lái)的對(duì)象作為修飾類(lèi)的構(gòu)造函數(shù)的參數(shù)积担。裝飾類(lèi)實(shí)現(xiàn)新的功能陨晶,但是,在不需要用到新功能的地方帝璧,它可以直接調(diào)用原來(lái)的類(lèi)中的方法。修飾類(lèi)必須和原來(lái)的類(lèi)有相同的接口湿刽。

具體結(jié)構(gòu)如下圖:


UML類(lèi)圖
  • Component:抽象類(lèi)的接口的烁,例如我們的圖畫(huà)類(lèi)Picture
  • ConcreteComponent:是 Component 的子類(lèi),具體的需要使用類(lèi)诈闺,實(shí)現(xiàn)了相應(yīng)的方法渴庆,它充當(dāng)了“被裝飾者”的角色。例如我們的油畫(huà)類(lèi)水墨畫(huà)類(lèi)
  • Decorator:也是 Component 的子類(lèi)雅镊,抽象裝飾者類(lèi)的接口襟雷,內(nèi)部有一個(gè) Component 對(duì)象被持有用于被裝飾,例如我們的畫(huà)框裝飾品類(lèi)
  • ConcreteDecorator:是Decorator的子類(lèi)仁烹,是具體的裝飾者耸弄。由于它同時(shí)也是Component的子類(lèi),因此它能方便地拓展Component的狀態(tài)(比如添加新的方法)卓缰。每個(gè)裝飾者都應(yīng)該有一個(gè)實(shí)例變量用以保存某個(gè)Component的引用计呈,這也是利用了組合的特性砰诵。在持有Component的引用后,由于其自身也是Component的子類(lèi)捌显,那么茁彭,相當(dāng)于ConcreteDecorator包裹了Component,不但有Component的特性扶歪,同時(shí)自身也可以有別的特性理肺,也就是所謂的裝飾。相當(dāng)于我們的木頭類(lèi)鉆石類(lèi)善镰。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哲嘲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子媳禁,更是在濱河造成了極大的恐慌眠副,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竣稽,死亡現(xiàn)場(chǎng)離奇詭異囱怕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)毫别,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)娃弓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人岛宦,你說(shuō)我怎么就攤上這事台丛。” “怎么了砾肺?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵挽霉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我变汪,道長(zhǎng)侠坎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任裙盾,我火速辦了婚禮实胸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘番官。我一直安慰自己庐完,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布徘熔。 她就那樣靜靜地躺著门躯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪近顷。 梳的紋絲不亂的頭發(fā)上生音,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天宁否,我揣著相機(jī)與錄音,去河邊找鬼缀遍。 笑死慕匠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的域醇。 我是一名探鬼主播台谊,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼譬挚!你這毒婦竟也來(lái)了锅铅?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤减宣,失蹤者是張志新(化名)和其女友劉穎盐须,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體漆腌,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贼邓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闷尿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塑径。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖填具,靈堂內(nèi)的尸體忽然破棺而出统舀,到底是詐尸還是另有隱情,我是刑警寧澤劳景,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布誉简,位于F島的核電站,受9級(jí)特大地震影響枢泰,放射性物質(zhì)發(fā)生泄漏描融。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一衡蚂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧骏庸,春花似錦毛甲、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至一姿,卻和暖如春七咧,著一層夾襖步出監(jiān)牢的瞬間跃惫,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工艾栋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爆存,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓蝗砾,卻偏偏與公主長(zhǎng)得像先较,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悼粮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 0x01 前言 ??裝飾器模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能闲勺,同時(shí)又不改變...
    菩提樹(shù)下成魔閱讀 402評(píng)論 0 0
  • 概念:標(biāo)準(zhǔn)的裝飾模式有包括一個(gè)抽象的Component父類(lèi),它聲明了一些操作扣猫,它具體的類(lèi)講進(jìn)行重載以實(shí)現(xiàn)自己特定的...
    平頭僧閱讀 304評(píng)論 0 0
  • 真誠(chéng)的菜循,TNANKS。 個(gè)人Github-23種設(shè)計(jì)模式案例鏈接 創(chuàng)建型模式 工廠模式 工廠模式(Factory ...
    水清_木秀閱讀 26,009評(píng)論 11 204
  • 簡(jiǎn)寧回來(lái)了申尤。 帶著戰(zhàn)敗的消息和萬(wàn)民的唾罵回來(lái)的癌幕。一起回來(lái)的,還有弟弟簡(jiǎn)昌的浸透了血水的腰牌瀑凝,和一支所剩無(wú)幾的軍隊(duì)序芦。...
    倪兒閱讀 605評(píng)論 0 2
  • 愿以此功德。莊嚴(yán)佛凈土粤咪。 上報(bào)四重恩谚中。下濟(jì)三途苦。 若有見(jiàn)聞?wù)吡戎ΑOぐl(fā)菩提心宪塔。 盡此一報(bào)身。同生極樂(lè)國(guó)囊拜。 南無(wú)大悲觀...
    天牛成長(zhǎng)記閱讀 136評(píng)論 0 1