美文网首页
结构型 装饰器模式(文末有项目连接)

结构型 装饰器模式(文末有项目连接)

作者: _River_ | 来源:发表于2021-05-03 09:15 被阅读0次
    1:从IO引入(奇怪的IO类)
    Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读取和写入。
    如果对 Java IO 类做一下分类,我们可以从下面两个维度将它划分为四类。具体如下所示:
    
    字节流 字符流
    输入流 InputStream Reader
    输出流 OutputStream Writer
    基于这4个父类上 扩展出很多子类
    
    曾经对 Java IO 的一些用法产生过很大疑惑:
    我们打开文件 test.txt,从中读取数据。
    其中,InputStream 是一个抽象类,FileInputStream 是专门用来读取文件流的子类。
    BufferedInputStream 是一个支持带缓存功能的数据读取类,可以提高数据读取的效率。
    
    需要先创建一个 FileInputStream 对象,然后再传递给 BufferedInputStream 对象来使用。
    Java IO 为什么不设计一个继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类
    
        InputStream inputStream = new FileInputStream("A:\\test.txt");
        InputStream bufferedInputStream = new BufferedInputStream(inputStream);
        byte[] data = new byte[128];
        while (bufferedInputStream.read(data) != -1) {
          //...
        }
    
        //继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类
       InputStream bufferedFileInputStream = new BufferedFileInputStream("A:\\test.txt");
       byte[] data = new byte[128];
       while (bufferedFileInputStream.read(data) != -1) {
        //...
    }
    
    2:基于继承的设计方案
    如果 InputStream 只有一个子类 FileInputStream 的话,那我们在 FileInputStream 基础之上,再设计一个孙子类 BufferedFileInputStream,
    也算是可以接受的,毕竟继承结构还算简单。
    但实际上,继承 InputStream 的子类有很多。我们需要给每一个 InputStream 的子类,再继续派生支持缓存读取的子类。
    
    除了支持缓存读取之外,如果我们还需要对功能进行其他方面的增强,
    比如下面的 DataInputStream 类,支持按照基本数据类型(int、boolean、long 等)来读取数据。
    
        InputStream inputStream = new FileInputStream("A:\\test.txt");
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        int data = dataInputStream.readInt();
    
         //继承 FileInputStream 并且支持基本数据类型的 dataFileInputStream  类
        InputStream dataFileInputStream = new DataFileInputStream("A:\\test.txt");
        int data = dataFileInputStream.readInt();
    
    假如说这时候需要一个支持缓存、又支持按照基本类型读取数据的类,
    那就要再继续派生出 BufferedDataFileInputStream 
    那么后面会变得没完没了。
    
    3:基于装饰器模式的设计方案
    之前还讲到“组合优于继承”,可以“使用组合来替代继承”。
    针对刚刚的继承结构过于复杂的问题,我们可以通过将继承关系改为组合关系来解决。
    下面的代码展示了 Java IO 的这种设计思路。
    
     那装饰器模式就是简单的“用组合替代继承”吗?
    当然不是。从 Java IO 的设计来看,装饰器模式相对于简单的组合关系,通常两个比较特殊的地方。
    注意要有共同的接口类 或者 抽象类
    
    1:第一个比较特殊的地方是:
        装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。
        我们对 FileInputStream 嵌套了两个装饰器类:
        BufferedInputStream 和 DataInputStream,让它既支持缓存读取,又支持按照基本数据类型来读取数据。
    
    
    第二个比较特殊的地方是:
        装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。
        拿比较相似的代理模式和装饰器模式来对比:
        代理模式中,代理类附加的是跟原始类无关的功能,
        装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。
    
    4:实战例子(对IO类的各种增强)
    public class BufferedInputStream extends InputStream {
        protected volatile InputStream in;
        protected BufferedInputStream(InputStream in) {
            System.out.println("提供支持缓存读取数据 功能");
            this.in = in;
        }
        //...实现基于缓存的读数据接口...
        @Override
        public int read() throws IOException {
            return 0;
        }
    }
    
    public class DataInputStream extends InputStream {
        protected volatile InputStream in;
        protected DataInputStream(InputStream in) {
            System.out.println("提供基本类型读取数据 功能");
            this.in = in;
        }
        //...实现读取基本类型数据的接口
        @Override
        public int read() throws IOException {
            return 0;
        }
    }
    
    public class FileInputStreamDemo {
    
        public void FileInputStreamDemo() throws IOException {
            InputStream inputStream = new FileInputStream("A:\\test.txt");
            //BufferedInputStream  继承了 InputStream  提供支持缓存读取数据 功能
            InputStream bufferedInputStream = new BufferedInputStream(inputStream);
            //DataInputStream  继承了 InputStream 提供基本类型读取数据 功能
            InputStream dataInputStream =new DataInputStream(bufferedInputStream);
    
            byte[] data = new byte[128];
            while (dataInputStream.read(data) != -1) {
                //...
            }
        }
    }
    
    5:实战例子(结束订单的积分抵扣)
    1:正常的订单接口类(或者抽象类)   
    2:正常的订单实现类( 或者子类)
    3:装饰器类 传入订单实现类 或者子类)  对其同名方法做了增强
    4:调用时  
         可以使用  订单实现类(或者子类)   
         可以使用  订单实现类(或者子类)的 装饰器类
    
    // 代理模式的代码结构(下面的接口也可以替换成抽象类)
    public interface OrderService {:
        void finishOrder(int amount);
    }
    
    public class OrderServiceImpl implements OrderService {
    
        @Override
        public void finishOrder(int amount) {
            System.out.println("结束订单 扣除:"+amount);
        }
    }
    
    public class OrderServiceImplDecorator implements OrderService {
    
        private OrderService orderService;
    
        public OrderServiceImplDecorator(OrderService orderService) {
            this.orderService = orderService;
        }
    
        @Override
        public void finishOrder(int amount) {
            //积分抵扣
            amount = amount -5;
            orderService.finishOrder(amount);
        }
    }
    
    public class OrderDecoratorDemoStart {
    
        public static void main(String[] args) {
    
            OrderService orderService =new OrderServiceImpl();
            orderService.finishOrder(10);
    
            //对OrderService 的实现类的具体方法做了增强
            OrderService orderServiceOther =new OrderServiceImpl();
            OrderService orderServiceDecorator =new OrderServiceImplDecorator(orderServiceOther);
            orderServiceDecorator.finishOrder(10);
        }
    }
    
    6:装饰器小结
    装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。
    
    1:它可以可以对原始类嵌套使用多个装饰器。
        注意:装饰器类需要跟原始类继承相同的抽象类或者实现相同接口类。
    
    2:它主要的作用是给原始类添加增强功能。
        注意:装饰器类方法名 最好与原始类一致
    

    项目连接

    请配合项目代码食用效果更佳:
    项目地址:
    https://github.com/hesuijin/hesujin-design-pattern
    Git下载地址:
    https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.git
    
    demo-study模块  下 structure_design_pattern  decorator包   

    相关文章

      网友评论

          本文标题:结构型 装饰器模式(文末有项目连接)

          本文链接:https://www.haomeiwen.com/subject/ejmxdltx.html