设计模式学习总结:(5)装饰模式
装饰模式:
顾名思义,就是装饰,比如手机套,用来装饰手机,但是,作为手机套的实现是不影响手机的,手机套就像一个装饰器。在比方,相框,相框花边,他们都是为了给相片添加新的额外的功能,但是这种功能本身不影响相片的性质。
行为模式
意图:
动态给一个对象添加额外的职责。就增加功能而言,装饰模式相比生成子类更为灵活。
比生成子类更灵活,确实,相比用继承方式去添加职责,装饰器模式表现非常灵活。
比如这样的例子,流操作,一开始有文件流和网络流,如果用继承的方式,我想得这样写:
class Stream{ public: virtual handleStream()=0; virtual ~Stream(){} };
文件流:
#include<iostream>
using namespace std; class FileStream: public Stream{ public: FileStream(){} virtual ~FileStream(){} void handleStream(){ cout<<"处理文件流"<<endl; } };
网络流:
#include<iostream> using namespace std; class NetwordStream: public Stream{ public: NetwordStream(){} virtual ~NetwordStream void handleStream(){ cout<<"网络流"<<endl; } };
如果要加密两种流,用继承我们还得这么写:
class CryptoFileStream :public FileStream{ public: CryptoFileStream():FileStream(){} virtual ~CryptoFileStream(); void handleStream() { FileStream::handleStream(); cout<<"加密"<<endl; } };
class CryptoFileStream :public NetwordStream{ public: CryptoFileStream():NetwordStream(){} virtual ~CryptoFileStream(); void handleStream() { NetwordStream::handleStream(); cout<<"加密"<<endl; } };
假如还有个转码的装饰类,那我们还得再写两个这样的类,我这里就不写了,不仅代码冗余,而且类膨胀严重。如果有n种具体子类和m中搭配装饰,粗略算一下应该要1+n+n*m+n*(2^m-m-1)个类,基类一个,然后有多少种子类可能就是n,而对类进行单一装饰是n*m种类,m种装饰器的搭配方式应该是2^m-m-1(二项式公式减前两项),n种子类就有n*(2^m-m-1).
当然,这不是重点,重点是我们用装饰模式可以如何实现,先看看书上给的结构图:
我们看到四个基本的角色:
1.抽象接口对象(component)
最基本的机构对象,用于动态添加职责
2.具体的实际对象,具有具体职责的对象(concrete component)
3.总装饰器(decorator)
需要保存一个component对象用于多态,同时继承component用于添加额外职责。
4.具体装饰
实现你需要实现的额外职责。
最基本的基类:
class AbStream { public: virtual void handleBuff()=0; virtual ~AbStream(){} };
文件流类:
class FileStream:public AbStream { public: FileStream(){} virtual ~FileStream(){} void handleBuff(); }; void FileStream::handleBuff() { cout << "处理文件流" << endl; }
网络流:
class NetwordStream:public AbStream { public: NetwordStream(); virtual ~NetwordStream(); void handleBuff(); }; void NetwordStream::handleBuff() { cout << "处理网络流" << endl; }
接下来是装饰器:
class StreamDocorator:public AbStream { public: virtual ~StreamDocorator(); void handleBuff(); protected: StreamDocorator(AbStream *stream) :_stream(stream) { } private: AbStream *_stream; }; void StreamDocorator::handleBuff() { _stream->handleBuff(); }
StreamDocorator::~StreamDocorator()
{
}
然后是加密,作为具体装饰类:
class CryptoStream :public StreamDocorator { public: CryptoStream(AbStream *stream) : StreamDocorator(stream){} virtual ~CryptoStream(){} void handleBuff() { StreamDocorator::handleBuff(); cout << "对数据流加密" << endl; } };
如果还有转码:
class BuffStream :public StreamDocorator { public: BuffStream(AbStream *stream) :StreamDocorator(stream){} virtual ~BuffStream(){} void handleBuff() { StreamDocorator::handleBuff(); cout << "二进制转码" << endl; } };
然后我们就可以测试一下:
int main() { FileStream *a = new FileStream(); CryptoStream *cS = new CryptoStream(a); cS->handleBuff();
cout << "----------------------" << endl; BuffStream *bcS = new BuffStream(cS); //还可以进行复合装饰 bcS->handleBuff(); return 0; }
运行后的结果:
对比装饰模式和策略模式:
最根本的是装饰模式对于服务的对象是透明的,这个服务的对象就好比上面例子中的FileStream,透明应该这么理解,对于FileStream,它完全不知道有装饰器这样的兄弟,装饰器存不存在对于FileStream并没有什么影响,而策略模式中,比如上次例子中的Economic类,实际上用策略模式也能实现FileStream这个逻辑,但是对于FileStream,它需要保持关于装饰器(策略类,这里假设是策略模式实现)的引用,那么这是不透明。
而且如果抽象Stream如果过于复杂,用策略模式也是比较好的选择,当然,装饰器更加强调了多种可能(指装饰和策略)的搭配,而策略好像并无法达成这个要求。
当然,最后的还是要感慨下,也许在以后的开发过程中能够领悟更多,毕竟古人常说:"纸上得来终觉浅,绝知此事要躬行".