结构式:适配器模式

1 基本介绍

​ 适配器模式:将一个接口转换成客户希望的另一个接口,使接口不兼容的那个类可以一起工作,其别名为包装类。

​ 根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器类适配器两种

  • 在对象适配器模式中,适配器与适配者之间是关联关系;
  • 在类适配器模式中,适配器与适配者之间是继承(或实现)关系

​ 一般来说,对象适配器使用频率更高

image-20220315141136533

2 设计分析

​ 使用适配器模式重用算法库中的算法

2.1 栗子

image-20220315142428170

代码如下

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
98
99
100
101
102
package DesignPatterns.JavaDesign.Adapter;

import com.tencentcloudapi.cdn.v20180606.models.Sort;

import java.util.Collections;

public class Test {
public static void main(String[] args) {
ScoreOperation operation; // 针对抽象目标接口编程
operation = new OperationAdapter(); // 可使用配置文件
int scores[] = {84, 76, 58, 79, 91, 84, 66};
int result[];
int score;

System.out.println("成绩排序结果");
result = operation.sort(scores);

for (int i : scores) {
System.out.print(i + " ");
}
System.out.println();
System.out.println("查找成绩91:");
score = operation.search(result, 91);
if (score != -1) {
System.out.println("找到成绩91");
} else {
System.out.println("没有找到成绩91");
}
}
}

// 抽象成绩操作类:目标接口
interface ScoreOperation {
public int[] sort(int array[]); //成绩排序
public int search(int array[], int key); //成绩排序
}

// 快速排序类:适配器
class QuickSort {
public int[] quickSort(int array[]) {
sort(array, 0, array.length - 1);
return array;
}

public void sort(int array[], int l, int r) {
if (l >= r) return ;
int i = l - 1, j = r + 1;
int mid = (l + r) / 2;
int x = array[mid];
while (i < j) {
do i ++; while (array[i] < x);
do j --; while (array[j] > x);
if (i < j) swap(array, i, j);
}
sort(array, l, j);
sort(array, j + 1, r);
}

public void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}

// 二分查找类:适配者
class BinarySearch {
public int binarySearch(int array[], int key) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (array[mid] < key) low = mid + 1;
else if (array[mid] > key) high = mid - 1;
else return 1;
}
return -1;
}
}

// 操作适配器:适配器
class OperationAdapter implements ScoreOperation {

private QuickSort sortObj; // 定义适配器QuickSort对象
private BinarySearch searchObj; // 定义适配器BinarySearch对象

public OperationAdapter() {
sortObj = new QuickSort();
searchObj = new BinarySearch();
}

@Override
public int[] sort(int[] array) {
return sortObj.quickSort(array); // 调用适配器类QuickSort的排序方法
}

@Override
public int search(int[] array, int key) {
return searchObj.binarySearch(array, key);
}
}

2.2 类适配器

​ 类适配器模式和对象适配器模式最大的区别在于适配器和适配者之间的关系不同,对象适配器模式中适配器和适配者之间是关联关系,而类适配器模式中适配器和适配者是继承关系。

image-20220315145425366

一般适配器的代码大概长这样:

1
2
3
4
5
class Adapter extends Adaptee implemements Target {
public void request() {
specificRequest();
}
}

​ 由于Java、C#等语言不支持多重类继承,因此类适配器的使用受到很多限制,例如如果目标抽象类Target不是接口,而是一个类,就无法使用类适配器;此外,如果适配者Adapter为最终 (Final)类,也无法使用类适配器。

2.3 双向适配器

​ 在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器

image-20220315145936709

​ 在实际开发中,很少使用双向适配器

2.4 缺省适配器

​ 缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。

image-20220315151221409

3 总结

优缺点分析

优点

  1. 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构
  2. 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用
  3. 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

缺点(对象适配器)

  1. 要在适配器中置换适配者类的某些方法比较麻烦

适用场景

  1. 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码
  2. 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

4 场景使用

4.1 Spring的Aop对适配器模式的使用

​ AOP的大致流程是这样的:使用代理模式(1、JDK动态代理。2、CGLib字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现的面向切面编程。

​ Advice(通知)的类型有:BeforeAdviceAfterReturningAdviceThreowSadvice的。

​ 在每个类型Advice(通知)都有对应的拦截器,MethodBeforeAdviceInterceptorAfterReturningAdviceInterceptorThrowsAdviceInterceptor

​ Spring需要将每个Advice(通知)都封装成对应的拦截器类型,返回给容器,所以需要使用适配器模式对Advice进行转换。下面我们看看具体的代码。

代码大致如下:

MethodBeforeAdvice类:Adaptee

1
2
3
4
5
public interface MethodBeforeAdvice extends BeforeAdvice {

void before(Method method, Object[] args, Object target) throws Throwable;

}

Adapter类接口: Target

1
2
3
4
5
6
7
public interface AdvisorAdapter {

boolean supportsAdvice(Advice advice);

MethodInterceptor getInterceptor(Advisor advisor);

}

MethodBeforeAdviceAdapter类:Adapter

1
2
3
4
5
6
7
8
9
10
11
12
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}

public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}

}

DefaultAdvisorAdapterRegistry类:Client

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
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);


/**
* Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
*/
public DefaultAdvisorAdapterRegistry() {//这里注册了适配器
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}


public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {//这里调用了适配器的方法
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {//这里调用了适配器的方法
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}

public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}

}

结构式:适配器模式
https://2w1nd.github.io/2022/03/15/设计模式/结构式:适配器模式/
作者
w1nd
发布于
2022年3月15日
许可协议