转一篇Decorator模式的讲解文章
感觉这篇文章讲的挺有意思的,转贴地址:
http://www.cnblogs.com/Blackie/archives/2006/04.html
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; }
编译后的运行结果如下: