C++设计模式:装饰器模式(四)

时间:2024-04-12 19:19:32
1、定义与动机
  • 装饰器模式定义:动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。

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

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

  • 装饰器模式有一个很独特的地方:不仅继承接口(is a)、还组合接口(has a);继承接口是为了重写接口实现接口的方法,组合是为了动态装入接口的其他子类,这样就可以轻松的实现功能拓展,但又不违背单一职责原则!

2、案例分析
  • C++中的文件流对象并没有Java中的那么丰富,在Java语言中存在各种各样的流对象,基类有InputStream/OutputStream、Reader/Writer等等
  • 同时派生出各种IO流类,通过不同的功能、作用、辅助分类可以分很多种:文件流、对象流、缓冲流、缓冲文件流、加密缓冲流、等等
  • 如果代码都单纯的靠继承来实现Java庞大的IO流类库,工作量是非常大的。
  • 导致工作量巨大的原因:单纯通过继承来实现,存在大量的CV代码

在这里插入图片描述

2.1、基础实现
  • 普通的实现方式就是一直继承,代码无线重复的去设计实现
  • 里氏替换原则:当继承基类或者父类的代码大量被重写时,继承失去其原本的意义,换句话说可以不需要继承。
  • 这样设计会导致继承失去其原本的意义,因为大量的代码都被重写(违背里氏替换原则)
class Stream{
    virtual char Read(int n) = 0;
    virtual void Seek(int n) = 0;
    virtual void Write(char data) = 0;
    virtual ~Stream(){}
};
/*
 * FileStream、NetWorkStream、MemoryStream、ObjectStream...
 */
class FileStream: public Stream{
public:
    virtual char Read(int n){
        // 读文件流
    }
    virtual void Seek(int n){
        // 定位文件流
    }
    virtual void Write(char data){
        // 写文件流
    }
};

class NetWorkStream: public Stream{
public:
    virtual char Read(int n){
        // 读网络流
    }
    virtual void Seek(int n){
        // 定位网络流
    }
    virtual void Write(char data){
        // 写网络流
    }
};

// CryptoFileStream、CryptoNetWorkStream、CryptoMemoryStream、CryptoObjectStream...
class CryptoFileStream: public FileStream{
public:
    virtual char Read(int n){
        // 加密
        FileStream::Read(n);
        // 加密
    }
    virtual void Seek(int n){
        // 加密
        FileStream::Seek(n);
        // 加密
    }
    virtual void Write(char data){
        // 加密
        FileStream::Write(n);
        // 加密
    }
};
class CryptoNetWorkStream: public NetWorkStream{
public:
    virtual char Read(int n){

    }
    virtual void Seek(int n){

    }
    virtual void Write(char data){

    }
};

// BufferedFileStream、BufferedNetWorkStream、BufferedMemoryStream、BufferedObjectStream...
class BufferedFileStream: public FileStream{
    // ...
};
class BufferedNetWorkStream: public NetWorkStream{
    // ...
};

// CryptoBufferedFileStream、CryptoBufferedNetWorkStream、CryptoBufferedMemoryStream、CryptoBufferedObjectStream...
class CryptoBufferedFileStream: public BufferedFileStream{
    virtual char Read(int n){
        // 加密
        // 缓存
        BufferedFileStream::Read(n);
    }
    virtual void Seek(int n){
        //...
    }
    virtual void Write(char data){
        //...
    }
};
  • 而这样编写到了后期持续的扩展,类的数量指数增长,代码量急剧上升

在这里插入图片描述

2.2、普通装饰器
  • 为了消除这些冗余的代码,可以优先考虑使用组合的形式来取代靠继承实现的这些IO流类
  • 基于上述代码,只要灵活使用多态和组合就可以省略掉很多冗余的代码
    • CryptoStream类中组合一个Stream抽象类的指针,当构造CryptoStream类时可以通过传入FileStream、NetWorkStream对象来完成动态绑定,然后CryptoFileStream、CryptoNetWorkStream…其内部所有的方法都一样(因为只是基于不同的流类套了一层加密,而具体哪个流类通过Stream抽象类接口来动态绑定),因此可以代码去重。
  • 因此这样就可以实现去重
class Stream{
    virtual char Read(int n) = 0;
    virtual void Seek(int n) = 0;
    virtual void Write(char data) = 0;
    virtual ~Stream(){}
};
/*
 * FileStream、NetWorkStream、MemoryStream、ObjectStream...
 */
class FileStream: public Stream{
public:
    virtual char Read(int n){
        // 读文件流
    }
    virtual void Seek(int n){
        // 定位文件流
    }
    virtual void Write(char data){
        // 写文件流
    }
};

class NetWorkStream: public Stream{
public:
    virtual char Read(int n){
        // 读网络流
    }
    virtual void Seek(int n){
        // 定位网络流
    }
    virtual void Write(char data){
        // 写网络流
    }
};

// CryptoFileStream、CryptoNetWorkStream、CryptoMemoryStream、CryptoObjectStream...
class CryptoStream: public Stream{
private:
    Stream *stream;             // new FileStream()、new NetWorkStream().....
public:
    CryptoStream(Stream *_stream): stream(_stream){
        
    }
    virtual char Read(int n){
        // 加密
        stream->Read(n);
        // 加密
    }
    virtual void Seek(int n){
        // 加密
        stream->Read(n);
        // 加密
    }
    virtual void Write(char data){
        // 加密
        stream->Read(n);
        // 加密
    }
};

// BufferedFileStream、BufferedNetWorkStream、BufferedMemoryStream、BufferedObjectStream...
class BufferedStream: public Stream{
    Stream *stream;
    // ...
};

// CryptoBufferedFileStream、CryptoBufferedNetWorkStream、CryptoBufferedMemoryStream、CryptoBufferedObjectStream...
class CryptoBufferedStream: public Stream{
    Stream *stream;             //....
    virtual char Read(int n){
        // 加密
        // 缓存
    }
    virtual void Seek(int n){
        //...
    }
    virtual void Write(char data){
        //...
    }
};

void process()
{
    // 运行时装配
    FileStream fileStream = new FileStream();
    CryptoStream cryptoStream1 = new CryptoStream(new FileStream());
    CryptoStream cryptoStream2 = new CryptoStream(new NetWorkStream());
    ....    
    CryptoBufferedStream cryptoBufferedStream = new CryptoBufferedStream(new FileStream());
}
2.3、抽象装饰器
  • 抽象装饰器就是在实现类和基类之间套了一个装饰器类
    • 装饰器类负责继承基类,通过多态的动态绑定来运行时确定加载某个类
    • 实现类通过继承装饰器类,调用装饰器类中的对象完成操作
class Stream{
    virtual char Read(int n) = 0;
    virtual void Seek(int n) = 0;
    virtual void Write(char data) = 0;
    virtual ~Stream(){}
};
/*
 * FileStream、NetWorkStream、MemoryStream、ObjectStream...
 */
class FileStream: public Stream{
public:
    virtual char Read(int n){
        // 读文件流
    }
    virtual void Seek(int n){
        // 定位文件流
    }
    virtual void Write(char data){
        // 写文件流
    }
};

class NetWorkStream: public Stream{
public:
    virtual char Read(int n){
        // 读网络流
    }
    virtual void Seek(int n){
        // 定位网络流
    }
    virtual void Write(char data){
        // 写网络流
    }
};

class DecoratorStream: Stream{
protected:
    Stream *stream;
    DecoratorStream(Stream *_stream): stream(_stream){

    }
};

// CryptoFileStream、CryptoNetWorkStream、CryptoMemoryStream、CryptoObjectStream...
class CryptoStream: public DecoratorStream{
public:
    CryptoStream(Stream *_stream): DecoratorStream(_stream){

    }
    virtual char Read(int n){
        // 加密
        stream->Read(n);
        // 加密
    }
    virtual void Seek(int n){
        // 加密
        stream->Read(n);
        // 加密
    }
    virtual void Write(char data){
        // 加密
        stream->Read(n);
        // 加密
    }
};

// BufferedFileStream、BufferedNetWorkStream、BufferedMemoryStream、BufferedObjectStream...
class BufferedStream: public DecoratorStream{
    // ...
};

// CryptoBufferedFileStream、CryptoBufferedNetWorkStream、CryptoBufferedMemoryStream、CryptoBufferedObjectStream...
class CryptoBufferedStream: public DecoratorStream{
    virtual char Read(int n){
        // 加密
        // 缓存
    }
    virtual void Seek(int n){
        //...
    }
    virtual void Write(char data){
        //...
    }
};

void process()
{
    // 运行时装配
    FileStream fileStream = new FileStream();
    CryptoStream cryptoStream1 = new CryptoStream(new FileStream());
    CryptoStream cryptoStream2 = new CryptoStream(new NetWorkStream());
    ....
    CryptoBufferedStream cryptoBufferedStream = new CryptoBufferedStream(new FileStream());
}

在这里插入图片描述

4、总结
  • 通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所有具有的接口;但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component实现类。
  • Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”—是为“装饰”的含义
    在这里插入图片描述