zoukankan      html  css  js  c++  java
  • [设计模式] 设计模式课程(五)--装饰模式

    场景

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

  • 相关阅读:
    力扣(LeetCode)验证回文串 个人题解(C++)
    力扣(LeetCode)平方数之和 个人题解
    Exclusive Access 2 UVA
    C语言中指针*p[N], (*P)[N]的区别
    2018年蓝桥杯国赛比赛心得
    2018acm/icpc西安邀请赛比赛心得
    [最小割]Cable TV Network UVA
    Tree Reconstruction UVA
    Twenty Questions UVA
    python中的enumerate 函数(编号的实现方式)
  • 原文地址:https://www.cnblogs.com/cxc1357/p/12293267.html
Copyright © 2011-2022 走看看