给小白举个例子:
大家都用过macbook pro(15款)笔记本, tmd没有网线端口!没有VGA 视频输出接口 !
tmd 用网线要买转换器!开会用投影仪要买视频输出转接口!
这里的转换器和转接口就是我今天要讲的主角:适配器。
这里 我举个项目中的案例,做说明:
我现在要做个日志系统,前期功能比较单一,这里我叫它第一版,就是把日志写入文件,这个ok?
1:先整个日志类
public class LogModel {
private String logId;
private String operateUser;
private String operateTime;
private String logContent;
get / set 省略...
public interface LogFileService {
List<LogModel> readLogFile();
void writeLogFile(List<LogModel> list);
}
public class LogFileServiceImpl implements LogFileService {
@Override
public List<LogModel> readLogFile() {
System.out.println("正在读文件日志");
return null;
}
@Override
public void writeLogFile(List<LogModel> list) {
System.out.println("正在写入文件日志");
}
}
LogModel logModel = new LogModel();
LogFileService service = new LogFileServiceImpl();
service.writeLogFile(null);
service.readLogFile();
System.out.println("第一版 将日志写入文件!测试完成!");
看上去很容易,是吧,别急,我们接着来!
日志写入文件系统用了一段时间,老板需求增加了,我们要做系统升级,现在我们要把日志记录到DB 数据库中,
6:于是我就把接口定义好了,日志写入DB 接口如下:(这里不与数据库交互, 意思下示例)
public interface LogDBService {
void createDBLog(LogModel logModel);
void updateDBLog(LogModel logModel);
void deleteDBLog(String logId);
void selectDBLog(LogModel logModel);
}
public class LogDBServiceImpl implements LogDBService {
@Override
public void createDBLog(LogModel logModel) {
System.out.println("DB日志插入成功");
}
@Override
public void updateDBLog(LogModel logModel) {
System.out.println("DB日志更新成功");
}
@Override
public void selectDBLog(LogModel logModel) {
System.out.println("DB日志查询成功");
}
}
很多同学心里一下子又答案了,当然了!当然都能支持了,但是实现方式不一定想的是相同的!
有的同学先到继承 ,对,设计个新接口,然后继承老的和新的接口功能,
但是,java继承只能继承一个不能同时继承,若果使用继承在继承实现,就不符合面向对象的原则了,!这里不多说。
上图抛个问题给大家:
7:这里就用到了适配器模式,就是我们找个适配器,就能用新接口兼容老的接口了!
这里我代码示例,把旧版接口功能适配成新版接口功能:我们先设计个适配器如下:
public class Adapter implements LogDBService {
//被适配的对象
private LogFileService adaptee;
//构造方法,传入要被适配的对象
public Adapter(LogFileService adaptee) {
this.adaptee = adaptee;
}
@Override
public void createDBLog(LogModel logModel) {
adaptee.writeLogFile(null);
}
@Override
public void updateDBLog(LogModel logModel) {
//1;先读取
List<LogModel> list = adaptee.readLogFile();
for (int i = 0; i < list.size(); i++) {
//2:修改内容
}
//3:重新写入
adaptee.writeLogFile(list);
}
@Override
public void selectDBLog(LogModel logModel) {
adaptee.readLogFile();
}
}
8:我们客户端调用也要稍微调整下:
LogModel logModel = new LogModel();
LogFileService fileService = new LogFileServiceImpl();
LogDBService dbService = new Adapter(fileService);
dbService.createDBLog(null);
dbService.selectDBLog(logModel);
logFileClient();
System.out.println("文件适配成DB ,测试完成!");
我们使用的是第二版的接口,但实现的却是是第一版的功能,因为中间我们用到了一个适配器Adaper,
就像我们网线转换器一样,用的是USB接口,我们用它确实实现网络连接!
9: 到此我们新的客户端通过新的接口(第二版接口) ,能使用新功能,也能使用老功能了,如图:
10:老板在这时候,有提出了个需求,老的项目也要使用新版日志系统和新版日志功能,
这里有个问题了,我们老的项目,旧的客户端怎么搞?旧的只有旧接口,没有新版接口啊(读到这里,想到复制代码自己实现的同学,可以先不用学设计模式了)
这里给大家介绍个双向适配器!(上面演示的是个单向适配器),双向适配器的作用就是双向兼容!
上图示例说明:
11:双向适配器代码示例:
public class TwoDirectAdapter implements LogDBService,LogFileService{
private LogFileService logFileAdaptee;
private LogDBService logDBAdaptee;
public TwoDirectAdapter(LogFileService logFileAdaptee, LogDBService logDBAdaptee) {
this.logFileAdaptee = logFileAdaptee;
this.logDBAdaptee = logDBAdaptee;
}
@Override
public void createDBLog(LogModel logModel) {
logFileAdaptee.writeLogFile(null);
}
@Override
public void updateDBLog(LogModel logModel) {
//1;先读取
List<LogModel> list = logFileAdaptee.readLogFile();
for (int i = 0; i < list.size(); i++) {
//2:修改内容
}
//3:重新写入
logFileAdaptee.writeLogFile(null);
}
@Override
public void selectDBLog(LogModel logModel) {
logFileAdaptee.readLogFile();
}
@Override
public List<LogModel> readLogFile() {
logDBAdaptee.selectDBLog(null);
return null;
}
@Override
public void writeLogFile(List<LogModel> list) {
logDBAdaptee.createDBLog(null);
}
}
//双向兼容,双向适配器
System.out.println("===========双向适配器测试开始============");
//创建操作日志文件对象
LogFileService fileSer = new LogFileServiceImpl();
LogDBService dbSer = new LogDBServiceImpl();
//创建经过双向适配器后的操作日志的接口对象
LogFileService logFileSer = new TwoDirectAdapter(fileSer,dbSer);
LogDBService dbFileSer = new TwoDirectAdapter(fileSer,dbSer);
//调用第一版,实际是第二版实现
logFileSer.readLogFile();
logFileSer.writeLogFile(null);
//调用第二版,实际是第一版实现
dbFileSer.createDBLog(null);
dbFileSer.selectDBLog(null);
System.out.println("===========双向适配器测试完成============");
13:测试结果如下:利用双向适配器,旧系统也能使用新版日志系统包括其所有功能了。
15:什么场景下使用适配器?
- 如果你想要使用已经存在的类,但是它的接口不符合你的需求。
- 如果你想创建一个可复用的类,但是这个类可能和一些不兼容的类一起工作。
- 如果你想使用一些已存的子类,但是不可能对每个子类都进行适配。