考虑程序要对一类 流 (网络流,IO流等等)进行操作。进行什么操作呢?可能在读(Read)这个流的时候对这个流进行加密,也可能对这个流进行缓存。
那么很自然的能设计出以下这些类
class Stream { public: void Read(); }; class IOStream : public Stream {}; // 输入输出流 class NetStream: public Stream {}; // 网络流 class BufferedIOStream : public IOStream { public: void Read() { // 实现了缓存功能的读操作 Buffer(); IOStream::Read(); } void Buffer() {} }; class EncryptIOStream : public IOStream { public: void Read() { // 实现了加密功能的读操作 Encrypt(); IOStream::Read(); } void Encrypt(); } // 实现加密和缓存功能的输入输出流 class BufferedEncryptIOStream : public EncryptIOStream, public BufferedIOStream { public: void Read() { EncryptIOStream::Encrypt(); BufferedIOStream::Read(); } }; // 网络流同理 class BufferedNetStream : public NetStream ......
那么这样做的缺点有什么呢?试想一下,如果我现在要对流增加新的操作,比如说要将流输出。你可能会有如下设计
class PrintIOStream : public IOStream { public: void Read() { Print(); IOStream::Read(); } void Print(); } class BufferedPrintIOStream : public BufferedIOStream, public PrintIOStream { // 既可以加密有可以缓存的流 public: void Read() { PrintIOStream::Print(); BufferedIOStream::Read(); } }; class EncryptPrintIOStream : public EncryptIOStream, public PrintIOStream {}; // 既可以加密有可以打印的IO流 class BufferedEncryptPrintIOStream : public BufferedPrintIOStream, public EncryptPrintIOStream {};
哇靠,有没有搞错!只不过是要求增加了一个新的打印的功能,结果多出了这么多的类。如果继续增加新功能的话,类的数量之多可想而知。这种现象被称作类爆炸。
当然了,你也可以说只设计一个BufferedEncryptPrintIOStream。但是假如你某个时刻不需要打印功能的话,那么打印功能就是累赘,这显然违背了接口隔离原则
这时候就需要一种更好的设计方法----装饰者模式
试想一下,如果我们通过组合的方式来设计类会不会有上述问题呢?答案是不会。
// 装饰者模式一瞥 class BufferedStream : public Stream { public: BufferedStream(Stream* stm) : stream(stm) {} void Read() {
Buffer();
myStream->Read();
}
void Buffer(); private: Stream* myStream; };
嗯?这个类怎么这么奇怪,又是继承了Stream,里面又包含了一个Stream。这其实就是装饰者模式的精髓。之所以继承是因为BufferedStream首先是个流,它可能有一些Stream通用的性质。而为什么它的成员函数也包含个Stream对象?请注意,这里的Stream是一个指针,对c++敏感的朋友可能已经意识到了指针意味着可以动态绑定。那么我们看看如何使用这个BufferedStream以实现上述的各种功能
// 如果我们想要一个缓冲的IO流 IOStream* stm = new IOStream(); BufferedStream* bufferedStm = new BufferedStream(stm); bufferedStm->Read(); // 如果我们要一个缓冲的加密的IO流 EncryptStream* encryptStream = new EncryptStram(new IOStream); BufferedStream* bufEncStream = new BufferedStream(encryptStream); bufEncStream->Read();
是不是觉得很神奇?关键点在于对流的层层包装,比如说先把流包装成可打印的流,那么它就能实现打印的功能了。在这个基础上把它包装成缓存流,那么它既有缓存功能有有打印功能。如果需要更多的功能怎么办?继续包装就是了。这样子就无需为新的功能拓展类,从而避免了类爆炸的问题
以上就是装饰者模式,接下来给出装饰者模式的完整设计代码
class Stream { public: void Read(); }; class IOStream : public Stream {}; class PrintStream : Stream { public: PrintStream(Stream* stm) : myStream(stm) {} void Read() { Print(); myStream->Read(); } void Print() {} private: Stream* myStream; }; class BufferedStream : Stream { public: BufferedStream(Stream* stm) : myStream(stm) {} void Read() { Buffer(); myStream->Read(); } void Buffer(); private: Stream* myStream; }; class EncryptStream : Stream { public: EncryptStream(Stream* stm) : myStream(stm) {} void Read() { Encrypt(); myStream->Read(); } void Encrypt(); private: Stream* myStream; } // 只需这几个类就能完成所有需要的功能,接下来看看使用方法 IOStream* iostm = new IOStream(); BufferedStream* bufferedPrintEncryptStream = new BufferedStream(new PrintStream(new EncryptStream(iostm)));
更近一步。细心的朋友可能已经发现了三个类中都有一个myStream成员,那么能优化的是将myStream放在一个新的类中,然后让这三个类继承这个新的类
class MyStream : Stream {
public:
MyStream(Stream* stm) : myStream(stm) {}
private:
Stream* myStream;
}
以上就是装饰者模式的所有内容