IO

参考:
深入分析 Java I/O 的工作机制
Java IO 中的“装饰模式”和“适配器模式”

装饰者模式

参考:图说设计模式–装饰模式
当考虑为一个类增加功能时,我们可以使用继承机制。但是当功能繁多并且要不断嵌套复用时,继承就不是一个好的选择了。对此我们可以采用装饰模式来达到这个目的。即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)

模式UML图

装饰模式包含如下角色:

  • Component: 抽象构件(抽象被装饰类)
  • ConcreteComponent: 具体构件(具体被装饰类)
  • Decorator: 抽象装饰类
  • ConcreteDecorator: 具体装饰类


如上图所示,抽象装饰类及其子类会持有一个抽象被装饰类的私有域,并在其继承方法中对应的调用其抽象被装饰类的方法。而具体装饰类则通过继承抽象装饰类后,在对应方法上加上自己的附加行为,完成行为的扩展。由于持有同样的抽象被装饰类,因此这些具体装饰类可以嵌套使用。

模式分析

  • 与继承关系相比,关联关系的主要优势在于不会破坏类的封装性,而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。在软件开发阶段,关联关系虽然不会比继承关系减少编码量,但是到了软件维护阶段,由于关联关系使系统具有较好的松耦合性,因此使得系统更加容易维护。当然,关联关系的缺点是比继承关系要创建更多的对象。
  • 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
  • 对比于静态代理,装饰模式更倾向于不同装饰类间的叠加组合使用,而不是对某一个具体类实行监管。并且装饰类需要传入一个具体被装饰类,而静态代理则在自身内部新建。

适配器模式

参考:
Java与模式之适配器(Adapter)模式
适配器模式可以分为类的适配对象的适配两种。其目的是为了将两个已经确定的类进行适配转换而存在的—由于无法改动两个已有类,只能通过搭桥的方式进行连通。

类的适配—基于继承


如上图所示,类的适配器用于如下情况:有一客户端类Client实现了Target接口,现有一个Client对象想要调用Adaptee对象,但Adaptee类并未实现Target接口。因此我们可以采用的方法是通过建立一个适配器类Adapter,其继承于Adaptee类,并实现了Target接口。这样就可以让Client对象调用Adapter类从而间接的调用了Adaptee对象了。

对象的适配—基于引用

如上图所示,对象适配器Adapter并不会继承Adaptee,而是在自己内部建立一个Adaptee对象的引用,并在相应的方法上进行直接调用Adaptee对象相应的方法。(类似静态代理)

类适配和对象适配的区别

  • 类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。
  • 对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理Adaptee的子类了。对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
  • 对于类适配器,适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。对于对象适配器,要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
  • 对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。对于对象适配器,需要额外的引用来间接得到Adaptee。

摘自:Android设计模式源码解析之适配器(Adapter)模式

IO包

Java的IO包具体可以分为流式部分,非流式部分以及其他涉及安全等方面的类。

流式部分

流式部分包括了字节流的读写类和字符流的读写类。字节流常用于图片、音频、视频等非字符持久化数据的读写,字符流则是用于在编程及文本数据的读写。字节流包括了InputStreamOutputStream两个抽象类,下面继承了很多具体的类。字符流则包括了ReaderWriter两个抽象类。
InputStream类的UML视图看看其构成。其余的UML图可以参看:Java IO 知识整理

从上图我们可以看出,InputStream类就是按照装饰模式进行实现的,首先有一个抽象被装饰类InputStream,然后又一些具体的被装饰类(eg.FileInputStream),接着有一个抽象装饰类(FilterInputerStream)。最后,通过继承该抽象类,又有一系列的具体装饰类。

另一方面,由于有时候会存在要将字节流输入转为字符流输入的情况,因此在Reader类下存在着一个InputStreamReader类,该类就是使用了对象的适配器设计模式。通过该类,我们可以将一个字节流输入转换成字符流输入。
部分源码如下:

public class InputStreamReader extends Reader {
    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);
        }
    }
....
----------------------
public class StreamDecoder extends Reader{
....
    private InputStream in;
    StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
        ....
        this.in = in;
        ....
    }

从上可以看出,InputStreamReader继承于Reader类,因此可以被其他Reader类的装饰器装饰,而其本身接受的是一个InputStream类的字节流类,通过持有该类的对象从而达到适配器的目的。细心的同学可能发现,StreamDecoder类也是Reader类啊,怎么就是持有了InputStream对象了呢?我们继续看看StreamDecoder类的源码就可以看到,由于要进行转码编译,因此才需要这个类,而这个StreamDecoder类本身是持有InputStream对象的,因此InputStreamReader就间接持有了InputStream对象。

后记:关于常用的网络Socket(属于java.net.*包)和非流式部分的File类,RandomAccessFile类等将在之后的时间在慢慢总结,先挖坑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值