仍以《大話設計模式》一書中裝飾模式的小菜穿衣的例子爲例,來看看裝飾模式是如何做到家裏紅旗不倒,外面彩旗飄飄的。小菜要去會妹子,臨行前爲了給妹子留個好印象,精選牛仔褲一條,然後搭上簡約風T恤,裝扮一新後出門,爲了能夠讓小菜搭配任何衣服,使用裝飾模式描述這一過程。
1、穿衣基類,只有一個函數Show(),顯示穿的衣服
class CDress
{
public:
virtual ~CDress() {}
virtual void Show()
{
printf("dressed boy.\n");
}
};
2、裝飾類基類class CFinery : public CDress
{
public:
CFinery() : m_poDress(NULL) {}
virtual ~CFinery() {}
virtual void Show()
{
if (m_poDress)
{
m_poDress->Show();
}
}
void Decorate(CDress* poCDress)
{
m_poDress = poCDress;
}
private:
CDress* m_poDress;
};
3、T恤和牛仔褲的具體裝飾類class CTShirt : public CFinery
{
public:
virtual ~CTShirt() {}
void Show()
{
printf("Tshirt ");
CFinery::Show();
}
};
class CJeans : public CFinery
{
public:
virtual ~CJeans() {}
void Show()
{
printf("Jeans ");
CFinery::Show();
}
};
這裏需注意,裝飾類對象的Show()函數在顯示出本對象的裝扮的同時,需要去調用裝飾基類的Show()函數,以顯示其被裝飾對象的舊有裝扮,即不能忘了老朋友。
4、裝飾過程int main(int argc, char* argv[])
{
CDress oCDress;
CTShirt oCTShirt;
CJeans oCJeans;
oCTShirt.Decorate(&oCDress);
oCJeans.Decorate(&oCTShirt);
oCJeans.Show();
return 0;
}
通過裝飾過程可以看出,oCJeans 對象裝飾了 oCTShirt 對象,所以 oCJeans 調用 Show() 函數時會調用 oCTShirt 的 Show() 函數,oCTShirt 又裝飾了 oCDress 對象,這時 oCTShirt 又會調用 oCDress 的 Show() 函數,有點類似遞歸,也有點類似鏈表的味道,當然我們知道遞歸需要有一個終結者的,不然就沒完沒了了,所以最後被裝飾的對象 oCDress 是不在有任何裝飾對象的。
同時,如果小菜的妹子不喜歡這套打扮風格了,說要小菜把T恤換成襯衫,這時我們只要新增一個襯衫裝飾類 CShirt ,然後把 oCJeans 的裝飾對象換成 CShirt 即可,這裏可見,使用了裝飾模式後,換衣服都方便靈活多了。
5、裝飾模式的應用裝飾模式適合在原有功能上增加了新功能,但是新功能被調用前/後仍需要調用原有功能的情況,特別適合功能一層一層的擴展,同時保持舊有功能的正常調用的場景。比如我們設計一個編輯框的控件類,最初只有編輯文字的功能,我們新增一個裝飾類後,我們能編輯出彩色的文字,這時我們只需要設置控件的背景顏色,然後去調用舊的對象編輯文字功能即可。過一段時間老大提新要求了,要求這個編輯框控件要搞和諧社會,要屏蔽敏感詞,這時我們再新增一個裝飾類,增加建設和諧社會功能後,再去調用編輯彩色文字的裝飾類。當然,我們還能靈活變動,搞和諧社會的裝飾類爲了不花哨,也可以直接裝飾原來的編輯框控件類,拋棄中間搞彩色文字的過程。