装饰模式,通过组合而不是继承的方式,动态地给类增加功能的方法!这句话不好理解,边看下图例子边说话吧:Streams是大多数I / O设备的基础抽象结构,它提供了将对象转换成为字节或字符流的操作接口,使我们可以将一个对象转变成一个文件或内存中的字符串。一个简单直接的方法是定义一个抽象的Stream类,它有两个子类MemoryStream与FileStream。但假定我们还希望能够做下面一些事情(添加功能):
• 用不同的压缩算法(行程编码, Lempel-Ziv等)对数据流进行压缩。
• 将流数据简化为7位A S C I I码字符,这样它就可以在A S C I I信道上传输。
现在来理解上面那句话:如果要用继承的方式完成上面的功能,那基本上要从Stream继承出4个类:Ascii7MemeoryStream, Ascii7FileStream, CompressingMemoryStream, CompressingFileStream。如果再加些别的功能,那么子类的功能不堪设想,其实像这种笛卡尔积种组合方案的设计需求,是可以用某种模式优雅地重构的。
由UML图可知,StreamDecorator类中组合了Stream类指针变量,在构造函数中传参初始化!同时,它也继承了Stream类,这我不太理解,可能是要保持同样的接口吧,因为该模式要求StreamDecorator类和Stream类必须有相同的操作接口,比如上例的HandleBuffeFull(), 这样继承的话省去了重写接口的麻烦?
还有所谓的动态增加功能,也就是在运行时添加,不是像继承那样一下子在编译时就都静态添加进来,导致子类膨胀。这就很容易让人想到C++多态技术!
还有一点就是,为什么Decorator还要派生出一些具体的子类?这是为了对添加功能进行模块化封装:压缩功能用一个子类,转码功能用一个子类!因为是额外添加功能,所以这些子类的HandleBufferFull()函数中除了要调用Stream类的HandleBufferFull()外,还要进行额外的操作。这里是解决问题的重点!形如:void Ascii7Stream::HandleBufferFull()
{
stream->HandleBufferFull();
this->ToAscii7();
}
这里的额外操作函数一般来说只对该子类有意义,因为该子类就是为额外操作而生的嘛,所以它一般可以设为私有!
用户代码怎么使用呢?
Stream * stm = new MemoryStream();
StreamDecorator *dec = new StreamDecorator(stm);
dec->HandleBufferFull();
更妙的是我们可以创建FileStream类,它首先将数据压缩,然后将压缩了的二进制数据转换成为7位ASCII码:
Stream* aStream = new ASCII7Stream ( new CompressingStream( new FileStream("aFileStream") ) );