适配器和装饰器非常像,在学习的过程中也非常容易搞混淆,因此希望通过文字记录学习的方式的深入理解两种设计模式的差异。
适配器主要是将一个类的接口转换成客户期望的另一个类的接口,重点关注接口层面。
装饰器重点关注对原有对象的增强,原有对象不能满足现有的需求通过装饰类来对原有对象的修饰。
装饰器使用的还是原先的抽象方法,让你毫无感知地被装饰了,而适配器涉及到了接口的改变了。
为了理解这两种模式的不同,我们把Java IO作为例子,
装饰器为例:
以InputStream为例:InputStream作为一个抽象方法内部有read()等一些列方法,该方法通过具体的子类实现
该抽象方法由具体的子类去实现,通过InputStream的族谱图可以看到,直接继承了InputStream,并且提供某一特定功能的子类有,具体有以下几种:
-
ByteArrayInputStream
-
FileInputStream
-
ObjectInputStream
-
PipedInputStream
-
SequenceInputStream
-
StringBufferInputStream
这些类都具有特定的功能,比如FileInputStream具备简单的文件输入功能。其中InputStream有个子类叫做FilterInputStream,该类与上述其它类的功能非常不通,而且该类内部持有了InputStream的引用。FilterInputStream的子类有
-
BufferedInputStream
-
DataInputStream
-
LineNumberInputStream
-
PushbackInputStream
以下面这段代码为例,FilerInputStream就是一个装饰器,BufferedInputStream就是具体的装饰者,FilerInputStream持有了FileInputStream的引用BufferedInputStream对FileInputStream功能做了增强使其具备了缓冲的功能,使用的接口还是FilerInputStream的接口,接口并没有发生变化但是功能却发生了变化。
File file = new File("hello.txt");
FileInputStream in = new FileInputStream(file);
BufferedInputStream inBuffered = new BufferedInputStream(in);
inByffered.read();
适配器说明,以下面代码为例:
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("text.txt"))));
我们知道IO流分为字节流(InpouStream、OutStream)和字符流(Reader、Writer),我们要想从文件中读取字符并直接输出只能通过字符流来读取。可以看到InputStreamReader就是将InputStream适配到Reader接口,通过适配器InputStreamReader持有FileInputStream对象来实现,这样BufferedReader就可以直接读取字符流了,因为InputStreamReader已经帮我们适配到了FileInputStream上面了,内部还是通过调用InputStream接口来读取。
从上面例子可以看到,适配器解决的将某个接口转换成客户期望的另一个接口的实现,上述例子就是将InpouStream接口转换成Reader接口,涉及到了接口变化,当前接口适配到另一个接口以满足客户对另一个接口的要求。
而装饰器用的还是一个接口InpouStream,解决的对当前接口实现者的增强,提供一个包装类实现当前接口并持有需要增强的类的引用从而来对需要装饰的对象的动态增强。装饰器并没有涉及到接口的改动,使用的还是相同的接口。