DECORATOR模式———小軒窗,正梳妝
[junguo](#)
DECORATOR中文的意思是裝飾,該模式的動機是幫助對象動態的添加一些功能。它強調是為對象而不是為類添加功能。為類添加功能最有效的方式是通過繼承來實現,但繼承的缺點是不夠靈活。下面我們還是通過例子來理解該模式。
???十年生死兩茫茫,不思量,自難忘。
???千里孤墳,無處話凄涼。
???縱使相逢應不識,塵滿面,鬢如霜。
???
???夜來幽夢忽還鄉,小軒窗,正梳妝。
???相顧無言,惟有淚千行。
???料的年年斷腸處,明月夜,短松岡。
???
挺喜歡這首詞的,是我能背下來的不多的幾首詞中的一首。這是蘇東坡為亡妻王弗寫的一首悼亡詩。王弗十六歲時嫁給蘇軾,婚后夫妻恩愛。蘇軾的《亡妻墓志銘》中寫到:“見軾讀書,則終日不去。”頗有“紅袖添香夜伴讀”之味。可惜正如蘇軾的《明月幾時有》中寫的一樣:“人有悲歡離合,月有陰晴圓缺,此事古難全。”王氏于二十七歲時病逝,蘇軾悲痛萬分。十年后的一個夜晚,蘇軾又一次在夢中夢到了與妻子往日的纏綿,醒來后不禁淚下,寫下了這首詞。
前兩天,和一兄弟網上聊天。他說自己陷入情網,無法自拔,痛苦萬分。問他什么原因?他說自己愛上了一個女孩,但人家已經有了男朋友;看著年齡越來越大,找到自己合適的伴侶是越來越難。為了給他雪上加霜,我勸他死心吧,找到合適的概率太小;不如等到那天混的可以了,有房子后,發個征婚啟示什么的,然后就可以直接結婚了。他堅決的拒絕了我給他的友好建議,說看到很多結婚的朋友,為了雞毛蒜皮的小事而吵架,一點意思都沒有;與其這樣,還不如自己一個人。是啊,隨著工業文明的進步,人的神經也被不斷的拉緊,少了農耕時代的悠閑。我們的生活越來越匆忙,少了欣賞美的情趣,生活中的點點滴滴的美,有多少人可以體驗得到?“小軒窗,正梳妝”,古人可以體會得到的美,而在我們的字典里能找到嗎?為了些須小事而吵架分離,婚姻不再是“圍城”,令城外的我們也忘而卻步。很多人都在等待,等待那份不會因物質而庸俗化的愛情,能等到嗎?“但愿人長久,千里共嬋娟”,將此祝福天下所有期望這份愛情的人。
好了,我們來描述我們的例子吧。考慮一下美女梳妝的情景,她會盤弄自己的頭發,填加首飾在自己頭上,可能還有耳環一類的東西。但每個美女身上的飾物并不相同,我們為美女提供一個類的話,如何可以做到讓她們的飾物各不相同呢?好的,我們還是一步一步來,首先抽象出一個美女類:

這個類比較簡單,就是畫一個美女出來(不好意思,這里仍然是用文字意思一下,不熟悉圖象處理;真希望能真正畫一個出來,什么時候有時間好好學學)。我們再來看如何幫美女填加飾物,仍是先看傳統的方式:

那么需要填加這樣的變量m_IsHasHair(什么,美女可以沒有頭發?當然可以,不信可以找金庸的《笑傲江湖》來看看),m_IsHasNeaklace(項鏈),m_IsEarbob(耳環)。那么我們的Draw的實現就需要根據不同的條件來實現,大概的代碼如下:
~~~
Void Draw()
{
……畫一個美女
If( m_IsHasHair)
{
…….
}
If ( m_IsHasNeakLace)
{
……..
}
If ( m_IsEarbob)
{
……..
}
}
~~~
還是結構化的東西,我們需要考慮的問題是:當新的飾物需要填加到類中的時候,我們該如何處理?再次強調面向對象的基本原則:一個模塊對擴展應該是開放的,對修改應該是關閉的。如果按現在的做法,我們不得不再次打開Draw為它添加新的條件,實現新的功能。這個方案被否決,那么我優先考慮到的還是繼承,看它能不能完成我們的功能?
如上類圖,我們試圖使用繼承的方法來實現該功能。當新加裝飾類型的時候,是可以做到不需改變以前的代碼。但新的問題又來,當一個美女既有頭發又有項鏈的時候,我們該怎么辦。那么通過繼承的方法就是新加一個類:BeautyWithHairAndNeakLace,如果一個一個條件組合下去,類又失去了控制,又是類爆炸現象。簡單的繼承無法完成我們的功能。還好,如果你知道了Decorator模式,問題就會變的簡單起來。我們首先來看看Decorator模式的類圖。
對該類圖,做一些說明:
???1, 我們需要為我們要用到的類和它所需要的包裝類提供一個共同的接口:Component。
???2, ConcreteComponent是我們要用到的類,就是需要為該類的對象動態的填加一些功能進去。
???3, Decorator是我們要用到的包裝類,它也應該是一個抽象類,我們是通過它的子類來實現對ConcreteComponent的包裝。每個Decorator子類的實例都應該擁有一個指向Component的指針或引用。
???4, ConcreteDecoratorA和ConcreteDecoratorB是我們用來包裝的實類,它擁有一個指向Component的實例。我們可以在該類中填加新的類型和方法,但這些類型和方法只能在類內部使用,因為該類的調用是通過Component接口來實現的。
好了,看完該類圖,你是否對該模式還是不清楚?沒關系,看到實際的代碼后,一切就清晰了。我們再按該模式來設計我們的美女類圖:
幫我們的美女抽象出一個接口:BeautyInterface。Beauty繼承自BeautyInterface是我們實際要用到的類。而BeautyDecorator是我們抽象出的裝飾類。Hair,Necklace,Earbob是要具體用來裝飾的類,可以看到他們重載了Draw,調用接口的Draw并且填加一些自己的功能進去。具體該如何用呢?來看代碼。
~~~
//美女接口
class BeautyInterface
{
public:
virtual void Draw() = 0;
};
//美女實類
class Beauty : public BeautyInterface
{
public:
void Draw(){ cout << "This is a beauty!" << endl;}
};
//美女裝飾類
class BeautyDecorator : public BeautyInterface
{
public:
virtual void Draw() = 0;
};
//頭發類
class Hair : public BeautyDecorator
{
private:
BeautyInterface *m_pBeauty;
public:
Hair(BeautyInterface *pBeauty):m_pBeauty(pBeauty){}
void Draw()
{
m_pBeauty->Draw();
DrawHair();
}
private:
void DrawHair()
{
cout << "Hi,this is my beautiful hair!" << endl;
}
};
//項鏈類
class Neaklace : public BeautyDecorator
{
private:
BeautyInterface *m_pBeauty;
public:
Neaklace(BeautyInterface *pBeauty):m_pBeauty(pBeauty){}
void Draw()
{
m_pBeauty->Draw();
DrawNeaklace();
}
private:
void DrawNeaklace()
{
cout << "Hi,look at my neaklace!" << endl;
}
};
//耳環類
class Earbob : public BeautyDecorator
{
private:
BeautyInterface *m_pBeauty;
public:
Earbob(BeautyInterface *pBeauty):m_pBeauty(pBeauty){}
void Draw()
{
m_pBeauty->Draw();
DrawEarbob();
}
private:
void DrawEarbob()
{
cout << "Hi,look at my Earbob!" << endl;
}
};
~~~
好了,這就是我們要實現的類代碼。我們需要注意的是Hair等裝飾類的構造函數,需要初始化一個BeautyInterface 接口指針。
Hair(BeautyInterface *pBeauty):m_pBeauty(pBeauty){}
我們再來看看該類的具體應用:
~~~
int main(int argc, char* argv[])
{
BeautyInterface *pBeauty = new Earbob(new Neaklace(new Hair(new Beauty)));
pBeauty->Draw();
delete pBeauty;
return 0;
}
~~~
編譯后的運行結果如下:
這就是DECORATOR模式的具體使用了,下次我們接著聊COMPOSITE模式。
參考書目:
1, 設計模式——可復用面向對象軟件的基礎(Design Patterns ——Elements of Reusable Object-Oriented Software) Erich Gamma 等著 李英軍等譯 機械工業出版社
2, Head First Design Patterns(影印版)Freeman等著 東南大學出版社
3, 道法自然——面向對象實踐指南 王詠武 王詠剛著 電子工業出版社