当前位置: 代码迷 >> 综合 >> 设计模式:Decorator
  详细解决方案

设计模式:Decorator

热度:29   发布时间:2023-12-08 09:12:17.0

Decorator-装饰模式

装饰模式用于处理,软件组件设计中的责任划分不清晰,导致子类通过继承得到大量的冗余重复代码,代码的急剧膨胀问题。装饰模式关键在于划清责任。
考虑设计一个IO库、相关流操作。其中有文件流,网络流、内存流等,操作有对流的加密,缓存等。
考虑设计一个Stream抽象基类,FileStream、NetWorkStream、MemoryStream继承与Stream基类。
而加密操作需要加密具体的流,而不是抽象上的流,所以继承自FileStream、NetWorkStream、MemoryStream。
具体代码如下:

版本1

//业务操作
class Stream{
    
publicvirtual 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();}

继承图谱

在这里插入图片描述

我们可以发现,加密、缓冲操作是一样的,只是加密、缓冲的对象不同,但加密的对象都继承自一个共同的抽象基类。代码大量重复,可以进行代码重构。
各类扩展操作从继承各类流,改为包含各类流的基类指针Stream*。使用组合替代继承,将原先写死的FileStream::Read等改为基类指针调用Stream->Read。
Stream实质是一个多态指针。当变量的声明都为某个类型的子类时,可以直接提升声明为该变量类型。
编译时复用,将变化需求提升至运行时。
同时为了Read等接口函数的虚性质,维护接口的规范,需要继承Stream。

版本2

//业务操作
class Stream{
    publicvirtual 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 CryptoStream: public Stream {
    Stream* stream;//...public:CryptoStream(Stream* stm):stream(stm){
    }virtual char Read(int number){
    //额外的加密操作...stream->Read(number);//读文件流}virtual void Seek(int position){
    //额外的加密操作...stream::Seek(position);//定位文件流//额外的加密操作...}virtual void Write(byte data){
    //额外的加密操作...stream::Write(data);//写文件流//额外的加密操作...}
};class BufferedStream : public Stream{
    Stream* stream;//...public:BufferedStream(Stream* stm):stream(stm){
    }//...
};void Process(){
    //运行时装配FileStream* s1=new FileStream();CryptoStream* s2=new CryptoStream(s1);BufferedStream* s3=new BufferedStream(s1);BufferedStream* s4=new BufferedStream(s2);}

最后使用时,从原先的编译时装配,改为运行时装配。
编译时,不存在流的加密缓存等类,运行时通过多态指针将各种操作组合起来,来满足未来的需求。
但到此为止,还不是最优的设计,根据马丁福勒的重构理论,如果某个类的多个子类都有同样的字段,应该将字段上提。
字段上提可以提升至基类,也可以提升至中间类。
考虑到FileStream、NetWorkStream等并不需要该字段,所以设计中间类包含字段,并继承Stream。

版本3

//业务操作
class Stream{
    publicvirtual 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){
    //写内存流}};//扩展操作DecoratorStream: public Stream{
    
protected:Stream* stream;//...DecoratorStream(Stream * stm):stream(stm){
    }};class CryptoStream: public DecoratorStream {
    public:CryptoStream(Stream* stm):DecoratorStream(stm){
    }virtual char Read(int number){
    //额外的加密操作...stream->Read(number);//读文件流}virtual void Seek(int position){
    //额外的加密操作...stream::Seek(position);//定位文件流//额外的加密操作...}virtual void Write(byte data){
    //额外的加密操作...stream::Write(data);//写文件流//额外的加密操作...}
};class BufferedStream : public DecoratorStream{
    Stream* stream;//...public:BufferedStream(Stream* stm):DecoratorStream(stm){
    }//...
};void Process(){
    //运行时装配FileStream* s1=new FileStream();CryptoStream* s2=new CryptoStream(s1);BufferedStream* s3=new BufferedStream(s1);BufferedStream* s4=new BufferedStream(s2);}

DecoratorStream类是中间类,包含重复字段,扩展继承该类,同时调用DecoratorStream里的构造器构造(基类构造器)。

继承图谱

在这里插入图片描述
可以从继承图谱看出,类的规模显著的减少,将大量的流扩展操作与各类流分离,避免了对继承的不良使用,提升代码的复用性。
装饰模式的要点在于,组合基类并继承基类。从经验推断,如果某类继承并组合某个基类,99%的可能是使用了装饰模式。
装饰模式是为了解决因为继承的误用,随着子类的增多,在静态特质下,各种子类的组合会导致更多的子类急剧的膨胀。通过动态的给对象增加格外的职责,来消除重复代码,减少子类个数。