“单一职责”模式
- 在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
- 典型目录
- 装饰器模式 Decorator
- 桥接模式 Bridge
装饰器模式 Decorator
引例
- 对于流操作,诸如文件流、网络流、内存流等,分别对其进行诸如加密、缓存等单个或组合的操作,子类庞大。
// 业务操作 class Stream { public: virtual char Read(int number) = 0; virtual char Seek(int position) = 0; virtual char Write(char data) = 0; virtual ~Stream(){} }; // 主体类 // 文件流 class FileStream : public Stream { public: virtual char Read(int number) { // 读文件流 } virtual char Seek(int position) { // 定位文件流 } virtual char Write(char data) { // 写文件流 } }; // 网络流 class NetworkStream : public Stream { public: virtual char Read(int number) { // 读网络流 } virtual char Seek(int position) { // 定位网络流 } virtual char Write(char data) { // 写网络流 } }; // 内存流 class MemoryStream : public Stream { public: virtual char Read(int number) { // 读内存流 } virtual char Seek(int position) { // 定位内存流 } virtual char Write(char data) { // 写内存流 } }; //扩展操作,如加密、缓存、加密缓存 // 加密文件流 class CryptoFileStream : public FileStream { public: virtual char Read(int number) { // 额外的加密操作 FileStream::Read(number); // 读文件流,这种为静态特质 } virtual char Seek(int position) { // 额外的加密操作 FileStream::Seek(position); // 定位文件流 } virtual char Write(char data) { // 额外的加密操作 FileStream::write(data);// 写文件流 } }; // 加密网络流 class CryptoNetworkStream : public NetworkStream { public: virtual char Read(int number) { // 额外的加密操作 NetworkStream ::Read(number); // 读网络流 } virtual char Seek(int position) { // 额外的加密操作 NetworkStream ::Seek(position); // 定位网络流 } virtual char Write(char data) { // 额外的加密操作 NetworkStream ::write(data);// 写网络流 } }; // 缓存文件流 class BufferedFileStream : public FileStream { public: virtual char Read(int number) { // 额外的缓存操作 FileStream::Read(number); // 读文件流 } virtual char Seek(int position) { // 额外的缓存操作 FileStream::Seek(position); // 定位文件流 } virtual char Write(char data) { // 额外的缓存操作 FileStream::write(data);// 写文件流 } }; // 缓存网络流 class BufferedNetworkStream : public NetworkStream { public: virtual char Read(int number) { // 额外的缓存操作 NetworkStream ::Read(number); // 读网络流 } virtual char Seek(int position) { // 额外的缓存操作 NetworkStream ::Seek(position); // 定位网络流 } virtual char Write(char data) { // 额外的缓存操作 NetworkStream ::write(data);// 写网络流 } }; // 加密缓存文件流 class CryptoBufferedFileStream : public FileStream { public: virtual char Read(int number) { // 额外的加密操作 // 额外的缓存操作 FileStream::Read(number); // 读文件流 } virtual char Seek(int position) { // 额外的加密操作 // 额外的缓存操作 FileStream::Seek(position); // 定位文件流 } virtual char Write(char data) { // 额外的加密操作 // 额外的缓存操作 FileStream::write(data);// 写文件流 } }; // 加密网络流 class CryptoBufferedNetworkStream : public NetworkStream { public: virtual char Read(int number) { // 额外的加密操作 // 额外的缓存操作 NetworkStream ::Read(number); // 读网络流 } virtual char Seek(int position) { // 额外的加密操作 // 额外的缓存操作 NetworkStream ::Seek(position); // 定位网络流 } virtual char Write(char data) { // 额外的加密操作 // 额外的缓存操作 NetworkStream ::write(data);// 写网络流 } }; void Process() { // 编译时装配 CryptoFileStream *cfs1 = new CryptoFileStream(); BufferedFileStream* bfs1 = new BufferedFileStream(); CryptoBufferedFileStream* cbfs1 = new CryptoBufferedFileStream(); }
如第二层m种变化类型,第三层n种变化类型,类的个数 :1+ m + m * n!/ 2
对第一版代码进行重构
// 业务操作 class Stream { public: virtual char Read(int number) = 0; virtual char Seek(int position) = 0; virtual char Write(char data) = 0; virtual ~Stream(){} }; // 主体类 // 文件流 class FileStream : public Stream { public: virtual char Read(int number) { // 读文件流 } virtual char Seek(int position) { // 定位文件流 } virtual char Write(char data) { // 写文件流 } }; // 网络流 class NetworkStream : public Stream { public: virtual char Read(int number) { // 读网络流 } virtual char Seek(int position) { // 定位网络流 } virtual char Write(char data) { // 写网络流 } }; // 内存流 class MemoryStream : public Stream { public: virtual char Read(int number) { // 读内存流 } virtual char Seek(int position) { // 定位内存流 } virtual char Write(char data) { // 写内存流 } }; //扩展操作,如加密、缓存、加密缓存 // 加密文件流 class CryptoStream : public Stream { // FileStream* stream; 不必现在确定类型 Stream* stream;// = new FileStream(); 未来确实,即运行时确定 public: CryptoStream(Stream* st) : stream(st) { } virtual char Read(int number) { // 额外的加密操作 stream->Read(number); // 读流 } virtual char Seek(int position) { // 额外的加密操作 stream->Seek(position); // 定位流 } virtual char Write(char data) { // 额外的加密操作 stream->write(data);// 写流 } }; // 加密网络流 class CryptoNetworkStream : public Stream { // NetworkStream * stream; 不必现在确定类型 Stream* stream;// = new NetworkStream (); 未来确实,即运行时确定 virtual char Read(int number) { // 额外的加密操作 stream->Read(number); // 读网络流 } virtual char Seek(int position) { // 额外的加密操作 stream->Seek(position); // 定位网络流 } virtual char Write(char data) { // 额外的加密操作 stream->write(data);// 写网络流 } }; // 加密文件流/网络流,合并为加密流CryptoStream,结构如下方缓存流 // 缓存流 class BufferedStream : public Stream { Stream* stream; public: BufferedStream(Stream* st) : stream(st) { } virtual char Read(int number) { // 额外的加密操作 stream->Read(number); // 读流 } virtual char Seek(int position) { // 额外的加密操作 stream->Seek(position); // 定位流 } virtual char Write(char data) { // 额外的加密操作 stream->write(data);// 写流 } }; void Process() { // BufferedFileStream* bfs1 = new BufferedFileStream(); // 代码1 // CryptoBufferedFileStream* cbfs1 = new CryptoBufferedFileStream(); // 代码1 // 运行时装配 FileStream* fs1 = new FileSteam(); CryptoStream* cfs1 = new CryptoStream(fs1); BufferedStream* bfs1 = new BufferedStream(fs1); CryptoStream* cbfs1 = new CryptoStream(bfs1); }
对第二部分代码,继续重构,将共有部分加入到装饰类:DecoratorStream
// 业务操作 class Stream { public: virtual char Read(int number) = 0; virtual char Seek(int position) = 0; virtual char Write(char data) = 0; virtual ~Stream(){} }; // 主体类 // 文件流 class FileStream : public Stream { public: virtual char Read(int number) { // 读文件流 } virtual char Seek(int position) { // 定位文件流 } virtual char Write(char data) { // 写文件流 } }; // 网络流 class NetworkStream : public Stream { public: virtual char Read(int number) { // 读网络流 } virtual char Seek(int position) { // 定位网络流 } virtual char Write(char data) { // 写网络流 } }; // 内存流 class MemoryStream : public Stream { public: virtual char Read(int number) { // 读内存流 } virtual char Seek(int position) { // 定位内存流 } virtual char Write(char data) { // 写内存流 } }; //扩展操作,如加密、缓存、加密缓存 DecoratorStream : public Stream { protected: Stream* stream; DecoratorStream(Stream* st) : stream(st) { } } // 加密文件流 class CryptoStream : public DecoratorStream { public: CryptoStream(Stream* st) : DecoratorStream(st) { } virtual char Read(int number) { // 额外的加密操作 stream->Read(number); // 读流 } virtual char Seek(int position) { // 额外的加密操作 stream->Seek(position); // 定位流 } virtual char Write(char data) { // 额外的加密操作 stream->write(data);// 写流 } }; // 缓存流 class BufferedStream : public DecoratorStream { public: BufferedStream(Stream* st) : stream(st) { } virtual char Read(int number) { // 额外的加密操作 stream->Read(number); // 读流 } virtual char Seek(int position) { // 额外的加密操作 stream->Seek(position); // 定位流 } virtual char Write(char data) { // 额外的加密操作 stream->write(data);// 写流 } }; void Process() { // 运行时装配 FileStream* fs1 = new FileSteam(); CryptoStream* cfs1 = new CryptoStream(fs1); BufferedStream* bfs1 = new BufferedStream(fs1); CryptoStream* cbfs1 = new CryptoStream(bfs1); }
类的个数: 1 + m + 1 + n,使用组合(装饰器类中含有被修饰类的指针),来减少类的数量。
动机 Motivation
- 在某些情况下,我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
模式定义
动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码及减少子类个数)。
结构 Structure
红色为稳定部分,蓝色为变化部分。
要点总结
- 通过采用组合而非继承的手法,装饰器模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了使用继承带来的“灵活性差”和“多子类衍生”问题。
- Decorator 装饰器类在接口上表现为 is-a Component 的继承关系,即Decorator装饰器类继承了Component 所具有的接口;但在实现上又表现为 has-a Component的组合关系,即Decorator装饰器类又使用了另外一个Component类。
- 装饰器模式的目的并非解决“多子类衍生的多继承”问题,其应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。