转载

c++ 设计模式6 (Decorator 装饰模式)

4. “单一职责”类模式

在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。

典型模式代表: Decorator,Bridge

4.1 Decorator 装饰模式

代码示例:不同的流操作(文件流,网络流,内存流)及其扩展功能(加密,缓冲)等的实现

实现代码1:

类图结构示意(大量使用继承)

c++ 设计模式6 (Decorator 装饰模式)

数据规模: 假设有n种文件,m种功能操作。该实现方法有(1 + n + n * m! / 2) 数量级的子类;

同时考察59行,79行,98行本身是相同的代码(类似还有很多),存在大量的冗余和重复。

开始重构,见方法2.

   1 //Decorator1.cpp   2 //业务操作   3 class Stream{   4 public:   5     virtual char Read(int number)=0;   6     virtual void Seek(int position)=0;   7     virtual void Write(char data)=0;   8        9     virtual ~Stream(){}  10 };  11   12 //主体类  13 class FileStream: public Stream{  14 public:  15     virtual char Read(int number){  16         //读文件流  17     }  18     virtual void Seek(int position){  19         //定位文件流  20     }  21     virtual void Write(char data){  22         //写文件流  23     }  24   25 };  26   27 class NetworkStream :public Stream{  28 public:  29     virtual char Read(int number){  30         //读网络流  31     }  32     virtual void Seek(int position){  33         //定位网络流  34     }  35     virtual void Write(char data){  36         //写网络流  37     }  38       39 };  40   41 class MemoryStream :public Stream{  42 public:  43     virtual char Read(int number){  44         //读内存流  45     }  46     virtual void Seek(int position){  47         //定位内存流  48     }  49     virtual void Write(char data){  50         //写内存流  51     }  52       53 };  54   55 //扩展操作  56 class CryptoFileStream :public FileStream{  57 public:  58     virtual char Read(int number){  59          60         //额外的加密操作...  61         FileStream::Read(number);//读文件流  62           63     }  64     virtual void Seek(int position){  65         //额外的加密操作...  66         FileStream::Seek(position);//定位文件流  67         //额外的加密操作...  68     }  69     virtual void Write(byte data){  70         //额外的加密操作...  71         FileStream::Write(data);//写文件流  72         //额外的加密操作...  73     }  74 };  75   76 class CryptoNetworkStream : :public NetworkStream{  77 public:  78     virtual char Read(int number){  79           80         //额外的加密操作...  81         NetworkStream::Read(number);//读网络流  82     }  83     virtual void Seek(int position){  84         //额外的加密操作...  85         NetworkStream::Seek(position);//定位网络流  86         //额外的加密操作...  87     }  88     virtual void Write(byte data){  89         //额外的加密操作...  90         NetworkStream::Write(data);//写网络流  91         //额外的加密操作...  92     }  93 };  94   95 class CryptoMemoryStream : public MemoryStream{  96 public:  97     virtual char Read(int number){  98           99         //额外的加密操作... 100         MemoryStream::Read(number);//读内存流 101     } 102     virtual void Seek(int position){ 103         //额外的加密操作... 104         MemoryStream::Seek(position);//定位内存流 105         //额外的加密操作... 106     } 107     virtual void Write(byte data){ 108         //额外的加密操作... 109         MemoryStream::Write(data);//写内存流 110         //额外的加密操作... 111     } 112 }; 113  114 class BufferedFileStream : public FileStream{ 115     //... 116 }; 117  118 class BufferedNetworkStream : public NetworkStream{ 119     //... 120 }; 121  122 class BufferedMemoryStream : public MemoryStream{ 123     //... 124 } 125  126  127  128  129 class CryptoBufferedFileStream :public FileStream{ 130 public: 131     virtual char Read(int number){ 132          133         //额外的加密操作... 134         //额外的缓冲操作... 135         FileStream::Read(number);//读文件流 136     } 137     virtual void Seek(int position){ 138         //额外的加密操作... 139         //额外的缓冲操作... 140         FileStream::Seek(position);//定位文件流 141         //额外的加密操作... 142         //额外的缓冲操作... 143     } 144     virtual void Write(byte data){ 145         //额外的加密操作... 146         //额外的缓冲操作... 147         FileStream::Write(data);//写文件流 148         //额外的加密操作... 149         //额外的缓冲操作... 150     } 151 }; 152  153  154  155 void Process(){ 156  157         //编译时装配 158     CryptoFileStream *fs1 = new CryptoFileStream(); 159  160     BufferedFileStream *fs2 = new BufferedFileStream(); 161  162     CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream(); 163  164 } 

实现代码2:

针对上述代码,重构步骤如下:

1)考察 CryptoFileStream ,CryptoNetworkStream,CryptoMemoryStream三个类,将其继承FileStream,NetworkStream,NetworkStream改为组合;即

  1 class CryptoFileStream{  2     FileStream* stream;  3 public:  4     virtual char Read(int number){  5          6         //额外的加密操作...  7         stream -> Read(number);//改用字段方式调用Read()  8         // ...seek() write() 同理  9     } 10 }     11  12 class CryptoNetworkStream{ 13     NetworkStream* stream; 14 public: 15     virtual char Read(int number){ 16          17         //额外的加密操作... 18         stream -> Read(number);//改用字段方式调用Read() 19         //... seek() write() 同理 20     } 21 }     22  23 class CryptoMemoryStream{ 24     MemoryStream* stream; 25 public: 26     virtual char Read(int number){ 27          28         //额外的加密操作... 29         stream -> Read(number);//改用字段方式调用Read() 30         //... seek() write() 同理 31     } 32 }     

2)考察上述2行, 13行, 24行, 发现其均为Stream子类, 应使用多态性继续重构。

  1 class CryptoFileStream{  2     Stream* stream; // = new FileStream()  3 public:  4     virtual char Read(int number){  5          6         //额外的加密操作...  7         stream -> Read(number);//改用字段方式调用Read()  8         // ...seek() write() 同理  9     } 10 }     11  12 class CryptoNetworkStream{ 13     Stream* stream; // = new NetworkStream(); 14 public: 15     virtual char Read(int number){ 16          17         //额外的加密操作... 18         stream -> Read(number);//改用字段方式调用Read() 19         //... seek() write() 同理 20     } 21 }     22  23 class CryptoMemoryStream{ 24     Stream* stream; // = newMemoryStream() 25 public: 26     virtual char Read(int number){ 27          28         //额外的加密操作... 29         stream -> Read(number);//改用字段方式调用Read() 30         //... seek() write() 同理 31     } 32 }     

3)发现三个类是相同的,不同的实现(需求的变化)是在运行时实现,编译时复用,改为一个类即可,命名为CryptoStream。

同时为了保证接口规范(read,seek等仍然是虚函数),继承Stream,出现既有组合,又有继承的情况。

  1 class CryptoStream : public Stream{  2     Stream* stream; // = new ...  3 public:  4     virtual char Read(int number){  5          6         //额外的加密操作...  7         stream -> Read(number);//改用字段方式调用Read()  8         // ...seek() write() 同理  9     } 10 }    

4)添加相应构造器,得到此轮重构后的结果,代码如下,主要查看使用方式(运行时装配):

   1 //Decorator2.cpp   2 class Stream{   3    4 public:   5     virtual char Read(int number)=0;   6     virtual void Seek(int position)=0;   7     virtual void Write(char data)=0;   8        9     virtual ~Stream(){}  10 };  11   12 //主体类  13 class FileStream: public Stream{  14 public:  15     virtual char Read(int number){  16         //读文件流  17     }  18     virtual void Seek(int position){  19         //定位文件流  20     }  21     virtual void Write(char data){  22         //写文件流  23     }  24   25 };  26   27 class NetworkStream :public Stream{  28 public:  29     virtual char Read(int number){  30         //读网络流  31     }  32     virtual void Seek(int position){  33         //定位网络流  34     }  35     virtual void Write(char data){  36         //写网络流  37     }  38       39 };  40   41 class MemoryStream :public Stream{  42 public:  43     virtual char Read(int number){  44         //读内存流  45     }  46     virtual void Seek(int position){  47         //定位内存流  48     }  49     virtual void Write(char data){  50         //写内存流  51     }  52       53 };  54   55 //扩展操作  56   57   58 class CryptoStream: public Stream {  59       60     Stream* stream;//...  61   62 public:  63     CryptoStream(Stream* stm):stream(stm){  64       65     }  66       67       68     virtual char Read(int number){  69          70         //额外的加密操作...  71         stream->Read(number);//读文件流  72     }  73     virtual void Seek(int position){  74         //额外的加密操作...  75         stream::Seek(position);//定位文件流  76         //额外的加密操作...  77     }  78     virtual void Write(byte data){  79         //额外的加密操作...  80         stream::Write(data);//写文件流  81         //额外的加密操作...  82     }  83 };  84   85   86   87 class BufferedStream : public Stream{  88       89     Stream* stream;//...  90       91 public:  92     BufferedStream(Stream* stm):stream(stm){  93           94     }  95     //...  96 };  97   98   99  100  101  102 void Process(){ 103  104     //运行时装配 105     FileStream* s1=new FileStream(); 106     CryptoStream* s2=new CryptoStream(s1); 107      108     BufferedStream* s3=new BufferedStream(s1); 109      110     BufferedStream* s4=new BufferedStream(s2); 111      112      113  114 } 

实现代码3:

上述实现代码2已经极大地缓解了冗余问题,符合面向对象的设计思想,该轮重构是锦上添花。

重构步骤如下:

考察上述代码,多个子类都有同样的字段(Stream* stream;//...)

应考虑“往上提”,方法有两种,第一种是提到基类(显然不合适,FileStream等并不需要Stream字段 )

所以考虑第二种方法,实现一个“中间类”。

 DecoratorStream: public Stream{ protected:     Stream* stream;//...          DecoratorStream(Stream * stm):stream(stm){          }      }; 

CryptoStream等继承中间类DecoratorStream:

 class CryptoStream: public DecoratorStream {   public:     CryptoStream(Stream* stm):DecoratorStream(stm){          }     //... }     

重构完成的最终版本:

FileStream,NetworkStream,MemoryStream等可以创建各自的对象;

但实现加密,缓存功能必须在已有FileStream/NetworkStream等对象基础上;

这些操作本质是扩展操作,也就是“装饰”的含义。

此时类图示意:

这时类的数量为(1 + n + 1 + m)

c++ 设计模式6 (Decorator 装饰模式)

   1 //Decorator3.cpp   2 class Stream{   3    4 public:   5     virtual char Read(int number)=0;   6     virtual void Seek(int position)=0;   7     virtual void Write(char data)=0;   8        9     virtual ~Stream(){}  10 };  11   12 //主体类  13 class FileStream: public Stream{  14 public:  15     virtual char Read(int number){  16         //读文件流  17     }  18     virtual void Seek(int position){  19         //定位文件流  20     }  21     virtual void Write(char data){  22         //写文件流  23     }  24   25 };  26   27 class NetworkStream :public Stream{  28 public:  29     virtual char Read(int number){  30         //读网络流  31     }  32     virtual void Seek(int position){  33         //定位网络流  34     }  35     virtual void Write(char data){  36         //写网络流  37     }  38       39 };  40   41 class MemoryStream :public Stream{  42 public:  43     virtual char Read(int number){  44         //读内存流  45     }  46     virtual void Seek(int position){  47         //定位内存流  48     }  49     virtual void Write(char data){  50         //写内存流  51     }  52       53 };  54   55 //扩展操作  56   57 DecoratorStream: public Stream{  58 protected:  59     Stream* stream;//...  60       61     DecoratorStream(Stream * stm):stream(stm){  62       63     }  64       65 };  66   67 class CryptoStream: public DecoratorStream {  68    69   70 public:  71     CryptoStream(Stream* stm):DecoratorStream(stm){  72       73     }  74       75       76     virtual char Read(int number){  77          78         //额外的加密操作...  79         stream->Read(number);//读文件流  80     }  81     virtual void Seek(int position){  82         //额外的加密操作...  83         stream::Seek(position);//定位文件流  84         //额外的加密操作...  85     }  86     virtual void Write(byte data){  87         //额外的加密操作...  88         stream::Write(data);//写文件流  89         //额外的加密操作...  90     }  91 };  92   93   94   95 class BufferedStream : public DecoratorStream{  96       97     Stream* stream;//...  98       99 public: 100     BufferedStream(Stream* stm):DecoratorStream(stm){ 101          102     } 103     //... 104 }; 105  106  107  108  109 void Process(){ 110  111     //运行时装配 112     FileStream* s1=new FileStream(); 113      114     CryptoStream* s2=new CryptoStream(s1); 115      116     BufferedStream* s3=new BufferedStream(s1); 117      118     BufferedStream* s4=new BufferedStream(s2); 119      120      121  122 } 

Decorator模式使用动机:

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

模式定义:

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

类图:

c++ 设计模式6 (Decorator 装饰模式)

要点总结:

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

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

3.Decorator模式的目的并非解决”多字类衍生的多继承“问题,Decorator模式应用的要点在于解决 ”主体类在多个方向上的扩展功能“(显然file,network与加密,缓冲是两种扩展方向) ——是为”装饰“的含义。

参考文献:

李建忠老师 《C++设计模式》网络课程

《设计模式:可复用面向对象软件的基础》

原文  http://www.cnblogs.com/wangxiaobao/p/5203010.html
正文到此结束
Loading...