[设计模式]装饰模式
1. “单一职责原则”
设计模式中八大设计原则中有一个“单一职责原则”,一个类一般只有一个职责,如果职责过多,随着类的派生,其会越来越混乱。
我们现在通过Stream派生出FileStream,MemoryStram;同时对流又存在加密,缓冲,既加密又缓冲等等,未来还会存在更多操作。
如果没学过设计模式,则写出下面代码一点都不奇怪,其随着需求的增多代码量急剧增加!其原因就是违背了“单一职责原则”,多继承的类混杂在一起。
//业务操作 class Stream{ public: virtual char Read(int number)=0; virtual void Seek(int position)=0; virtual void Write(char data)=0; virtual ~Stream(){} }; //主体类 class FileStream: public Stream{ public: virtual char Read(int number){ //读文件流 } virtual void Seek(int position){ //定位文件流 } virtual void Write(char data){ //写文件流 } }; class NetworkStream :public Stream{ public: virtual char Read(int number){ //读网络流 } virtual void Seek(int position){ //定位网络流 } virtual void Write(char data){ //写网络流 } }; class MemoryStream :public Stream{ public: virtual char Read(int number){ //读内存流 } virtual void Seek(int position){ //定位内存流 } virtual void Write(char data){ //写内存流 } }; //扩展操作 class CryptoFileStream :public FileStream{ public: virtual char Read(int number){ //额外的加密操作... FileStream::Read(number);//读文件流 } virtual void Seek(int position){ //额外的加密操作... FileStream::Seek(position);//定位文件流 //额外的加密操作... } virtual void Write(byte data){ //额外的加密操作... FileStream::Write(data);//写文件流 //额外的加密操作... } }; class CryptoNetworkStream : :public NetworkStream{ public: virtual char Read(int number){ //额外的加密操作... NetworkStream::Read(number);//读网络流 } virtual void Seek(int position){ //额外的加密操作... NetworkStream::Seek(position);//定位网络流 //额外的加密操作... } virtual void Write(byte data){ //额外的加密操作... NetworkStream::Write(data);//写网络流 //额外的加密操作... } }; class CryptoMemoryStream : public MemoryStream{ public: virtual char Read(int number){ //额外的加密操作... MemoryStream::Read(number);//读内存流 } virtual void Seek(int position){ //额外的加密操作... MemoryStream::Seek(position);//定位内存流 //额外的加密操作... } virtual void Write(byte data){ //额外的加密操作... MemoryStream::Write(data);//写内存流 //额外的加密操作... } }; class BufferedFileStream : public FileStream{ //... }; class BufferedNetworkStream : public NetworkStream{ //... }; class BufferedMemoryStream : public MemoryStream{ //... } class CryptoBufferedFileStream :public FileStream{ public: virtual char Read(int number){ //额外的加密操作... //额外的缓冲操作... FileStream::Read(number);//读文件流 } virtual void Seek(int position){ //额外的加密操作... //额外的缓冲操作... FileStream::Seek(position);//定位文件流 //额外的加密操作... //额外的缓冲操作... } virtual void Write(byte data){ //额外的加密操作... //额外的缓冲操作... FileStream::Write(data);//写文件流 //额外的加密操作... //额外的缓冲操作... } }; void Process(){ //编译时装配 CryptoFileStream *fs1 = new CryptoFileStream(); BufferedFileStream *fs2 = new BufferedFileStream(); CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream(); }
2. 装饰模式
设计模式中存在一句话,“继承优于组合”。面向对象设计语言仅C++支持多继承,多继承会带来代码冗杂的问题。
我们现在就采用组合的方式来解决多继承带来的困扰,对于Stream,一个核心的路线就是Stream的读写操作,而缓冲加密等起到装饰作用。
我们将起到装饰作用的用一个类将其打包,如果一个类需要用到装饰类,之后通过装饰类派生来获得这个装饰属性,当然装饰类同样继承于基类。
因为其同样继承于基类,所以类的主体属性并没有改变,依然是Stream流,这样也避免了多继承,而是采用组合继承的方式,需要什么功能通过继承添加什么功能。
3. 装饰模式核心代码
装饰模式核心代码如下,其最为显著的特点:即派生于基类,有存在一个其基类指针成员。
① 派生于基类的原因是规范接口,当类的实例x通过装饰类a获取功能a,其同样可以通过装饰类b获取功能b.....。
② 存在基类指针成员的原因是获取实例x的功能,该指针成员代指x,x->Read()与x->Write(),这样就不用再用代码去写这些功能。
可以说,如果同时存在上面两种特性,这段代码99%的概率使用装饰模式。
// 装饰器 class DecorationStream : public Stream { public: Stream* stream; // 构造函数为流赋值 DecorationStream(Stream* stm) :stream(stm) { } };
4. 代码实例
其采用装饰模式修改的代码如下,可以看到,其代码量相当少,不会重写类的功能,其调用原本的类。
#include <stdio.h> #include <iostream> #include <windows.h> using namespace std; // 流基类 class Stream { public: virtual void Read() = 0; virtual void Write() = 0; virtual ~Stream() {} }; // 文件流 class FileStream :public Stream { public: virtual void Read() { cout << "正在读取文件流..." << endl; } virtual void Write() { cout << "正在写入文件流..." << endl; } }; // 内存流 class MemoryStream :public Stream { public: virtual void Read() { cout << "正在读取内存流..." << endl; } virtual void Write() { cout << "正在写入内存流..." << endl; } }; // 装饰器 class DecorationStream : public Stream { public: Stream* stream; // 构造函数为流赋值 DecorationStream(Stream* stm) :stream(stm) { } }; // 加密操作 class CryptoStream :public DecorationStream { public: CryptoStream(Stream* stm) :DecorationStream(stm) { } virtual void Read() { cout << "正在执行其加密读操作..." << endl; stream->Read(); } virtual void Write() { cout << "正在执行加密写操作..." << endl; stream->Write(); } }; // 缓冲操作 class BufferStream :public DecorationStream { public: BufferStream(Stream* stm) :DecorationStream(stm) { } virtual void Read() { cout << "正在执行缓冲读操作..." << endl; stream->Read(); } virtual void Write() { cout << "正在执行缓冲写操作..." << endl; stream->Write(); } }; int main() { // 一般情况 FileStream* f = new FileStream(); f->Read(); cout << "------------------" << endl; // 当该文件流需要加密时 CryptoStream* Cf = new CryptoStream(f); Cf->Read(); cout << "------------------" << endl; // 当既需要加密又需要缓冲操作时 BufferStream* Bf = new BufferStream(Cf); Bf->Read(); }