设计模式之适配器模式

适配器模式定义

将一个类的接口转换为另一个接口,以满足特定场景的需要。
举个栗子,比如三孔插头通过一个转换头转换为两孔插头;比如通过转接线将普通耳机接口转换为苹果方孔耳机接口等。

适配器模式允许通常因为接口不兼容而不能在一起工作的类相互配合完成工作。

适配器模式类图

F7su6I.png

可以看到适配器模式包含以下角色:

  • Target:目标接口,即用户所需的接口。
  • Adaptee:适配者类,即被适配的角色,是一个已经存在的接口,其中有用户需要的方法。
  • Adapter:适配器类,可以理解为转换器,通过它调用另一个接口(Adaptee)完成适配,是适配器模式的核心。

适配器模式分为类适配器和对象适配器两种。以上类图为对象适配器类图。

类适配器

类适配器的重点是通过继承 Adaptee类来构造一个适配器。

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
//目标接口
interface Target {
void request();
}
//普通目标类,只有普通方法
class CommonTarget implements Target {
@Override
public void request() {
System.out.println("我是一个普通方法");
}
}
//适配者类,拥有特殊方法
class Adaptee {
public void specialRequest() {
System.out.println("我是一个特殊方法");
}
}
//适配器类
class Adapter extends Adaptee implements Target{
@Override
public void request() {
super.specialRequest();
}
}
//测试类
public class ClassAdapter {
public static void main(String[] args) {
//通过普通类调用普通方法
Target commonTarget = new CommonTarget();
commonTarget.request();
//通过适配器类调用特殊方法
Target target = new Adapter();
target.request();
}
}

对象适配器

对象适配器的重点是通过组合的方式,包含一个Adaptee类对象来构造一个适配器。

以普通圆孔耳机借助转换头转换为苹果手机可以使用的方孔耳机为例。
场景:想用苹果手机听歌,但是没有方孔耳机,同学有一个普通圆孔耳机,怎么办?通过适配器模式生成一个转接头就可以听歌啦。来看代码实现:

方孔耳机,即客户所需接口Target(SquareEar接口)
1
2
3
4
5
6
7
8
9
public class AppleSquareEar implements SquareEar {
@Override
public void square() {
System.out.println("一个苹果方孔耳机");
}
}
public interface SquareEar {
void square();
}
普通圆孔耳机,即适配者Adaptee类
1
2
3
4
5
6
7
8
9
public class CommonRoundEar implements RoundEar{
@Override
public void round() {
System.out.println("一个普通圆孔耳机");
}
}
public interface RoundEar {
void round();
}
转接头,即 Adapter 适配器类(核心),持有Adaptee类
1
2
3
4
5
6
7
8
9
10
11
12
13
public class EarAdapter implements SquareEar {
private RoundEar roundEar; //持有Adaptee类
public EarAdapter(RoundEar roundEar) {
this.roundEar = roundEar;
}
@Override
public void square() {
roundEar.round();
}
}
苹果手机,我需要方孔耳机才能听歌!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ApplePhone {
public static void main(String[] args) {
//假如我有方孔耳机,那么我可以直接拿来听歌
SquareEar squareEar = new AppleSquareEar();
listen(squareEar);
//假如我没有方孔耳机,借助转换器,通过同学的圆孔耳机,我也可以听歌呀
RoundEar roundEar = new CommonRoundEar();
EarAdapter earAdapter = new EarAdapter(roundEar);
listen(earAdapter);
}
public static void listen(SquareEar squareEar) {
System.out.println("我开始听歌了!我用的耳机是:");
squareEar.square();
}
}

可以看到,通过适配器模式将 方孔耳机接口 和 圆孔耳机接口 这两个原本不能兼容的接口一起工作。这就是适配器模式。

适配器模式优缺点及应用场景

优点
  • 复用:现有的类可以实现功能,可以复用
  • 透明:对客户端透明,因为调用的是同一接口
  • 解耦:通过引入适配器类,将目标类和适配者类解耦
  • 扩展性:可在适配器类中添加自己实现的业务功能,提高扩展性
缺点
  • 复杂性提高:过多使用适配器会使代码复杂,接口间的调用凌乱
  • 类适配器一次只能适配一个适配者类,因为Java单继承
  • 对象适配器不易改变适配者类的方法,需要修改源码
应用场景
  • 两个类所做的事情相同或相似,但是具有不同接口的时候。
  • 当使用第三方组件的时候,组件的接口和自定义的接口不同,此时又不希望修改自己的接口,可以使用适配器模式将组件的接口适配到自定义的接口。
  • 想要重用现有的类,但是类的接口不符合。
  • Spring 中 AOP 模块对 BeforeAdvice、 AfterAdvice、 ThrowsAdvice 三种通知类型的支持实际上是借助适配器模式来实现的
  • Java IO 流中字节流与字符流之间的转换也用到了适配器模式