设计模式 - 装饰模式

来源:互联网 发布:mac上的麻将游戏 编辑:程序博客网 时间:2024/06/05 02:39

动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。

 

结构图

结构图好像和Composite模式有点像。确实是,我们后面再讲Decorator和Composite模式的关系。

从结构图中我们可以看出Decorator的子类可以对Component进行一些扩充。这也就是Decorator模式的作用:对一个现有的类进行扩充来满足更多的需求。

继续使用Composite模式里面的例子。假设我们已经有了CGraphic类和CLine类,现在我们想给CLine类增加一个功能:支持线条阴影。那么应该怎么做呢?有很多办法:

1. 直接修改CLine类,在Draw函数里面增加阴影支持。这不是个好办法,我们违反了OOP设计的一个基本原则:开闭原则(我们修改了CLine类,这个会影响到系统里面其他用到CLine的代码)。

2. 继承CLine类,比如CShadowLine类,这是个常见的办法。但是继承不够灵活,一旦生成了CShowLine对象,这个对象就永远带有阴影。当然这个也不见得是坏处。

3. 采用本文介绍的装饰模式。这就在灵活性方面比第二种方式继承好一些,用户可以动态地控制是否需要阴影。当然也有一定的坏处,这个后面再讲。

那么装饰模式是什么工作的呢。

先给出类图:

例子里面的CGraphic就是Component,CLine是ConcreteComponent,CGraphicDecorator是Decorator。先看看CGraphic和CLine类,跟前文(Composite模式)中一模一样。

[cpp] view plaincopy
  1. class CGraphic  
  2. {  
  3. public:  
  4.     virtual void Add(CGraphic* g){};  
  5.     virtual void Remove(CGraphic* g){};  
  6.   
  7.     virtual void Draw() = 0;  
  8. };  
  9.   
  10. class CLine: public CGraphic  
  11. {  
  12. public:  
  13.     virtual void Draw()  
  14.     {  
  15.         std::cout << "Draw line\n";  
  16.     }  
  17. };  

接下来重点看看Decorator类。

[cpp] view plaincopy
  1. class CGraphicDecorator: public CGraphic  
  2. {  
  3. public:  
  4.     CGraphicDecorator()  
  5.     {  
  6.         _g = NULL;  
  7.     }  
  8.     virtual void Draw()  
  9.     {  
  10.         if (_g)  
  11.         {  
  12.             _g->Draw();  
  13.         }  
  14.     }  
  15.   
  16.     virtual ~CGraphicDecorator()  
  17.     {  
  18.         if (_g)  
  19.         {  
  20.             delete _g;  
  21.             _g = NULL;  
  22.         }  
  23.     }  
  24.   
  25. protected:  
  26.     CGraphic* _g;  
  27. };  

CGraphicDecorator继承了CGraphic类,Decorator类的接口和CGraphic类的其他子类的接口一致。这就是说Decorator对客户是透明的,任何使用CLine或者其他子类的地方都可以使用装饰类来代替。
我们看到Decorator基类只是调用保存在里面的CGraphic对象的Draw函数,也就是转发一下。如果我们用CGraphicDecorator(Decorator基类)来代替一个CGraphic对象g,那么得到的结果跟不装饰没有区别。因为装饰对象没有做任何事情。其实CGraphicDecorator只是Decorator的一个接口,当然我们在里面也可以做一些装饰的事情。但是通常我们是在Decorator的子类里面做装饰。(如果应用环境很简单的话,当然可以用一个Decorator类搞定)

再来看看Decorator的子类CShadowDecorator:

[cpp] view plaincopy
  1. class CShadowDecorator: public CGraphicDecorator  
  2. {  
  3. public:  
  4.     CShadowDecorator(CGraphic* g)  
  5.     {  
  6.         _g = g;  
  7.     }  
  8.   
  9.     virtual void Draw()  
  10.     {  
  11.         DrawShadow();  
  12.         CGraphicDecorator::Draw();  
  13.     }  
  14. protected:  
  15.     virtual void DrawShadow()  
  16.     {  
  17.         std::cout <<"CShadowDecorator, Draw shadow\n";  
  18.     }  
  19. };  

这个子类进行了一些装饰工作,这里包括2部分意思:

1. 用传递进来的CGraphic对象(被装饰者)来调用Draw函数,也就是说调用被装饰者的功能;

2. 调用装饰函数DrawShadow,这是个新函数,用来实现阴影效果。

也就是说,我们通过一个装饰类给CLine类扩展了阴影功能。客户端怎么调用呢?我们假设CLine类有个客户,叫做CMyWindow,CMyWindow会使用CGraphic接口在窗口上面画直线。代码如下:就是简单的调用CGraphic的Draw函数。(当然实际代码没这么简单,这里只是突出模式的应用,而省去了其他的代码,比如传递窗口句柄啊,HDC啊等等)

[cpp] view plaincopy
  1. class CMyWindow  
  2. {  
  3. public:  
  4.     void AddGraphic(CGraphic* g)  
  5.     {  
  6.         g->Draw();  
  7.     }  
  8. };  

ok,假如我们要在一个窗口对象上面画一条直线,就这么调用:

[cpp] view plaincopy
  1. CGraphic* g = new CLine();  
  2.     CMyWindow* win = new CMyWindow();  
  3.     win ->AddGraphic(g);  

这样就画了一条直线。那么现在要画带阴影效果的直线,该怎么调用呢?很简单,看下面的代码:

[cpp] view plaincopy
  1. CGraphic* g = new CLine();  
  2.     CGraphic* shadow = new CShadowDecorator(g);  
  3.   
  4.     CMyWindow* win = new CMyWindow();  
  5.     win ->AddGraphic(shadow);  

用CShadowDecorator对CLine对象g进行装饰(增加阴影),然后把shadow传递给CMyWindow对象,这样就在CMyWindow对象上面画出了阴影直线。我们可以很方便地将CLine对象替换成CGraphicDecorator对象,这是因为CGraphicDecorator和CLine一样从CGraphic继承下来,它们具有一致的接口。
 

装饰模式的优点:

1. 比静态继承更加灵活,Decorator可以动态地添加对象的职责,而且一个Decorator可以为多个CGraphic子类添加职责,上面的例子我们会CLine添加了阴影,当然我们也可以为CText添加阴影,所要做的就是把CText对象作为参数传给CShadowDecorator,从而使得CShadowDecorator可以为CText对象进行装饰。如果用继承的话,那么就得分别给CLine和CText创建2个子类了。而且我们还可以对一个Decorator对象进行装饰,比如创建一个CColorDecorator类,然后把CShadowDecorator对象传给CColorDecorator,那么就可以同时画出阴影和颜色效果。也就是说我们可以使用不同的Decorator类来组合出不同的效果。

2. 避免在层次结构高层的类有太多的特征。我们可以在Decorator类里面慢慢地添加。比如CGraphic类可以很简单,然后CGraphicDecorator以及它的子类里面慢慢的进行装饰以获得更多的功能。


 缺点:

会有许多小对象,比如阴影装饰,颜色装饰等等。

 

通常来说,Decorator可以看成一个对象的外壳。Decorator包装了一个对象,并且进行一些装饰(扩展功能)。也就是说Decorator并没有改变对象的内核,只是通过外壳装饰来改变一个对象的行为。这个和Strategy模式有着根本的区别,Strategy模式是改变对象的内核来改变对象的行为。Strategy模式在后面会讲到。

 

 相关模式:

1. Adapter, Decorator模式只是改变了接口的职责而没有改变接口,比如CLine的Draw函数只是画一条直线,然后CShadowDecorator的Draw函数画一条带阴影的直线。CLine和CShadowDecorator都是用同一个接口Draw()。而Adapter模式是给对象一个全新的接口。

2. Composite, 本文的前面我们讲到了Decorator和Composite有点像,有什么关系呢?我们可以把Decorator看成是一种退化了的Composite。这个Composite只有一个组件(Decorator里面只保存了一个组件对象,而Composite保存了一系列组件,看Composite的代码就可以看到里面放了一个list或者其他的collection)。当然Decorator的目的和Composite也有着本质的区别,Decorator只是给对象添加一些额外的职责,而Composite着重于对象的聚集,比如通过一些小对象来生成一个大对象。

3. Strategy,用一个装饰来改变一个对象的外表,用一个策略来改变一个对象的内核。这是改变对象的两种途径。


转载:http://blog.csdn.net/zj510/article/details/8136773

原创粉丝点击