适配器模式
一、适配器模式
1.适配器模式结构图
2.适配器模式示例代码
1 (1)先看看Target接口定义的示例代码如下 2 /** 3 * 定义客户端使用的接口,与特定领域相关 4 */ 5 public interface Target { 6 /** 7 * 示意方法,客户端请求处理方法 8 */ 9 public void request(); 10 } 11 (2)再看看需要被适配的对象定义。示例代码如下: 12 /** 13 * 已经存在的接口,这个接口需要配适配 14 */ 15 public class Adaptee{ 16 /** 17 * 示意方法,原本已经存在,已经实现的方法 18 */ 19 public void specificRequest() { 20 //具体的功能处理 21 } 22 } 23 24 (3)下面是适配器对象的基本实现。示例代码如下: 25 /** 26 * 适配器 27 */ 28 public class Adapter implements Target { 29 /** 30 * 持有需要被适配的接口对象 31 */ 32 private Adaptee adaptee; 33 34 /** 35 * 构造方法,传入需要被适配的对象 36 * @param adaptee 37 */ 38 public Adapter(Adaptee adaptee) { 39 this.adaptee = adaptee; 40 } 41 42 @Override 43 public void request() { 44 //可能转调已经实现了的方法,进行适配 45 adaptee.specificRequest(); 46 } 47 48 } 49 50 (4)再来看看使用适配器客户端的示例代码如下: 51 /** 52 * 使用适配器的客户端 53 */ 54 public class Client { 55 public static void main(String[] args) { 56 //创建需要被适配的对象 57 Adaptee adaptee = new Adaptee(); 58 //创建客户端需要调用的接口对象 59 Target target = new Adapter(adaptee); 60 //请求处理 61 target.request(); 62 } 63 }
二、具体实现代码---日志管理
需求一:用户要求日志以文件的形式记录。
1 /** 2 * 日志数据对象 3 * @author abc 4 * 5 */ 6 public class LogModel implements Serializable { 7 private static final long serialVersionUID = 1L; 8 9 /** 10 * 日志编号 11 */ 12 private String logId; 13 /** 14 * 操作人员 15 */ 16 private String operateUser; 17 /** 18 * 操作时间,以yyyy-MM-dd HH:mm:ss的格式记录 19 */ 20 private String operateTime; 21 /** 22 * 日志内容 23 */ 24 private String logContent; 25 public String getLogId() { 26 return logId; 27 } 28 public void setLogId(String logId) { 29 this.logId = logId; 30 } 31 public String getOperateUser() { 32 return operateUser; 33 } 34 public void setOperateUser(String operateUser) { 35 this.operateUser = operateUser; 36 } 37 public String getOperateTime() { 38 return operateTime; 39 } 40 public void setOperateTime(String operateTime) { 41 this.operateTime = operateTime; 42 } 43 public String getLogContent() { 44 return logContent; 45 } 46 public void setLogContent(String logContent) { 47 this.logContent = logContent; 48 } 49 @Override 50 public String toString() { 51 return "logId=" + logId + ", operateUser=" + operateUser + ", operateTime=" + operateTime 52 + ", logContent=" + logContent; 53 } 54 55 56 } 57 58 /** 59 * 日志文件操作接口 60 * @author abc 61 * 62 */ 63 public interface LogFileOperate { 64 65 /** 66 * 读取日志文件,从文件里面获取存储的日志列表对象 67 * @return 存储的日志列表对象 68 */ 69 public List<LogModel> readLogFile(); 70 71 /** 72 * 写日子文件,把日志列表写出到日志文件中 73 * @param list 要写到日志文件的日志列表 74 */ 75 public void writeLogFile(List<LogModel> list); 76 77 } 78 79 /** 80 * 日志文件操作接口实现 81 * @author abc 82 * 83 */ 84 public class LogFileOperateImpl implements LogFileOperate { 85 86 /** 87 * 日志文件的路径和文件名称,默认是当前项目根路劲下的AdapterLog.log 88 */ 89 private String logFilePathName = "AdapterLog.log"; 90 91 /** 92 * 构造方法,传入文件的路径和名称 93 * @param logFilePathName 文件路径和名称 94 */ 95 public LogFileOperateImpl(String logFilePathName) { 96 if (logFilePathName != null && logFilePathName.trim().length() > 0) { 97 //判断是否传入了文件的路径和名称,传入就重置路径和名称 98 this.logFilePathName = logFilePathName; 99 } 100 } 101 102 @Override 103 public List<LogModel> readLogFile() { 104 List<LogModel> list = null; 105 ObjectInputStream oin = null; 106 try { 107 File f = new File(logFilePathName); 108 if (f.exists()) { 109 oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(f))); 110 list = (List<LogModel>)oin.readObject(); 111 } 112 } catch (Exception e) { 113 // TODO Auto-generated catch block 114 e.printStackTrace(); 115 } finally { 116 try{ 117 if (oin != null) { 118 oin.close(); 119 oin = null; 120 } 121 } catch (IOException e) { 122 e.printStackTrace(); 123 } 124 } 125 return list; 126 } 127 128 @Override 129 public void writeLogFile(List<LogModel> list) { 130 File f = new File(logFilePathName); 131 ObjectOutputStream oos = null; 132 try { 133 oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(f))); 134 oos.writeObject(list); 135 } catch (IOException e) { 136 // TODO Auto-generated catch block 137 e.printStackTrace(); 138 } finally { 139 try { 140 if (oos != null) { 141 oos.close(); 142 oos = null; 143 } 144 } catch (IOException e) { 145 // TODO Auto-generated catch block 146 e.printStackTrace(); 147 } 148 } 149 150 } 151 152 } 153 154 /** 155 * 客户端测试 156 * @author abc 157 * 158 */ 159 public class Client { 160 public static void main(String[] args) { 161 //准备日志内容,测试的数据 162 LogModel model = new LogModel(); 163 model.setLogId("002"); 164 model.setOperateUser("admin1"); 165 model.setOperateTime("2018-01-02 16:16:00"); 166 model.setLogContent("这是一个测试"); 167 168 List<LogModel> list = new ArrayList<LogModel>(); 169 list.add(model); 170 //创建操作日志文件的对象 171 LogFileOperate logFileOperate = new LogFileOperateImpl(""); 172 //保存日志文件 173 logFileOperate.writeLogFile(list); 174 175 //读取日子文件的内容 176 List<LogModel> readLog = logFileOperate.readLogFile(); 177 System.out.println("readLog=" + readLog); 178 179 } 180 }
需求二:用户要求日志以数据库的形式管理日志。
/** * 定义操作的应用接口 * @author abc * */ public interface LogDbOperate { /** * 新增日志 * @param model 需要新增的日志对象 */ public void createLog(LogModel model); /** * 修改日志 * @param model 需要修改的日志对象 */ public void updateLog(LogModel model); /** * 删除日志 * @param model 需要删除的日志对象 */ public void removeLog(LogModel model); /** * 获取所有的日志 * @return 所有日志对象集合 */ public List<LogModel> getAllLog(); } /** * 定义操作的应用接口实现类 * @author abc */ public interface LogDbOperateImpl { //具体实现内容 } /** * 客户端测试 * @author abc * */ public class Client { public static void main(String[] args) { //准备日志内容,测试的数据 LogModel model = new LogModel(); model.setLogId("002"); model.setOperateUser("admin1"); model.setOperateTime("2018-01-02 16:16:00"); model.setLogContent("这是一个测试"); List<LogModel> list = new ArrayList<LogModel>(); list.add(model); //创建操作日志文件的对象 LogFileOperate logFileOperate = new LogFileOperateImpl(""); LogDbOperate logDbOperate = new Adapter(logFileOperate); //保存日志文件 logDbOperate.createLog(model); //读取日志文件的内容 List<LogModel> allLog = logDbOperate.getAllLog(); System.out.println("allLog=" + allLog); } }
思路总结:
1.原有存取日志的方式
2.现在有了新的基于数据库的实现,新的实现由自己的接口
3.现在想要在第二版的实现里面,能够同时兼容第一版的功能,那么就应有一个类来实现第二版的接口,然后在这个类里面去调用已有的功能实现,这个类就是适配器
整体结构
三、认识适配器模式
1.模式的功能
适配器模式的主要功能是进行转换匹配,目的是复用已有的功能,而不是来实现新的接口。也就是说,客户端需要的功能应该是已经实现好了的,不需要适配器模式来实现,适配器模式主要负责把不兼容的接口转换成客户端期望的样子就可以了。
但这并不是说,在适配器里面就不能实现功能。适配器里面可以实现功能,称这种适配器为智能适配器。再说了,在接口匹配和转换的过程中,也有可能需要额外实现一定的功能,才能转换过来,比如需要调整参数以进行匹配等。
2.Adaptee 和 Target 的关系
适配器模式中被适配的接口Adaptee和适配成为的接口Target是没有关联的,也就是说,Adaptee和Target中的方法既可以相同,也可以不同。极端情况下两个接口里面方法可能完全不同的,当然这种情况下也可以完全相同。
这里所说的相同和不同,是指方法定义的名称、参数列表、返回值,以及方法本省的功能都可以相同或不同。
3.对象组合
根据前面的实现,你会发现,适配器的实现方式其实是依靠对象组合的方式。通过给适配器对象组合被适配的对象,然后当客户端调用Targer的时候,适配器会吧相应的功能委托给适配的对象去完成。
4.适配器模式的调用顺序示意图