Decorator模式
一 意图
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。——包装器Wrapper。
二 动机
有时我们希望给某个对象而不是整个类添加一些功能。
例如,一个图形用户界面工具箱允许你对任意一个用户界面组件添加一些特性,例如边框,或是一些行为,例如窗口滚动。
使用继承机制是添加功能的一种有效途径,从其他类继承过来的边框特性可以被多个子类的实例所使用。
但这种方法不够灵活,因为边框的选择是静态的,用户不能控制对组件加边框的方式和时机。
一种较为灵活的方式是将组件嵌入另一个对象中,由这个对象添加边框。我们称这个嵌入的对象为装饰。
这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。它将客户请求转发给该组件,并且
可能在转发前后执行一些额外的动作(例如画一个边框)。
透明性使得你可以递归的嵌套多个装饰,从而可以添加任意多的功能,如下图所示。
这样一种结构:
VisualComponent是一个描述可视对象的抽象类,它定义了绘制和事件处理的接口。
注意Decorator类怎样将绘制请求简单地发送给它的组件,以及Decorator的子类如何扩展这个操作。
Decorator的子类为特定功能可以自由地添加一些操作。
例如,如果其他对象知道界面中恰好有一个ScrollDecorator对象,这些对象就可以用ScrollDecorator对象的ScrollTo操作滚动这个界面。
这个模式中有一点很重要,它使得在VisualComponent可以出现的任何地方都可以有装饰。
因此,客户通常不会感觉到装饰过的组件与未装饰组件之间的差异,也不会与装饰产生任何依赖关系。
Decorator中存在一个Component的对象,来接收所要装饰的组件——被装饰。
继承结构来实现:
或者组合的方式:
对于这种情况,这三种方式中:我觉得使用Deccorator或许并不合适,Decorator让对象被装饰,
将对象传递给可以装饰的装饰对象,得到的将是一个被装饰后的对象。不是对象本身。
虽然可以通过Decorator传递请求,但势必让事情变得更麻烦。故通常我们仍然需要操作的是原对象本身。
这样一来需要维护两个对象……麻烦! Why???
三 适用性以及其结构
以下情况使用Decorator模式
1.在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2 处理那些可以撤消的职责。
3.当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,
为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
结构:
Component ( VisualComponent )
定义一个对象接口,可以给这些对象动态地添加职责。
• ConcreteComponent ( TextView )
定义一个对象,可以给这个对象添加一些职责。
• Decorator
维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。
• ConcreteDecorator(BoaderDecorator,ScrollDecorator)
向组件添加职责。
•Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加的动作。
四 效果分析
1 比静态继承更灵活
可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。
而继承机制要求为每个添加的职责创建一个新的子类。更灵活的添加装饰:
使用Decorator模式可以很容易地重复添加一个特性,例如在TextView上添加双边框时,
仅需将添加两个BoarderDecorator即可。而两次继承Boarder类则极容易出错的。
2 避免在层次结构高层的类有太多的特征
Decorator模式提供了一种“即用即付”的方法来添加职责。可以通过定制来逐步的构造复杂的对象。
五 代码实现
1 Decorator
#include <iostream.h> #include <memory.h> /*----------------------------------------------------------------*/ /* class VisualComponent */ /*----------------------------------------------------------------*/ class VisualComponent { public: VisualComponent(){} virtual void Draw() = 0; }; /*----------------------------------------------------------------*/ /* class TextView */ /*----------------------------------------------------------------*/ class TextViewControl : public VisualComponent { #define CONTENT_LEN 100 public: TextViewControl(char* str, int len) { memset(m_content,0,CONTENT_LEN); memcpy(m_content,str,len); } virtual void Draw() { cout<<" TextViewControl Draw content = "<<m_content<<endl; } private: char m_content[CONTENT_LEN]; }; /*----------------------------------------------------------------*/ /* class Decorator */ /*----------------------------------------------------------------*/ class Decorator: public VisualComponent { public: Decorator(VisualComponent* component) { m_componnet = component; } virtual void Draw() { m_componnet->Draw(); cout<<" I am Decorator Draw"<<endl; } private: VisualComponent* m_componnet; }; /*----------------------------------------------------------------*/ /* class BoarderDecorator */ /*----------------------------------------------------------------*/ class BoarderDecorator: public Decorator { public: BoarderDecorator(VisualComponent* component, int boarderWidth):Decorator(component) { m_boarderWidth = boarderWidth; } virtual void Draw() { Decorator::Draw(); cout<<" I am BoarderDecorator Draw"<<endl; this->DrawBorder(); } protected: void DrawBorder() { cout<<" DrawBorder m_boarderWidth = "<<m_boarderWidth<<endl; } private: int m_boarderWidth; }; /*----------------------------------------------------------------*/ /* class ScrollDecorator */ /*----------------------------------------------------------------*/ class ScrollDecorator: public Decorator { public: ScrollDecorator(VisualComponent* component, int scrollHeight):Decorator(component) { m_scrollHeight = scrollHeight; } virtual void Draw() { Decorator::Draw(); cout<<" I am ScrollDecorator "<<endl; this->DrawScroll(); } protected: void DrawScroll() { cout<<" DrawScroll m_scrollHeight = "<<m_scrollHeight<<endl; } private: int m_scrollHeight; };
2 Test
#include "Decorator.h" #include <string.h> int main() { char str[] = "TextViewControl"; TextViewControl* textView = new TextViewControl(str,strlen(str)); ScrollDecorator* scroll = new ScrollDecorator(textView,100); BoarderDecorator* board = new BoarderDecorator(scroll,200); board->Draw(); return 0; }
3 Result
TextViewControl Draw con I am Decorator I am ScrollDecorator m_scrollHeight = 100 I am Decorator I am BoarderDecorator m_boarderWidth = 200
六 实例分析
Decorator装饰或者Wrapper包装这个些词太容易让人想到的是图形用户界面,这些控件方面的应用了。但这仅仅只是一种形象的表达而已,不局限于此。
1 For Example:
Streams是大多数I / O设备的基础抽象结构,它提供了将对象转换成为字节或字符流的操作接口,
使我们可以将一个对象转变成一个文件或内存中的字符串,可以在以后恢复使用。一个简单直接的
方法是定义一个抽象的Stream类,它有两个子类MemoryStream与FileStream。但假定我们还希望能够做下面一些事情:
• 用不同的压缩算法(行程编码, Lempel-Ziv等)对数据流进行压缩。
• 将流数据简化为7位A S C I I码字符,这样它就可以在A S C I I信道上传输。
Decorator模式提供的将这些功能添加到Stream中方法很巧妙。下面的类图给出了一个解决问题的方法。
Stream抽象类维持了一个内部缓冲区并提供一些操作( PutInt, PutString)用于将数据存入流中。
一旦这个缓冲区满了,Stream就会调用抽象操作HandleBufferFull进行实际数据传输。
在FileStream中重定义了这个操作,将缓冲区中的数据传输到文件中去。
这里的关键类是StreamDecorator,它维持了一个指向组件流的指针并将请求转发给它,
StreamDecorator子类重定义HandleBufferFull 操作并且在调用StreamDecorator的HandleBufferFull操作之前执行一些额外的动作。
例如,CompressingStream子类用于压缩数据,而ASCII7Stream将数据转换成7位ASCII码。
现在我们创建FileStream类,它首先将数据压缩,然后将压缩了的二进制数据转换成为7位ASCII码,
我们用CompressingStream和ASCII7Stream装饰FileStream:
Stream* aStream = new ASCII7Stream ( new CompressingStream ( new FileStream("aFileStream") ) ); aStream->PutInt(12); aStream->PutString("aString");
2 相关模式:
- Adapter模式:Decorator模式不同于Adapter模式,因为装饰仅改变对象的职责而不改变它的接口;而适配器将给对象一个全新的接口。
- Composite模式:可以将装饰视为一个退化的、仅有一个组件的组合。然而,装饰仅给对象添加一些额外的职责—它的目的不在于对象聚集。
- Strategy模式:用一个装饰你可以改变对象的外表;而Strategy模式使得你可以改变对象的内核。这是改变对象的两种途径。
通过几个例子到这里之后,我一直在慢慢觉得,这个模式不错感觉很好!
竟然有一种无法表达的感觉……他能够把你想干的几件相关的事情分开来处理,让事情变得清晰明了。。。
评论