设计模式

“单一职责”模式

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

典型模式
Decorator
Bridge

代码示例:

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

流stream作为一个基类有一些基础的方法,如read,write,seek

各种流文件流,网络流,内存流等继承于基类流,同时会有扩展的需求,如加密,且加密相同,只是读取的操作不一样,因此又会有新的不同流的扩展类继承于各种流。当然除了加密还会有其他的扩展操作如缓冲buffer,同理也可以像加密一样继承。

如果要对一种流既加密又缓冲,可以继承了流之后在读写方法中添加加密操作,缓冲,读流的基础操作。

以上的代码会有什么缺陷?

 

 随着扩展方法的增加,由于对继承的不良使用,子类的规模不断膨胀,产生很多重复的代码。

改进一:使用组合

  1 //业务操作
  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 class CryptoStream: public Stream {
 57     
 58     Stream* stream;//...
 59 
 60 public:
 61     CryptoStream(Stream* stm):stream(stm){
 62     
 63     }
 64     
 65     
 66     virtual char Read(int number){
 67        
 68         //额外的加密操作...
 69         stream->Read(number);//读文件流
 70     }
 71     virtual void Seek(int position){
 72         //额外的加密操作...
 73         stream::Seek(position);//定位文件流
 74         //额外的加密操作...
 75     }
 76     virtual void Write(byte data){
 77         //额外的加密操作...
 78         stream::Write(data);//写文件流
 79         //额外的加密操作...
 80     }
 81 };
 82 
 83 
 84 
 85 class BufferedStream : public Stream{
 86     
 87     Stream* stream;//...
 88     
 89 public:
 90     BufferedStream(Stream* stm):stream(stm){
 91         
 92     }
 93     //...
 94 };
 95 
 96 
 97 void Process(){
 98 
 99     //运行时装配
100     FileStream* s1=new FileStream();
101     CryptoStream* s2=new CryptoStream(s1);
102     
103     BufferedStream* s3=new BufferedStream(s1);
104     
105     BufferedStream* s4=new BufferedStream(s2);
106     
107     
108 
109 }
decorator2.cpp

若多个子类含有相同的字段,那应该将该字段向上层提。

改进二:于是在创建一个中间类Decorator

  1 //业务操作
  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 void Process(){
107 
108     //运行时装配
109     FileStream* s1=new FileStream();
110     
111     CryptoStream* s2=new CryptoStream(s1);
112     
113     BufferedStream* s3=new BufferedStream(s1);
114     
115     BufferedStream* s4=new BufferedStream(s2);
116     
117     
118 
119 }
decorator3.cpp

 在DecoratorStream中,使用组合的方式实现多态,包含了Stream基类。

装饰模式,动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更灵活。有时我们希望给某个对象而不是整个类添加一些功能。例如上面的例子,有一个文件流,允许对它添加加密或者缓冲的扩展功能。装饰模式下,会将文件流嵌入到另一个对象(Decorator)中,由该对象完成特性的添加,该嵌入的对象就成为装饰,这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。

动机

  • 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
  • 如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?

继承引入的静态特性如下第七行:

 1 //扩展操作
 2 class CryptoFileStream :public FileStream{
 3 public:
 4     virtual char Read(int number){
 5        
 6         //额外的加密操作...
 7         FileStream::Read(number);//读文件流
 8     }
 9     ...
10 }

组合引入的动态特性如下第八行:

 1 class CryptoStream: public DecoratorStream {
 2 
 3 public:
 4     CryptoStream(Stream* stm):DecoratorStream(stm){
 5     
 6     }
 7     
 8     virtual char Read(int number){
 9         //额外的加密操作...
10         stream->Read(number);//读文件流
11     }
12     ...
13 }

模式定义

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

                              ——《设计模式》GoF

要点总结

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

(若一个类继承了一个父类,且在字段里包含(组合)了该父类,很大可能就是Decorator设计模式,基类和组合在功能上由类似的地方,一般来说不会在一个类中同时继承和组合同一个类。但在装饰模式中,使用继承是为了完善基类stream接口的规范,使用组合是为了支持调用filestream,netstream中的接口)     

装饰模式提供了更加灵活的向对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。装饰模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并且用装饰类给它逐渐地添加功能。可以从简单的部件组合出复杂的功能

 参考:

设计模式C++实现(11)——装饰模式

Decorator

原文地址:https://www.cnblogs.com/y4247464/p/14259058.html