1. 什么是适配器模式?
适配器模式 (Apdater Pattern):
将一个系统的接口转换成另外一种形式,从而使原来不能直接调用的接口变得可以调用。
2. 为什么使用适配器模式?
在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。
例如,讲中文的人同讲英文的人对话时需要一个翻译,用直流电的笔记本电脑接交流电源时需要一个电源适配器,用计算机访问照相机的 SD 内存卡时需要一个读卡器等。
在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题。
3. 应用场景
- 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
- 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
- 新老版本接口的兼容
- Mybatis 多种日志框架的整合
4. 原理
如下图结构所示:
适配器模式(Adapter)包含以下主要角色。
目标(Target)接口:
当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:
它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:
它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
5. 适配器模式-案例 1:实现日志收集
需求:设计一个日志收集系统,可能会考虑文件写入、也可能考虑写入 MQ、也可能考虑写入数据库等。
1. 目标(Target)接口
java
package com.log.service;
/**
* description: 日志写入数据库-目标接口: 当前系统业务所期待的接口,它可以是抽象类或接口。
* date: 2020/9/21 13:35
* author: Calvin
* version: 1.0
*/
public interface LogWriteDbService {
/**
* 将本地文件写入到数据库中 新的目标
*/
void logWriteDb();
}
2. 适配者(Adaptee)类
java
package com.log.service;
import com.log.entity.Log;
import java.util.List;
/**
* description: 日志写入本地文件-适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
* date: 2020/9/21 13:35
* author: Calvin
* version: 1.0
*/
public interface LogWriteFileService {
/**
* 将日志写入本地文件
*/
void logWriteFile();
/**
* 从本地文件中读取日志
*
* @return
*/
List<Log> readLogFile();
}
java
package com.log.service.impl;
import com.log.entity.Log;
import com.log.service.LogWriteFileService;
import java.util.ArrayList;
import java.util.List;
/**
* description: 日志写入数据库-目标接口: 当前系统业务所期待的接口,它可以是抽象类或接口。
* date: 2020/9/21 13:35
* author: Calvin
* version: 1.0
*/
public class LogWriteFileServiceImpl implements LogWriteFileService {
@Override
public void logWriteFile() {
System.out.println(">>>将日志写入文件中...");
}
@Override
public List<Log> readLogFile() {
Log log1 = new Log();
log1.setLogId("0001");
log1.setLogContent("Tomcat启动成功..");
Log log2 = new Log();
log2.setLogId("0002");
log2.setLogContent("Jetty启动成功..");
List<Log> logs = new ArrayList<Log>();
logs.add(log1);
logs.add(log2);
return logs;
}
}
3. 适配器(Adapter)类
java
package com.log.apdater;
import com.log.entity.Log;
import com.log.service.LogWriteDbService;
import com.log.service.impl.LogWriteFileServiceImpl;
import java.util.List;
/**
* description: 类适配器模式-适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
* date: 2020/9/21 13:35
* author: Calvin
* version: 1.0
*/
public class LogAdapter extends LogWriteFileServiceImpl implements LogWriteDbService {
@Override
public void logWriteDb() {
// 1.从文件中读取日志文件
List<Log> logs = super.readLogFile();
// 2.写入到数据库中
System.out.println(">>>将数据写入到数据库中.." + logs);
// 3.写入到本地文件中
super.logWriteFile();
}
}
4. 编写测试
java
package com.log.contoller;
import com.log.apdater.LogAdapter;
import com.log.service.LogWriteFileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* description: LogController
* date: 2020/9/21 15:51
* author: Calvin
* version: 1.0
*/
@RestController
@RequestMapping(value = "log")
public class LogController {
@Autowired
private LogWriteFileService logWriteFileService;
@GetMapping("old")
public void write() {
logWriteFileService.logWriteFile();
}
@GetMapping("new")
public void write1() {
LogAdapter logAdapter = new LogAdapter();
logAdapter.logWriteDb();
}
}
6. 结果演示

7. 优缺点
优点:
系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。
比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。