设计模式--装饰者模式

  考虑程序要对一类 流 (网络流,IO流等等)进行操作。进行什么操作呢?可能在读(Read)这个流的时候对这个流进行加密,也可能对这个流进行缓存。

  那么很自然的能设计出以下这些类

class Stream {
public:
  void Read();
};
class IOStream : public Stream {};                   // 输入输出流
class NetStream: public Stream {};                    // 网络流
class BufferedIOStream : public IOStream {
public:
  void Read() {                       // 实现了缓存功能的读操作
    Buffer();
    IOStream::Read();
  }
  void Buffer() {}              
};       
class EncryptIOStream : public IOStream {
public:
  void Read() {                      // 实现了加密功能的读操作
    Encrypt();
    IOStream::Read();
  }
  void Encrypt();                     
}
// 实现加密和缓存功能的输入输出流
class BufferedEncryptIOStream : public EncryptIOStream, 
                   public BufferedIOStream {
public: 
  void Read() {
    EncryptIOStream::Encrypt();
    BufferedIOStream::Read();
  }
};
// 网络流同理
class BufferedNetStream : public NetStream ......

 

  那么这样做的缺点有什么呢?试想一下,如果我现在要对流增加新的操作,比如说要将流输出。你可能会有如下设计

class PrintIOStream : public IOStream {
public: 
  void Read() {
    Print();
    IOStream::Read();
  }
  void Print();
}
class BufferedPrintIOStream : public BufferedIOStream,
                  public PrintIOStream {  // 既可以加密有可以缓存的流
public: 
  void Read() {
    PrintIOStream::Print();
    BufferedIOStream::Read();
  }
};  
class EncryptPrintIOStream : public EncryptIOStream,
                  public PrintIOStream {};  // 既可以加密有可以打印的IO流
class BufferedEncryptPrintIOStream : public BufferedPrintIOStream,
                      public EncryptPrintIOStream {};

 

  哇靠,有没有搞错!只不过是要求增加了一个新的打印的功能,结果多出了这么多的类。如果继续增加新功能的话,类的数量之多可想而知。这种现象被称作类爆炸

当然了,你也可以说只设计一个BufferedEncryptPrintIOStream。但是假如你某个时刻不需要打印功能的话,那么打印功能就是累赘,这显然违背了接口隔离原则

 

  这时候就需要一种更好的设计方法----装饰者模式

  试想一下,如果我们通过组合的方式来设计类会不会有上述问题呢?答案是不会。

// 装饰者模式一瞥
class BufferedStream : public Stream {
public:
  BufferedStream(Stream* stm) : stream(stm) {}
  void Read() {
    Buffer();
    myStream->Read();
  }
  void Buffer(); private:   Stream* myStream; };

  嗯?这个类怎么这么奇怪,又是继承了Stream,里面又包含了一个Stream。这其实就是装饰者模式的精髓。之所以继承是因为BufferedStream首先是个流,它可能有一些Stream通用的性质。而为什么它的成员函数也包含个Stream对象?请注意,这里的Stream是一个指针,对c++敏感的朋友可能已经意识到了指针意味着可以动态绑定。那么我们看看如何使用这个BufferedStream以实现上述的各种功能

// 如果我们想要一个缓冲的IO流
IOStream* stm = new IOStream();
BufferedStream* bufferedStm = new BufferedStream(stm);   
bufferedStm->Read();

// 如果我们要一个缓冲的加密的IO流
EncryptStream* encryptStream = new EncryptStram(new IOStream);
BufferedStream* bufEncStream = new BufferedStream(encryptStream);
bufEncStream->Read();

  是不是觉得很神奇?关键点在于对流的层层包装,比如说先把流包装成可打印的流,那么它就能实现打印的功能了。在这个基础上把它包装成缓存流,那么它既有缓存功能有有打印功能。如果需要更多的功能怎么办?继续包装就是了。这样子就无需为新的功能拓展类,从而避免了类爆炸的问题

  

  以上就是装饰者模式,接下来给出装饰者模式的完整设计代码

class Stream {
public:
  void Read();
};
class IOStream : public Stream {};
class PrintStream : Stream {
public:
  PrintStream(Stream* stm) : myStream(stm) {}
  void Read() {
    Print();
    myStream->Read();
  }
  void Print() {}
private:
  Stream* myStream;
};
class BufferedStream : Stream {
public:
  BufferedStream(Stream* stm) : myStream(stm) {}
  void Read() {
    Buffer();
    myStream->Read();
  }  
  void Buffer();
private:
  Stream* myStream;
};
class EncryptStream : Stream {
public:
  EncryptStream(Stream* stm) : myStream(stm) {}
  void Read() {
    Encrypt();
    myStream->Read();
  }
  void Encrypt();
private:
  Stream* myStream;
}

// 只需这几个类就能完成所有需要的功能,接下来看看使用方法
IOStream* iostm = new IOStream();
BufferedStream* bufferedPrintEncryptStream = 
  new BufferedStream(new PrintStream(new EncryptStream(iostm)));

  

  更近一步。细心的朋友可能已经发现了三个类中都有一个myStream成员,那么能优化的是将myStream放在一个新的类中,然后让这三个类继承这个新的类

class MyStream : Stream {
public:
  MyStream(Stream* stm) : myStream(stm) {}
private:
  Stream* myStream;
}

  

  以上就是装饰者模式的所有内容

 

posted @ 2019-05-01 13:31  SkyMelody  阅读(202)  评论(0编辑  收藏  举报