通过源码认识设计模式-装饰器模式(JAVA I/O流)

350 阅读4分钟

通过源码认识设计模式-装饰器模式(JAVA I/O流)

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

1.java/IO流

  • 什么是流 简单理解,数据在两个设备之间进行的是流传输(流的本质就是数据传输);

  • 分类

根据数据类型不同分为字符流(以字符为单位),字节流(以字节为单位)其中字节流可以处理所有类型的数据

根据数据的流向分为输入流和输出流

  • java/io的知识架构图

image.png

  1. InputStream 是所有输入字节流的父类,他是一个抽象类 其下面的子类:

    ByteArrayInputStream、StringBufferInputStream、FileInputStream 这三个是基础的输入流,他们分别读Byte 数组、StringBuffer、和文件系统中的文件中读取数据

    PipedInputStream 连接管道输入流到管道输出流;然后管道输入流提供写入管道输出流的任何数据字节 一个线程从PipedInputStream对象读取数据,并将数据写入相应的 pipeoutputstream

    ObjectInputStream和FilterInputStream都是装饰流(例如我们常用的BufferedInputStream

  2. OutputStream 是所有输出字节流的父类,它是一个抽象类。其内容和字节输入流InputStream类似 只是一个是输入 一个是输出

  3. Reader 和Writer是字符的输入输出流

2.装饰器模式

  • 我们一般要扩展一个类,常用的就是继承,但是继承过多,会导致子类膨胀,会增加代码耦合性等一系列问题,想要扩展一个类又不想增加更多子类,可以考虑使用装饰器模式:动态为一个对象增加一些额外的职能。(举一个通俗的例子:例如一件房间它主要是用来睡觉,但是我想让这个房间在睡觉的同时能够放音乐,于是我在房间安了一个音响 但是这个活动并没有改变房间原来的功能,但是却能让我在睡觉的时候更加舒服)

装饰类和他的被装饰类之间不会像继承一样 产生耦合性,并且装饰类还可以动态扩展 比继承更为灵活

代码举例: 1.创建一个接口 画图形的接口

public interface Shape {
    void draw();
}

2.创建实现接口的实体类

public class Circle implements Shape {

    @Override
    public void draw() {
        System.out.println("Shape: 圆");
    }
}

3.创建实现了 Shape 接口的抽象装饰类和实体装饰类

public abstract class ShapeDecorator implements Shape {
    Shape decoratedShape;
    public ShapeDecorator(Shape decoratedShape){
        this.decoratedShape = decoratedShape;
    }
    public void draw(){
        decoratedShape.draw();
    }
}


//实体装饰类
public class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }
    @Override
    public void draw() {
        decoratedShape.draw();
        setRedBorder(decoratedShape);
    }
    private void setRedBorder(Shape decoratedShape){
        System.out.println("颜色:红色");
    }
}

4.动态扩展

Shape circle = new Circle(); //输出:Shape: 圆
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());  
//输出:Shape: 圆
//     颜色:红色

3.代码展示

结合JAVA/IO流中FilterInputStream的子类BufferedInputStream来看装饰器模式:

  • BufferedInputStream他有InputStream所有的功能 除此之外他还为输入流提供缓冲功能和支持mark和reset方法的能力 当BufferedInputStream被创建时,将创建一个内部缓冲区数组。当读取或跳过流中的字节时,将根据需要从所包含的输入流中重新填充内部缓冲区 源码:

image.png

reset(),mark() (BufferedInputStream中) image.png

image.png

  • mark()方法:标记此输入流中的当前位置
  • reset()方法:将输入流重新定位到最后一次在此输入流上调用mark()方法标记的位置
  • pos: 冲区中的当前位置
  • markpos: 调用最后一个mark()方法时,pos字段的值,值总是在-1到pos的范围内
  • marklimit: 用reset方法失败之前,在调用mark方法之后允许的最大预读,当pos和markpos之间的差异超过marklimit时,可以通过将markpos设置为-1来删除该标记

reset(),mark() (InputStream中) image.png

image.png

通过对比展示 即可知道BufferedInputStream实际上是InputStream的一个装饰类 BufferedInputStream在读字节流的时候创建一个缓冲区,这个缓冲区减少了对字节操作的开销 提高了效率。

4.总结

装饰器模式 为提供扩展一个类提供了一个思想,相比继承,装饰器类可以降低程序的耦合性,扩展使用更灵活,具体何时使用装饰器模式要视情况而定,而不能为了使用装饰器模式而使用装饰器模式。