所謂的設(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)如下圖:
-
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)善镰。