设计模式六之适配器模式(Adapter)

什么是适配者模式

适配者模式 将一个类的接口,装换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

  • 《Head First 设计模式》

适配者别名为包装器(Wrapper)

适配者模式有什么用

通常情况下,客户端可以通过目标类的接口访问它所提供的服务。有时,现有的类可以满足客户端的需求,但是它所提供的接口不一定是客户端所需要的,这可能是因为现有类中的方法名和目标类的方法名不一致等原因导致的。在这种情况下,现有的接口需要转化为客户端所需要的接口,这样保证了对现有类的重用。如果不进行这样的转化客户端就不能利用现有类所提供的功能,而适配器模式就可以完成这样的转化。

在适配器模式中,可以定义一个适配类,或者叫包装类,包装不兼容的接口的对象,它所包装的对象就是适配器类(Adaptee),即被适配的类。

适配器提供客户端所需要的接口,适配器的实现就是把客户端的请求转化为对被适配者的相应接口的调用。也就是说,当客户端调用适配器的方法时,在适配器内部将调用被适配者类的方法,而这个过程对客户端来说是透明的,客户类并不直接访问被适配者类。因此,适配器可以使由于接口不兼容而不能交互的类一起工作。

模式结构

适配器模式包含的角色如下:

  • Target:目标抽象类
  • Adapter:适配器类
  • Adaptee:被适配者类
  • Client:客户端

类图

适配者模式的使用

1
2
3
4
5
6
7
8
9
10
11
package com.github.archerda.designpattern.adapter;
/**
* Target:目标抽象类
*
* Create by archerda on 2017/11/20.
*/
public interface Duck {
void quack();
void fly();
}
1
2
3
4
5
6
7
8
9
package com.github.archerda.designpattern.adapter;
/**
* Create by archerda on 2017/11/20.
*/
public interface Turkey {
void gobble();
void fly();
}
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
package com.github.archerda.designpattern.adapter;
/**
* Adapter:适配器
*
* Create by archerda on 2017/11/20.
*/
public class TurkeyAdapter implements Duck {
// Adaptee:被适配者
private Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
@Override
public void quack() {
turkey.gobble();
}
@Override
public void fly() {
for (int i = 0; i < 5; i++) {
turkey.fly();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.github.archerda.designpattern.adapter;
/**
* Client:客户端
*
* Create by archerda on 2017/11/20.
*/
public class Main {
public static void main(String[] args) {
// 要的是Duck,却只有Turkey,那就写个适配器吧
Turkey turkey = new WildTurkey();
Duck adapter = new TurkeyAdapter(turkey);
adapter.quack();
adapter.fly();
}
}

优点

  • 将目标类与适配者类通过适配器解耦,可以重用代码,而无需修改原来的代码;
  • 扩展性很好,可以很方便地更换适配器,符合开闭原则
  • 被适配的任何子类,都可以搭配适配器使用;

缺点

  • 替换被适配者的方法比较麻烦,需要写一个子类继承适配者类,然后覆盖适配者的方法。再把这个子类作为最后的适配者类与适配器进行配合,比较麻烦。

应用场景

下面这些场景可以考虑使用适配器模式:

  • 系统需要使用现有的类,但是这些类的接口不符合系统的需要;
  • 想要建立一个可以重用的类,这个类关联其他类进行工作,而且要考虑到将来可能会引入新的类;

扩展

当不需要实现所有目标接口的方式时,可提供一个抽象类实现目标接口,并为接口方法提供默认方法(空方法),那么该抽象类的子类就可以有选择性的实现方法来完成需求。它适用于一个接口不想使用其所有方法的情况,这种模式也叫做 单接口适配器模式

在JDK中的应用

在JDK的io包中,有两个类,分别是InputStreamReaderOutputStreamWriter,用于把字节流适配成字符流。

适配图

具体应用:

1
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("text.txt"))));

在Spring中的应用

在Spring AOP中,使用Advice(通知)来增强被代理类的能力。通过在被代理类执行的方法前设置拦截器,增强了被代理方法的功能。位于org.springframework.aop.framework.adapter包中的AdvisorAdapter是一个适配器接口,它定义了自己支持的Advice类型,并且能把一个Advisor适配成MethodInterceptor。

被适配抽象类:Advice
被适配具体类:MethodBeforeAdvice AfterReturningAdvice ThrowsAdvice
适配器抽象类:AdvisorAdapter
适配器具体类:MethodBeforeAdviceAdapter AfterReturningAdviceAdapter ThrowsAdviceAdapter
目标类抽象类:MethodBeforeAdviceInterceptor AfterReturningAdviceInterceptor ThrowsAdviceInterceptor
客户端:DefaultAdvisorAdapterRegistry

// TODO 这块其实理解的还不清晰,后续学习Spring源码后再进行补充。

参考文档