适配器是将两个不兼容的类融合在一起,它有点像粘合剂,将不同的东西通过一种转换使得它们能够协作起来。例如,经常碰到要在两个没有关系的类型之间进行交互,第一个解决方案是修改各自类的接口,但是如果没有源代码或者我们不愿意为了一个应用而修改各自的接口,此时怎么办?这种情况我们往往会使用一个 Adapter ,在这两种接口之间创建一个 “混血儿” 接口,这个 Adapter 会将这两个接口进行兼容,在不修改原有代码的情况下满足需求。

1 适配器模式 UML

图片来源于《Android 源码设计模式解析与实战》

  • 冲突:Target 期待调用 operation2 方法,而 Adaptee 并没有(这就是所谓的不兼容了)。
  • 解决方案:为使 Target 能够使用 Adaptee 类里的 operation3 方法,故提供一个中间环节Adapter类(继承 Adaptee & 实现 Target 接口),把Adaptee 的 API 与 Target 的 API 衔接起来(适配)。
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
//目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
public interface Target {
void operation2();
}

//适配者即被适配的角色,它定义了一个已经存在的接口,
// 这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
public class Adaptee {
public void operation3() {
System.out.println("被适配者的方法");
}
}

//适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,
// 适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
public class Adapter extends Adaptee implements Target{
@Override
public void operation2() {
//...一些操作...
super.operation3();
//...一些操作...
}
}

Target adapterTarget = new Adapter();
adapterTarget.operation2();

这是继承的方式,当然也可以用组合的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Adapter implements Target {
Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}

@Override
public void operation2() {
//...一些操作...
adaptee.operation3();
//...一些操作...
}
}

Target adapterTarget = new Adapter(new Adaptee());
adapterTarget.operation2();

2 装饰模式案例 电压适配器

我们国家的民用电都是 220V,有些国家是 110V,而我们的手机充电一般需要 5V,这时候要充电,就需要一个电压适配器,将 220V 或者 100V 的输入电压变换为 5V 输出。

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//Adaptee
public interface AC {
int outputAC();
}
public class AC110 implements AC {
public final int output = 110;
@Override
public int outputAC() {
return output;
}
}
public class AC220 implements AC {
public final int output = 220;
@Override
public int outputAC() {
return output;
}
}

//Target
public interface DC5Adapter {
//用于检查输入的电压是否与适配器匹配
boolean support(AC ac);

//用于将输入的电压变换为 5V 后输出
int outputDC5V(AC ac);
}

//adapter
public class PowerAdapter110 implements DC5Adapter {
public static final int voltage = 110;

@Override
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}

@Override
public int outputDC5V(AC ac) {
int adapterInput = ac.outputAC();
//变压器...
int adapterOutput = adapterInput / 22;
System.out.println("使用 PowerAdapter110 变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
return adapterOutput;
}
}
public class PowerAdapter220 implements DC5Adapter {
public static final int voltage = 220;

@Override
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}

@Override
public int outputDC5V(AC ac) {
int adapterInput = ac.outputAC();
//变压器...
int adapterOutput = adapterInput / 44;
System.out.println("使用 PowerAdapter220 变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
return adapterOutput;
}
}

//test
public class Client {
private List<DC5Adapter> adapters = new LinkedList<>();

public Client() {
this.adapters.add(new PowerAdapter220());
this.adapters.add(new PowerAdapter110());
}

// 根据电压找合适的变压器
public DC5Adapter getPowerAdapter(AC ac) {
DC5Adapter adapter = null;
for (DC5Adapter ad : this.adapters) {
if (ad.support(ac)) {
adapter = ad;
break;
}
}
if (adapter == null) {
throw new IllegalArgumentException("没有找到合适的变压适配器");
}
return adapter;
}

public void Adapter() {
Client test = new Client();
// AC ac = new AC220();
AC ac = new AC110();
//通过外部传入 ac 确定电压适配器
DC5Adapter adapter = test.getPowerAdapter(ac);
adapter.outputDC5V(ac);
}
}

4 Android 源码中的适配器模式

说起 Android 最常见的就是 Adapter 了吧,那就整个学习一下 RecyclerView 、LayoutManager 和 Adapter

通过桥接模式,使 RecyclerView 将布局方式独立成 LayoutManager,实现对布局的定制化。

通过组合模式,使 RecycleView 通过 dispatchLayout 对 Item View 进行布局绘制的。

通过适配器模式,ViewHolder 将 RecycleView 与 ItemView 联系起来,使得 RecycleView 方便操作 ItemView。

通过观察者模式,给 ViewHolder 注册观察者,当调用 notifyDataSetChanged 时,就能重新绘制。

RecyclerView 源码解析

RecyclerView 相关面试

感谢

设计模式之美

《Android 源码设计模式解析与实战》

以及上文中的链接