• Target(目标接口):所需要转换的所期待的接口
  • Adaptee(源角色):需要适配的接口
  • Adapter(适配器):将原接口适配成目标接口,继承原接口,实现目标接口

一些流处理器是对其它类型的流源的适配。这就是适配器模式的应用

FutureTask接收Runnabel转换为Callable

1627660307124-a44a5124-c143-48bf-a777-5d786e088679.png

使用的是Executors 里面的RunnableAdapter

自己有Runnable属性,自己还实现了Callable接口。在call里面调用了run方法,实现适配、

传进去Runnable,拿到一个Callable来使用

1617066758494-cd589987-cbde-4cb6-9cf2-4020f05fa24d.png

[Input/Output]Stream[Reader/Writer]流

InputStreamReader实现了Reader接口,并持有了InputStream的引用,这里是通过StreamDecoder类间接持有的,因为从byte到char要经过解码。

适配器就是InputStreamReader类,

源角色就是InputStream代表的实例对象,

目标接口就是Reader类了。

1616823119832-34093fcd-d993-43c2-b31b-46e3c0710462.png

1616823220244-9054720f-8920-4bc6-a7f6-92a9301b9cdf.png

I/O类库中还有很多类似的用法,如StringReader将String类适配到Reader接口,ByteArrayInputStream适配器将byte数组适配到InputStream流处理接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.deltaqin.designPattern.d11_adapter;

import java.io.*;

/**
* @author deltaqin
* @date 2021/3/27 1:38 下午
*/
public class Demo {
public static void main(String[] args) throws Exception {

File f = new File("./test.data");
// 字节输出流
FileOutputStream fos = new FileOutputStream(f);
// 字符一个一个输出流
OutputStreamWriter osw = new OutputStreamWriter(fos);
// 字符一行一行输出流
BufferedWriter bw = new BufferedWriter(osw);
bw.write("http://www.com");
bw.flush();
bw.close();


FileInputStream fis = new FileInputStream("./test.data");
// InputStreamReader 转换,一次读取一个字符
InputStreamReader isr = new InputStreamReader(fis);
// 一次读取一行字符
BufferedReader br = new BufferedReader(isr);

String line = br.readLine();
while (line != null && !line.equals("")) {
System.out.println(line);
line = br.readLine();
}
br.close();
}

}

装饰者与适配器

适配器模式的意义是将一个接口转变成另一个接口,通过改变接口达到重复使用的目的;适配器****重点在于转换

装饰器模式不是要改变被装饰对象的接口,而恰恰要保持原有的接口,但增强原有对象的功能,或者改变原有对象的处理方法而提高性能。所有这两个模式的设计目的是不同的。装饰器****重点在于增强

适配器模式在日志框架的使用

适配器模式主要解决的是**由于接口不能兼容而导致类无法使用的问题这在处理遗留代码以及集成第三方框架的时候用得比较多。**

其核心原理是:通过组合的方式,将需要适配的类转换成使用者能够使用的接口。

1622873057685-f0935cac-e9eb-493e-9cce-db1bcd975f30.png

  • 目标接口(Target):使用者能够直接使用的接口。以处理遗留代码为例,Target 就是最新定义的业务接口。
  • 需要适配的类/要使用的实现类(Adaptee):定义了真正要执行的业务逻辑,但是其接口不能被使用者直接使用。这里依然以处理遗留代码为例,Adaptee 就是遗留业务实现,由于编写 Adaptee 的时候还没有定义 Target 接口,所以 Adaptee 无法实现 Target 接口。
  • 适配器(Adapter):在实现 Target 接口的同时,维护了一个指向 Adaptee 对象的引用。Adapter 底层会依赖 Adaptee 的逻辑来实现 Target 接口的功能,这样就能够复用 Adaptee 类中的遗留逻辑来完成业务。

适配器模式带来的最大好处就是复用已有的逻辑,避免直接去修改 Adaptee 实现的接口,这符合开放-封闭原则(也就是程序要对扩展开放、对修改关闭)。

MyBatis 使用的日志接口是自己定义的 Log 接口,但是 Apache Commons Logging、Log4j、Log4j2 等日志框架提供给用户的都是自己的 Logger 接口。为了统一这些第三方日志框架,MyBatis 使用适配器模式添加了针对不同日志框架的 Adapter 实现,使得第三方日志框架的 Logger 接口转换成 MyBatis 中的 Log 接口,从而实现集成第三方日志框架打印日志的功能。