设计模式4-装饰器模式Decorator

装饰器模式属于"单一职责"模式.
在软件组件的设计中,如果责任划分不清晰,使用继承得到的结果,往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这是代码的bad smell.
典型的单一职责模式:Decorator,Bridge.这两种模式表现出了很强的"单一职责模式的味道".本文中我们讨论Decorator模式.

动机

在某些情况下,我们可能会"过度的使用继承来扩展对象的功能",由于继承为类型引入了静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀.

如何使对象功能的扩展能够根据需要动态实现,同时避免扩展功能的增多带来的子类膨胀问题?从而使得任何功能扩展变化所带来的影响最低.

实例

考虑设计一个IO的库,主要是一些流操作.可以首先定义一个基类Stream,

//业务操作
class Stream{
public:
    virtual  char Read(int number)=0;
    virtual void Seek(int postion)=0;
    virtual void Write (char data)=0;
    ~Stream() { }
};

基于Stream可以定义文件流,网络流,内存流等派生类,然后重写实现基类的成员函数.


class FileStream:public Stream{
public:
    
    virtual  char Read(int number){
        //文件读
    }
    virtual void Seek(int postion){
        //定位文件
    }
    virtual void Write (char data){
        //文件写
    }
    


};

class NetworkStream:public Stream{
public:
    virtual  char Read(int number){
        //网络读


    }
    virtual void Seek(int postion){
        //定位网络流

    }
    virtual void Write (char data){
        //网络写

    }



};

class MemoryStream:public Stream{
public:
    virtual  char Read(int number){
        //内存读


    }
    virtual void Seek(int postion){
        //定位内存流

    }
    virtual void Write (char data){
        //内存写

    }

};

现在我们想给文件流,网络流,进行功能扩展,比如在流读写的过程中,添加额外的加密操作,这个是时候应该怎么处理?
首先我们先看用继承的的方式来解决这个问题.

首先是对文件读写进行加密,这个时候会增加3个类,分别对FileStream,NetworkStream,MemoryStream


class  CryptoFileStream:public FileStream{
public:    
    virtual  char Read(int number){
        //进行加密操作
        FileStream::Read(number);
        //进行加密操作
    }
    virtual void Seek(int postion){
        //进行加密操作
        FileStream::Seek(postion);        
        //进行加密操作

    }
    virtual void Write (char data){
        
        //进行加密操作
        FileStream::Write(data);
        //进行加密操作

    }   



};

class  CryptoNetworkStream: public NetworkStream{
public:
    virtual  char Read(int number){
        //进行加密操作
        NetworkStream::Read(number);
        //进行加密操作
    }
    virtual void Seek(int postion){
        //进行加密操作
        NetworkStream::Seek(postion);        
        //进行加密操作

    }
    virtual void Write (char data){
        
        //进行加密操作
        NetworkStream::Write(data);
        //进行加密操作

    }   
    


};

class  CryptoMemoryStream: public MemoryStream{
public:
    virtual  char Read(int number){
        //进行加密操作
        MemoryStream::Read(number);
        //进行加密操作
    }
    virtual void Seek(int postion){
        //进行加密操作
        MemoryStream::Seek(postion);        
        //进行加密操作

    }
    virtual void Write (char data){
        
        //进行加密操作
        MemoryStream::Write(data);
        //进行加密操作

    }     


};

可以看到,这个时候类增多,并且存在很多重复代码,这种设计不好.考虑用组合的方式,不用继承.
我们这个时候不用继承,采用组合的模式,在加密的类中增加一个成员变量.这样的话可以用一个加密的类,替换掉三个派生类,加密的文件类型取决于成员stream_的取值,可以相应的对FileStream,Network,Memory进行加密.

class  CryptoStream {
private:
    Stream*  stream_;  // =new FileStream() ,new NetWorkSteam, new MemoryStream,
public:    
    virtual  char Read(int number){
        //进行加密操作
        stream_->Read(number);
        //进行加密操作
    }
    virtual void Seek(int postion){
        //进行加密操作
        stream_->Seek(postion);        
        //进行加密操作

    }
    virtual void Write (char data){
        
        //进行加密操作
        stream_->Write(data);
        //进行加密操作

    }   



};

这个时候我们需要考虑一下,CryptoStream中的成员函数还都是虚函数,这个是不合理的,因为它并不是基类,为了保持接口一致性,我们需要让他继承Stream,

class  CryptoStream:public Stream {
private:
    Stream*  stream_;  // =new FileStream() ,new NetWorkSteam, new MemoryStream,
public:
    CryptoStream(Stream* stream):stream_(stream){
        
    }    
    virtual  char Read(int number){
        //进行加密操作
        stream_->Read(number);
        //进行加密操作
    }
    virtual void Seek(int postion){
        //进行加密操作
        stream_->Seek(postion);        
        //进行加密操作

    }
    virtual void Write (char data){
        
        //进行加密操作
        stream_->Write(data);
        //进行加密操作

    }   



};

这个时候就出现了一个非常奇妙的现象,也就是CryptoStream 既继承了基类,同时也有一个成员变量是基类的指针.

再进一步可以将 Stream* stream_字段上移,定义一个Decorator


class Decorator:public Stream{

public:
    Decorator(Stream* stream):stream_(stream){

    }
protected:
    Stream*  stream_;

};


class  CryptoStream:public Decorator {

public:
    CryptoStream(Stream* stream):Decorator(stream){

    }    
    virtual  char Read(int number){
        //进行加密操作
        stream_->Read(number);
        //进行加密操作
    }
    virtual void Seek(int postion){
        //进行加密操作
        stream_->Seek(postion);        
        //进行加密操作

    }
    virtual void Write (char data){
        
        //进行加密操作
        stream_->Write(data);
        //进行加密操作
    }   

};

动态地(组合)给一个对象增加一些额外的职责,就增加功能而言,Decorator模式比生成子类(继承)更加灵活(消除重复代码,减少子类个数)

总结

通过采用组合而非继承的方式,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了使用继承带来的"灵活性差"和"多子类衍生问题".

Decorator 类在接口上变现为 is a Component 的继承关系,即Decorator类继承了Component类的所具有的接口.但是在实现上又表现为has a Component的关系,即Decorator类又使用了另外一个Component类.继承是为了接口规范,组合是为了调用具体的实现类.

Decorator 模式并非解决,多子类衍生的多继承问题,Decorator模式应用的要点在于解决,主体类在多个方向上的扩展功能.

上一篇:在Python中装饰实例方法


下一篇:python – Django:’str’对象没有属性’user’