适配器模式2
用来解决上述问题的一个合理的解决方案就是适配器模式。那么什么是适配器模式呢?
(1)适配器模式定义
?
(2)应用适配器模式来解决的思路
?????? 仔细分析上面的问题,问题的根源在于接口的不兼容,功能是基本实现了的,也就是说,只要想办法让两边的接口匹配起来,就可以复用第一版的功能了。
按照适配器模式的实现方式,可以定义一个类来实现第二版的接口,然后在内部实现的时候,转调第一版已经实现了的功能,这样就可以通过对象组合的方式,既复用了第一版已有的功能,同时又在接口上满足了第二版调用的要求。
完成上述工作的这个类就是适配器。
适配器模式的结构如图4.6所示:
?
图4.6? 适配器模式结构图
Client:
客户端,调用自己需要的领域接口Target。
Target:
定义客户端需要的跟特定领域相关的接口。
Adaptee:
已经存在的接口,通常能满足客户端的功能要求,但是接口与客户端要求的特定领域接口不一致,需要被适配。
Adapter:
适配器,把Adaptee适配成为Client需要的Target。
(1)先看看Target接口的定义,示例代码如下:
/**
?* 定义客户端使用的接口,与特定领域相关
?*/
public interface Target {
??? /**
??? ?* 示意方法,客户端请求处理的方法
??? ?*/
??? public void request();
}
(2)再看看需要被适配的对象定义,示例代码如下:
/**
?* 已经存在的接口,这个接口需要被适配
?*/
public class Adaptee {
??? /**
??? ?* 示意方法,原本已经存在,已经实现的方法
??? ?*/
??? public void specificRequest() {
?????? //具体的功能处理
??? }
}
(3)再看看适配器对象的基本实现,示例代码如下:
/**
?* 适配器
?*/
public class Adapter implements Target {
??? /**
??? ?* 持有需要被适配的接口对象
??? ?*/
??? private Adaptee adaptee;
??? /**
??? ?* 构造方法,传入需要被适配的对象
??? ?* @param adaptee 需要被适配的对象
??? ?*/
??? public Adapter(Adaptee adaptee) {
?????? this.adaptee = adaptee;
??? }
?
??? public void request() {
?????? //可能转调已经实现了的方法,进行适配
?????? adaptee.specificRequest();
??? }
}
(4)再来看看使用适配器的客户端,示例代码如下:
/**
?* 使用适配器的客户端
?*/
public class Client {???
??? public static void main(String[] args) {
?????? //创建需被适配的对象
?????? Adaptee adaptee = new Adaptee();
?????? //创建客户端需要调用的接口对象
?????? Target target = new Adapter(adaptee);
?????? //请求处理
?????? target.request();
??? }
}
?????? 要使用适配器模式来实现示例,关键就是要实现这个适配器对象,它需要实现第二版的接口,但是在内部实现的时候,需要调用第一版已经实现的功能。也就是说,第二版的接口就相当于适配器模式中的Target接口,而第一版已有的实现就相当于适配器模式中的Adaptee对象。
(1)把这个适配器简单的实现出来,示意一下,示例代码如下:
/**
?* 适配器对象,把记录日志到文件的功能适配成第二版需要的增删改查的功能
?*/
public class Adapter implements LogDbOperateApi{
??? /**
??? ?* 持有需要被适配的接口对象
??? ?*/
??? private LogFileOperateApi adaptee;
??? /**
??? ?* 构造方法,传入需要被适配的对象
??? ?* @param adaptee 需要被适配的对象
??? ?*/
??? public Adapter(LogFileOperateApi adaptee) {
?????? this.adaptee = adaptee;
??? }
???
??? public void createLog(LogModel lm) {
?????? //1:先读取文件的内容
?????? List<LogModel> list = adaptee.readLogFile();
?????? //2:加入新的日志对象
?????? list.add(lm);
?????? //3:重新写入文件
?????? adaptee.writeLogFile(list);
??? }
??? public List<LogModel> getAllLog() {
?????? return adaptee.readLogFile();
??? }
??? public void removeLog(LogModel lm) {
?????? //1:先读取文件的内容
?????? List<LogModel> list = adaptee.readLogFile();
?????? //2:删除相应的日志对象
?????? list.remove(lm);
?????? //3:重新写入文件
?????? adaptee.writeLogFile(list);
??? }
??? public void updateLog(LogModel lm) {
?????? //1:先读取文件的内容
?????? List<LogModel> list = adaptee.readLogFile();
?????? //2:修改相应的日志对象
?????? for(int i=0;i<list.size();i++){
?????????? if(list.get(i).getLogId().equals(lm.getLogId())){
????????????? list.set(i, lm);
????????????? break;
?????????? }
?????? }
?????? //3:重新写入文件
?????? adaptee.writeLogFile(list);
??? }
}
(2)此时的客户端也需要一些改变,示例代码如下:
public class Client {
??? public static void main(String[] args) {
?????? //准备日志内容,也就是测试的数据
?????? LogModel lm1 = new LogModel();
?????? lm1.setLogId("001");
?????? lm1.setOperateUser("admin");
?????? lm1.setOperateTime("2010-03-0210:08:18");
?????? lm1.setLogContent("这是一个测试");
?????? List<LogModel> list = new ArrayList<LogModel>();
?????? list.add(lm1);
?????? //创建操作日志文件的对象
?????? LogFileOperateApi logFileApi = new LogFileOperate("");
??????
?????? //创建新版的操作日志的接口对象
???????
图4.7? 原有文件存取日志的方式
②现在有了新的基于数据库的实现,新的实现有自己的接口,如图4.8所示:
?
图4.8? 新的基于数据库的实现
③现在想要在第二版的实现里面,能够同时兼容第一版的功能,那么就应有一个类来实现第二版的接口,然后在这个类里面去调用已有的功能实现,这个类就是适配器,如下图4.9所示:
?
图4.9? 加入适配器的实现结构示意图
上面是分步的思路,现在来看一下前面示例的整体结构,如图4.10所示:
?
图4.10? 适配器实现的示例的结构示意图
如同上面的例子,原本新的日志操作接口不能和旧的文件实现一起工作,但是经过适配器适配后,新的日志操作接口就能和旧的文件实现日志存储一起工作了。