本博客大部分内容来于免费在线学习设计模式

1:适配器模式

适配器模式是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。
20200819154420

2:适配器模式问题

例如你开发了一个程序,能从不同来源获取xml格式信息。在开发过程中,你决定整合一个第三方分析库,但是第三方分析库只兼容json格式。
也许你可以修改第三方库来支持json,但是这需要修改很多代码,甚至你可能无法修改第三方库。

该如何导出数据呢?

3:适配器模式解决方案

适配器是一个特殊的对象,能够转换对象接口,使其能与其他对象进行交互。

适配器模式通过封装对象将复杂的转换过程隐藏于幕后。被封装的对象甚至察觉不到适配器的存在。

适配器不仅可以转换不同格式的数据,其还有助于采用不同接口的对象之间的合作。它的运作方式如下:

  1. 适配器实现与其中一个现有对象兼容的接口。
  2. 现有对象可以使用该接口安全地调用适配器方法。
  3. 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。

有时候你还可以写一个双向适配器来实现双向转换调用

例:出国旅行时,不同国家的电源插头和插座标准不同。同时提供中国标准和欧洲标准插头的适配器能解决问题。

4:适配器模式结构

4-1:对象适配器

使用构成原则来实现:适配器实现了其中一个对象的接口,并对另一个对象进行封装。所有流行的编程语言都可以实现适配器。

20200819155218

  1. 客户端 (Client) 是包含当前程序业务逻辑的类。
  2. 客户端接口 (Client Interface) 描述了其他类与客户端代码合作时必须遵循的协议。
  3. 服务 (Service) 中有一些功能类 (通常来自第三方或遗留系统)。客户端与其接口不兼容,因此无法直接调用其功能。。
  4. 适配器 (Adapter)是一个可以同时与客户端和服务交互的类:它在实现客户端接口的同时封装了服务对象。适配器接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。
  5. 客户端代码只需通过接口与适配器交互即可,无需与具体的适配器类耦合。因此,你可以向程序中添加新类型的适配器而无需修改已有代码。这在服务类的接口被更改或替换时很有用: 你无需修改客户端代码就可以创建新的适配器类。

代码示例:

“方钉”和“圆孔”
适配器假扮成一个圆钉 (Round­Peg),其半径等于方钉 (Square­Peg) 横截面对角线的一半 (即能够容纳方钉的最小外接圆的半径)。

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
// 圆孔
class RoundHole {
private double radius;

public RoundHole(double radius) {
this.radius = radius;
}

// 返回孔的半径
public double getRadius() {
return radius;
}

public boolean fits(RoundPeg roundPeg) {
return this.getRadius() >= roundPeg.getRadius();
}
}

// 圆钉:和圆孔兼容
class RoundPeg {
private double radius;

public RoundPeg() {
}

// 返回圆钉半径
public RoundPeg(double radius) {
this.radius = radius;
}

public double getRadius() {
return radius;
}
}

// 方钉:和圆孔不兼容
class SquarePeg {
private double width;

public SquarePeg(double width) {
this.width = width;
}

// 返回方钉宽度
public double getWidth() {
return width;
}
}

// 适配器类让你能够将方钉放入圆孔中。它会对 RoundPeg 类进行扩展,以接收适配器对象作为圆钉。
class SquarePegAdapter extends RoundPeg {
// 方钉实例
private SquarePeg squarePeg;

public SquarePegAdapter(SquarePeg squarePeg) {
this.squarePeg = squarePeg;
}

@Override
public double getRadius() {
// 适配器会假扮为一个圆钉,其半径刚好能与适配器实际封装的方钉搭配起来。
return squarePeg.getWidth() * Math.sqrt(2) / 2;
}
}

public class Adapter {
public static void main(String[] args) {
RoundHole roundHole = new RoundHole(5);
RoundPeg roundPeg = new RoundPeg(5);
System.out.println(roundHole.fits(roundPeg));
// true

SquarePeg smallSqPeg = new SquarePeg(5);
SquarePeg largeSqPeg = new SquarePeg(10);
// roundHole.fits(smallSqPeg);
// 上诉代码无法编译

SquarePegAdapter smallSqPegAdapter = new SquarePegAdapter(smallSqPeg);
SquarePegAdapter largeSqPegAdapter = new SquarePegAdapter(largeSqPeg);
System.out.println(roundHole.fits(smallSqPegAdapter));
// true
System.out.println(roundHole.fits(largeSqPegAdapter));
// false
}
}

5:适配器模式适用场景

  1. 当你希望使用某个类,但是其接口与其他代码不兼容时,可以使用适配器类。
  2. 如果您需要复用这样一些类,他们处于同一个继承体系,并且他们又有了额外的一些共同的方法,但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。

6:适配器模式优缺点

优点 缺点
_单一职责原则_你可以将接口或数据转换代码从程序主要业务逻辑中分离。 代码整体复杂度增加,因为你需要新增一系列接口和类。有时直接更改服务类使其与其他代码兼容会更简单。
开闭原则。只要客户端代码通过客户端接口与适配器进行交互,你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。 由于java的继承机制, 至多只能适配一个适配器类
提高类的复用
灵活度高

7:适配器模式与其他模式关系

  • 桥接模式通常会于开发前期进行设计,使你能够将程序的各个部分独立开来以便开发。另一方面,适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作。
  • 适配器可以对已有对象的接口进行修改,装饰模式则能在不改变对象接口的前提下强化对象功能。此外,装饰还支持递归组合,适配器则无法实现。
  • 适配器能为被封装对象提供不同的接口,代理模式能为对象提供相同的接口,装饰则能为对象提供加强的接口。
  • 外观模式为现有对象定义了一个新接口,适配器则会试图运用已有的接口。适配器通常只封装一个对象,外观通常会作用于整个对象子系统上。
  • 桥接、状态模式和策略模式(在某种程度上包括适配器)模式的接口非常相似。实际上,它们都基于组合模式——即将工作委派给其他对象,不过也各自解决了不同的问题。模式并不只是以特定方式组织代码的配方,你还可以使用它们来和其他开发者讨论模式所解决的问题。

8:适配器模式举例

拿手机充电线举例,你现在只有一条Micorosoft USB充电线,但是手机是type-c接口,这时候只需要给充电线安装一个转接口就能给手机充电了。

9:参考