一、适配器模式

1.1、基本介绍

  • 适配器模式(Adapter Pattern)将某个类的接口转换为客户端期望的另一个接口表示,主要目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作,其别名为 包装器 (Wrapper)
  • 适配器模式属于 结构型模式,主要分为三类,即 类适配器模式对象适配器模式接口适配器模式

假如 A 类中有一方法 m1 ,B 类中有一方法 m2 ,由于种种原因,m1 方法需要调用 m2 方法,但 m1 不能直接调用 m2 ,那么此时就可以使用适配器模式来解决这个问题。、

编写一个 Adapter 类,令 A 类依赖这个 Adapter 类, Adapter 类依赖 B 类,让 Adapter 类作为一个中介, A 类对象调用 Adapter 类中的方法,然后由 Adapter 类调用 B 类中的方法。

在调用过程中,可能需要在 Adapter 类的方法中对原本不适配的方法逻辑、参数、结果集进行适配。

1.2、工作原理

  • 适配器模式,就是将一个类的接口转换为另一个接口,使原本接口不兼容的类可以兼容
  • 从用户的角度看不到被适配者,是解耦的
  • 用户调用适配器转换出来的目标接口方法,适配器再调用被适配者的相关接口。
  • 用户收到反馈结果,感觉只是和目标接口交互

image-20210829141723263

1.3、类适配器模式

1、介绍

Adapter 通过继承被适配者类,然后实现目标接口,从而完成从 被适配者目标 的适配

2、应用实例

使用充电器模拟电压转换,将 220V 电压转换为 5V 的电压。

  • 编写一个类,模拟 220 V 的电压
1
2
3
4
5
6
7
8
9
10
11
public class Voltage220V {
/**
* 这个方法输出 220 V 的电压
* @return
*/
public int output220V () {
int source = 220;
System.out.println("输出电压为:" + source + "伏");
return source;
}
}
  • 编写一个接口
1
2
3
public interface IVoltage5V {
int output5V();
}
  • 编写一个适配器类 VoltageAdapter ,这个类继承 Voltage220V ,实现 IVoltage5V

这个类需要做这几件事

  1. 获取被适配者的电压
  2. 将获取到的被适配者电压进行转换
  3. 将转换后的目标电压进行返回,
1
2
3
4
5
6
7
8
9
10
11
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
//1 获取到 220 V 的电压
int source = output220V();
//2 将获取到的被适配者的电压进行降压
int target = source / 44;
//3 输出转换后得到的目标电压
return target;
}
}
  • 编写一个手机类,模拟充电
1
2
3
4
5
6
7
8
9
10
public class Phone {
public void charging(IVoltage5V voltage) {
if (voltage.output5V() == 5) {
// 如果获取到的电压为 5 v,那么进行充电
System.out.println("获取到的电压为 5V ,可以进行充电...");
} else {
System.out.println("没法充电...");
}
}
}
  • 进行测试
1
2
3
4
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}

image-20210829145505585

3、类适配器注意事项和细节

  • Java 是单继承机制,所以类适配器需要继承被适配者类,这算是一个缺点,同时要求目标必须是一个接口,有一定局限性
  • 被适配者类的方法在 Adapter 类中会暴露出来

image-20210829145704225

  • 由于适配器继承了被适配者类,所以它可以根据需要重写被适配者类中的方法,使得 Adapter 类的灵活性增强。

1.4、对象适配器模式

1、介绍

基本思路与类适配器相同,只是将 Adapter 类做修改,不再继承被适配者,而是持有被适配者类的实例,以解决兼容性的问题。

即持有被适配者实例,实现目标接口

  • 根据合成复用原则,在系统中尽量使用关联关系来替换继承关系
  • 对象适配器模式是适配器模式中常用的一种

2、应用实例

用对象适配器改进前面的类适配器,修改 Adapter 类的代码即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ObjectVoltageAdapter implements IVoltage5V {
private final Voltage220V voltage220V;

public ObjectVoltageAdapter (Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}

@Override
public int output5V() {
int targetVoltage = 0;
if (null != voltage220V) {
int sourceVoltage = voltage220V.output220V();
targetVoltage = sourceVoltage / 44;
System.out.println("适配完成!");
}
return targetVoltage;
}
}

3、总结

  • 对象适配器和类适配器其实是同一种思想,只是实现方式有所不同

对象适配器使用组合替代继承,它解决了类适配器必须继承被适配者类的问题,同时也不再要求目标必须是接口

  • 相较类适配器更加灵活、成本更低。

1.5、接口适配器模式

1、介绍

接口适配器模式又称为缺省适配器模式

  • 当不需要全部实现接口提供的方法时,可以设计一个抽象类实现接口,并为该接口中的每个方法提供一个默认实现(空方法)

该抽象类的子类可有选择地覆盖父类的某些方法来实现需求

  • 适用于一个接口不想使用其所有的方法的情况

2、实例

  • 创建一个接口,这个接口有4个抽象方法
1
2
3
4
5
6
public interface AdapterInterface {
void method1();
void method2();
void method3();
void method4();
}
  • 创建一个抽象类,这个抽象类实现 AdapterInterface 接口,并为接口中的抽象方法提供默认实现

这样,在实现子类调用它没有覆盖的方法时,会抛出一个 UnsupportedOperationException 异常,表示这个实现子类不支持它没有覆盖的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class AbsAdapterInterfaceImpl implements AdapterInterface {
@Override
public void method1() {
throw new UnsupportedOperationException();
}
@Override
public void method2() {
throw new UnsupportedOperationException();
}
@Override
public void method3() {
throw new UnsupportedOperationException();
}
@Override
public void method4() {
throw new UnsupportedOperationException();
}
}
  • 创建一个实现子类,这个子类继承自 AbsAdapterInterfaceImpl 类,我们覆盖其中的某一个方法

可以使用 匿名内部类 来代替实现子类

1
2
3
4
5
6
public class AdapterInterfaceImplMethod1 extends AbsAdapterInterfaceImpl {
@Override
public void method1() {
System.out.println("AdapterInterfaceImplMethod1 覆盖了父类的 method1 方法...");
}
}
  • 测试
  1. 调用覆盖的方法时

  2. 调用未曾覆盖的方法时

1
2
3
4
5
public static void main(String[] args) {
AdapterInterfaceImplMethod1 method1 = new AdapterInterfaceImplMethod1();
method1.method1();
method1.method2();
}

image-20210829155929652

1.6、适配器模式在 Spring MVC 框架应用中的源码分析

  • 在 Spring MVC 中的 HandlerAdapter 中,就使用了适配器模式

对于不同的请求,需要调用不同的控制器(Handler)去处理

1、源码追踪

  • 查看 Spring MVC 中的 DispatcherServlet 源码

查看此类中的 doDispatch 方法,这个方法用于处理请求

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
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 使用一个变量接收请求
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
// 根据请求对象获取一个处理器对象
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
// 获取一个处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
  • 查看 getHandlerAdapter 方法,可以看到这个方法返回一个 HandlerAdapter 对象
1
2
3
4
5
6
7
8
9
10
11
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
  • HandlerAdapter 是一个接口,我们查看它的实现类

HandlerAdapter 的实现子类使得每一种 Controller 有一种对应的适配器实现类,令每一种 Controller 都有不同的实现方式

1
2
3
public interface HandlerAdapter {
...
}

image-20210829162616157

  • 回到 doDispatch 方法,在拿到适配器对象后,会调用适配器对象的 handle 方法真正调用控制器中的方法,并返回一个 ModelAndView 对象
1
2
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

二、责任链模式

2.1、问题引入 - 采购审批需求

学校的 OA 系统有一个采购审批项目,需求如下

  • 采购员采购教学器材

如果金额小于等于 5000 ,那么由教学主任审批

如果金额大于 5000 小于 10000 ,那么由院长审批

如果金额大于 10000 小于 30000 ,那么由副校长进行审批

如果金额大于 30000 ,那么由校长审批

  • 传统解决方案

接收到一个采购请求后,根据采购金额来调用对应的审批人完成审批

  • 问题分析

客户端在这里会使用到分支判断(if - else 或 switch)来对不同的采购请求进行处理,会存在以下问题

  1. 如果各个级别的人员审批金额发生变化,那么在客户端的代码也需要进行变化
  2. 客户端需要明确知道有多少个审批级别

这样一来,处理申请请求就与审批人之间存在强耦合关系,不利于代码的扩展和维护。

2.2、基本介绍

  • 职责链模式(Chain of Responsibility)又叫责任链模式,它为请求创建了一个接收者对象的链,这种模式对请求的发送者和接收者进行解耦
  • 职责链模式中,通常每个接收者都包含另一个接收者的引用。如果一个对象不能处理该请求,那么就将相同的请求传给下一个接收者,以此类推,直到有一个接收者可以处理这个请求为止

MyBatis 的二级缓存中就使用了职责链模式

1、类图

image-20210829180614565

2、角色介绍

  • Handler

抽象的请求处理者,它定义了一个处理请求的方法,同时包含指向下一个处理者的 Handler 属性

  • ConcreteHandler A / B

请求的具体处理者,注意,它只负责自己职责范围内的请求,对于无法处理的请求,它会将请求传递给它的后继者(即下一个处理器),从而形成一个职责链

  • Request

含有许多属性,表示一个请求

2.3、应用实例

使用职责链模式解决上面的采购审批需求

1、实例类图

image-20210829182109877

2、编写一个采购请求类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PurchaseRequest {
private int type;
private int price;
private String id;

public PurchaseRequest(int type, int price, String id) {
this.type = type;
this.price = price;
this.id = id;
}
public PurchaseRequest() {}
...
}

2、编写一个抽象审批人类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class Approver {
private Approver next;
private String name;

public Approver(String name) {
this.name = name;
}

public void setNext(Approver next) {
this.next = next;
}

/***
* 处理请求的抽象方法,这个方法由具体的子类实现
* @param request
*/
public abstract void processRequest(PurchaseRequest request);
}

3、编写具体的审批人类

这个类需要继承上面的抽象审批人类

  • 教学主任类,这个类处理金额小于等于 5000 的采购请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TeacherDirector extends Approver {
public TeacherDirector(String name) {
super(name);
}

@Override
public void processRequest(PurchaseRequest request) {
if (request.getPrice() <= 5000) {
System.out.println("请求编号 id = " + request.getId() + "被" + this.getName() + "处理...");
} else {
// 将请求传递给下一个审批人请求
System.out.println("由于请求金额为 :" + request.getPrice() + ",所以教学主任处理不了这个请求...");
this.getNext().processRequest(request);
}
}
}

对于其他具体的审批人类,代码与教学主任大同小异,只是金额略有不同,这里不再给出。

4、测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {
//1 创建一个请求
PurchaseRequest request = new PurchaseRequest();
request.setId(UUID.randomUUID().toString().replaceAll("-",""));
request.setPrice(10000);

//2 创建相关的审批人
TeacherDirector teacherDirector = new TeacherDirector("李主任");
Dean dean = new Dean("王院长");
ViceChancellor viceChancellor = new ViceChancellor("乔副校长");
HeadMaster headMaster = new HeadMaster("陈校长");
//3 需要设置各个审批人的后继审批者
teacherDirector.setNext(dean);
dean.setNext(viceChancellor);
viceChancellor.setNext(headMaster);
//4 需要将处理人构成一个环
headMaster.setNext(teacherDirector);

//5 遇到采购请求时,先让级别最低的教学主任处理
teacherDirector.processRequest(request);
}

image-20210829185732090