[设计模式] 设计模式课程(五)--装饰模式

场景

  • 按目的划分,属于结构型模式;按封装划分,属于单一职责模式
  • 使用继承来扩展对象的功能时,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性,并且随着子类的增多,各种子类的组合会导致更多子类的膨胀
  • 如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?
  • 如果责任划分不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时的关键是划分责任
  • 将对象放入包含行为的特殊封装对象中,为原来的对象绑定新的行为
  • 通知基类与装饰器实现相同的接口
  • 采用组合的方式实现功能扩展,即装饰类实现的接口和其构造函数传入的对象是同一接口
  • 在无需修改原对象代码的情况下,为对象增加额外的行为
  • 对于用final关键字限制扩展的类,只能通过装饰模式进行修饰

 

结构

  • 部件接口:声明装饰器和被封装对象的公用接口
  • 具体部件类:被封装对象的类,定义可被装饰的基本行为
  • 基础装饰类:包含指向被封装对象的引用成员变量
  • 具体装饰类:定义了可动态添加到部件的额外行为,重写装饰基类的方法,添加额外行为

场景

  • 实现消息通知功能时,将邮件通知行为放在基类通知器中,将其他通知方法(QQ、微信、短信)放在装饰器中,客户代码将通知器放入自己需要的装饰器中
  • 当数据即将被写入磁盘前 通过装饰器对数据进行加密和压缩 在原始类对改变毫无察觉的情况下 将加密后的受保护数据写入文件
  • 当数据刚从磁盘读出后 同样通过装饰对数据进行解压和解密 装饰和数据源类实现同一接口 从而能在客户端代码中相互替换

示例1

decorator1.cpp

  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 class CryptoBufferedFileStream :public FileStream{
126 public:
127     virtual char Read(int number){
128         
129         //额外的加密操作...
130         //额外的缓冲操作...
131         FileStream::Read(number);//读文件流
132     }
133     virtual void Seek(int position){
134         //额外的加密操作...
135         //额外的缓冲操作...
136         FileStream::Seek(position);//定位文件流
137         //额外的加密操作...
138         //额外的缓冲操作...
139     }
140     virtual void Write(byte data){
141         //额外的加密操作...
142         //额外的缓冲操作...
143         FileStream::Write(data);//写文件流
144         //额外的加密操作...
145         //额外的缓冲操作...
146     }
147 };
148 
149 void Process(){
150 
151         //编译时装配
152     CryptoFileStream *fs1 = new CryptoFileStream();
153 
154     BufferedFileStream *fs2 = new BufferedFileStream();
155 
156     CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();
157 
158 }
View Code
  • 问题:Stream规模太大,60,80,99是一样的操作,产生代码冗余

decorator2.cpp

  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;//未来可变成各种stream,支持变化
 59 
 60 public:
 61     CryptoStream(Stream* stm):stream(stm){
 62     
 63     }
 64     
 65     virtual char Read(int number){
 66        
 67         //额外的加密操作...
 68         stream->Read(number);//读文件流
 69     }
 70     virtual void Seek(int position){
 71         //额外的加密操作...
 72         stream::Seek(position);//定位文件流
 73         //额外的加密操作...
 74     }
 75     virtual void Write(byte data){
 76         //额外的加密操作...
 77         stream::Write(data);//写文件流
 78         //额外的加密操作...
 79     }
 80 };
 81 
 82 class BufferedStream : public Stream{
 83     
 84     Stream* stream;//...
 85     
 86 public:
 87     BufferedStream(Stream* stm):stream(stm){
 88         
 89     }
 90     //...
 91 };
 92 
 93 void Process(){
 94     //运行时装配
 95     FileStream* s1=new FileStream();
 96     
 97     //加密
 98     CryptoStream* s2=new CryptoStream(s1);
 99     
100     //缓存
101     BufferedStream* s3=new BufferedStream(s1);
102     
103     //既加密又缓存
104     BufferedStream* s4=new BufferedStream(s2);
105 }
View Code
  • 用组合代替继承,合并子类,消除重复性
  • CryptoStream类既继承了Stream(完善接口规范),又有stream字段(别忘写构造函数)
  • FileStream::Read(number); 和 stream->Read(number); 效果一样
  • 实际代码看到组合和继承同时出现在一个类中时,99%是装饰器模式
  • 编译时一样(复用),运行时不一样(组合,用多态支持变化)
  • 编译使装配(decorator1) vs. 运行时装配(decorator2)
  • 当变量声明类型都是某个类型的子类时,直接声明成某个类型即可

decorator3.cpp

  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 public:
 70     CryptoStream(Stream* stm):DecoratorStream(stm){
 71     
 72     }
 73     
 74     
 75     virtual char Read(int number){
 76        
 77         //额外的加密操作...
 78         stream->Read(number);//读文件流
 79     }
 80     virtual void Seek(int position){
 81         //额外的加密操作...
 82         stream::Seek(position);//定位文件流
 83         //额外的加密操作...
 84     }
 85     virtual void Write(byte data){
 86         //额外的加密操作...
 87         stream::Write(data);//写文件流
 88         //额外的加密操作...
 89     }
 90 };
 91 
 92 class BufferedStream : public DecoratorStream{
 93     
 94     Stream* stream;//...
 95     
 96 public:
 97     BufferedStream(Stream* stm):DecoratorStream(stm){
 98         
 99     }
100     //...
101 };
102 
103 void Process(){
104 
105     //运行时装配
106     FileStream* s1=new FileStream();
107     
108     CryptoStream* s2=new CryptoStream(s1);
109     
110     BufferedStream* s3=new BufferedStream(s1);
111     
112     BufferedStream* s4=new BufferedStream(s2);
113     
114 }
View Code
  • 有相同的字段,尽量往上(基类)提(decorator3)
  • 设计一个中间类DecoratorStream,继承Stream
  • decorator1的类个数:1+n+n*m!/2
  • decorator1的类个数:1+n+1+m

示例2

View Code

联系

  • 适配器模式可对已有对象接口进行修改,装饰模式可在不改变对象接口的前提下强化对象功能
  • 装饰模式可改变对象的外表,策略模式可改变对象的本质
  • 装饰和代理:都基于组合原则,即一个对象将部分工作委派给另一对象,但代理通常自行管理其服务对象的生命周期,而装饰的生成则总是由客户端进行控制
  • 装饰和组合:都可通过递归组织无限数量的对象,但装饰仅有一个子组件,且为被封装对象添加了额外职责,而组合仅对子节点的结果进行求和
  • 装饰和责任链:都依赖递归组合将需要执行的操作传递给一系列对象,但责任链的管理者可以独立地执行一切操作,还可以随时停止传递请求,而装饰仅可在遵循基本接口的情况下扩展对象的行为,且无法中断请求的传递

总结

  • 动态(组合)地给一个对象增加一些额外职责,就增加功能而言,Decorator模式比继承更为灵活(消除重复代码 & 减少子类个数)
  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所有的接口。但实际上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类
  • 主体操作和扩展操作应该分开

参考

https://refactoringguru.cn/design-patterns/decorator

posted @ 2020-04-04 19:39  cxc1357  阅读(146)  评论(0编辑  收藏  举报