Java I/O中的设计模式

433 阅读5分钟

一、Java I/O中的适配器模式

适配器模式主要考虑的目的就是兼容,具体操作就是将一个类的接口转换称为客户端所能够接收的另外一种接口,从而使得客户端能够在不改变使用方式的情况下间接调用另外一个类。

这一模式通常被使用在一些项目需要引用外部框架来实现功能的情况,这些框架的内部都有一些关于环境信息的接口,需要从外部引入,但是外部的接口不一定能匹配,在这种情况下,可以使用适配器模式来转换接口。

1. 适配器模式的结构

  • 目标(Target):即期望实现的接口,客户端最终调用的接口
  • 适配器(Adapter):用于将源接口转换成目标接口的类
  • 被适配器(Adaptee):源角色,即需要被适配的接口

2. Java I/O 中的适配器模式

在Java I/O中又很多可以用适配器实现的需求,如将字符串数据编程字节数据保存在文件中,将字节数据编程流数据等,下面以InputStreamReader为例子介绍。

2.1 具体结构

InputStreamReader继承了Reader接口,但是新建对象时,还需要在构造器中传入一个InputStream的实例对象,InputStreamReader的作用也就是将InputStream匹配到Reader中,结构如下所示:

2.2 代码分析

// 继承Reader,实现Read接口
public class InputStreamReader extends Reader {
    
    // 用于byte到char之间的编码
    private final StreamDecoder sd;

    public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            // The default encoding should always be available
            throw new Error(e);
        }
    }
    
    ...
    // 通过 StreamDecoder 实力对象调用 InputStream 中方法
    public int read() throws IOException {
        return sd.read();
    }
    
    ...
}

InputStreamReader实现了Reader接口,并持有了InputStream的引用,在这里是通过StreamDecoder间接持有的,因为byte到char要经过编码。之后可以通过sd实例对象访问in实例对象的中的方法。

很显然,在这里InputStream就是源角色,是需要被适配的的接口,而InputStreamReader是适配器,用于将源接口转换成目标接口,实现了Reader接口,并通过StreamDecoder间接调用了源角色中的函数。这就是Java IO中适配模式的一个实现例子,类似的例子还有许多,比如OutputStreamWriterStringReader等。

二、Java I/O中的装饰器模式

装饰者模式的目的是增强,顾名思义,就是将某个类从新装饰一下,使这个类的功能比原来更加强大,这就是装饰者模式所要达到的目的。除此之外,不能使原来这个类的使用者感受到装饰前后有什么异同,否则就是破坏原有的类的结构了,所以装饰者模式还要做到对被装饰类透明,这是对装饰者模式的一个要求。

1. 装饰者模式的结构

  • 抽象组件(Component):接口,定义一个抽象接口装饰对象与真实对象具有相同的接口,以便装饰器动态地添加职责;
  • 具体组件(ConcreteComponent):实现这个抽象组件的所有功能;
  • 装饰器(Decorator):装饰类,继承Component,从外类来拓展Component的功能,并且持有一个Component的引用,通过构造器实例化,从而实现对真实对象的职责装饰增强;
  • 具体装饰器(ConcreteComponent):具体装饰类,用于给实际对象添加职责。

2. Java I/O 中的装饰者模式

Java I/O中有很多不同的功能组合情况,这些不同的功能组合都是使用装饰器模式实现的,下面以FilterInputStream为例介绍装饰器模式的使用。

2.1 具体结构

其中InputStream类是以抽象组件存在的,而FilterInputStream就是具体组件,它实现了组件的所有接口,并且持有InputStream的对象实例的引用;而BufferedInputStream是具体的装饰器实现,它给原有的类附加了功能。

2.2 代码分析

// 实现InputStream接口
public class FilterInputStream extends InputStream {
    
    protected volatile InputStream in;
    
    // 注入InputStream组件的实现类的实例对象
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }

    // 在这里使用这个实例对象
    public int read() throws IOException {
        return in.read();
    }
    
    ...
    
}

BufferedInputStreamInputStream类附加了功能,这个装饰器类的作用就是使得InputStream读取的数据保存在内存中,从而提高读取的性能。

public class BufferedInputStream extends FilterInputStream  {
    
    ...

    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    
    ...
}

三、适配器模式与装饰者模式的区别

装饰者模式与适配器模式都有一个别名就是包装模式(Wrapper),它们看似都是起到包装一个类或对象的作用,但是使用它们的目的很不一样。适配器模式的意义是要将一个接口转变成为另外一个接口,它的目的是通过改变接口来达到重复使用的目的;而装饰器模式是要改变被装饰对象的接口,而是恰恰要保持原有的接口,但是增强原有对象的功能,或者改变原有对象的处理方法而提升性能,所以这两个模式设计的目的是不同的。

文章参考: