设计模式的应用场景(7)--适配器模式
2017-08-29 00:21
429 查看
适配器模式
定义:将一个系统的接口转换成另外一种形式,从而使原来不能直接调用的接口变得可以调用。优点
适配器模式也是一种包装模式,它与装饰模式同样具有包装的功能,此外,对象适配器模式还具有委托的意思。总的来说,适配器模式属于补偿模式,专用来在系统后期扩展、修改时使用。
缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
适配器模式应用场景
在软件开发中,也就是系统的数据和行为都正确,但接口不相符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。比如在需要对早期代码复用一些功能等应用上很有实际价值。适用场景大致包含三类:
1、已经存在的类的接口不符合我们的需求;
2、创建一个可以复用的类,使得该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作;
3、在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类。
下面举例说明适配器模式的使用:
接着上面的例子,内网的负责人写的代码是接收Map类型的
import java.util.HashMap; import java.util.Map; public class Application { public static void execute(HashMap map) { for (int i = 0; i < map.size(); i++) { System.out.println(map.get(i+"")); } } }
小巩基于外观模式写的接口是提供list
import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; public class Facade { public List getEmpByOrgan(String orgId) { List list = new ArrayList(); list.add("张三"); list.add("李四"); list.add("王五"); return list; } 4000 }
那么问题来了,两边接口不一样,需要适配器
import java.util.Map; import java.util.List; import java.util.HashMap; public class ListAdapter extends HashMap{ private List list; ListAdapter(List list) { this.list = list; } public int size() { return list.size(); } public Object get(Object i) { return list.get((Integer.valueOf(i.toString())).intValue()); } }
最后,看看客户端调用代码
public class Client { public static void main(String[] argv) { Facade facade = new Facade(); ListAdapter listAdapter = new ListAdapter(facade.getEmpByOrgan("1")); Application.execute(listAdapter); } }
上面演示的是对象适配器的用法,可以看到被适配的对象是被引用的。另外一种方式是类适配器,被适配的类是以继承接口方式使用的。
下面的例子展示两种适配器的使用
考虑一个记录日志的应用,用户可能会提出要求采用文件的方式存储日志,也可能会提出存储日志到数据库的需求,这样我们可以采用适配器模式对旧的日志类进行改造,提供新的支持方式。
首先我们需要一个简单的日志对象类
public class LogBean { private String logId;//日志编号 private String opeUserId;//操作人员 public String getLogId(){ return logId; } public void setLogId(String logId){ this.logId = logId; } public String getOpeUserId(){ return opeUserId; } public void setOpeUserId(String opeUserId){ this.opeUserId = opeUserId; } public String toString(){ return "logId="+logId+",opeUserId="+opeUserId; } }
接下来定义一个操作日志文件的接口
import java.util.List; /* * 读取日志文件,从文件里面获取存储的日志列表对象 * @return 存储的日志列表对象 */ public interface LogFileOperateApi { public List<LogBean> readLogFile(); /** * 写日志文件,把日志列表写出到日志文件中去 * @param list 要写到日志文件的日志列表 */ public void writeLogFile(List<LogBean> list); }
然后实现日志文件的存储和获取
import java.io.File; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.List; /* * 实现对日志文件的操作 */ public class LogFileOperate implements LogFileOperateApi{ /* * 设置日志文件的路径和文件名称 */ private String logFileName = "file.log"; /* * 构造方法,传入文件的路径和名称 */ public LogFileOperate(String logFilename){ if(logFilename!=null){ this.logFileName = logFilename; } } @Override public List<LogBean> readLogFile() { // TODO Auto-generated method stub List<LogBean> list = null; ObjectInputStream oin =null; //业务代码 return list; } @Override public void writeLogFile(List<LogBean> list) { // TODO Auto-generated method stub File file = new File(logFileName); ObjectOutputStream oout = null; //业务代码 } }
如果这时候需要引入数据库方式,引入适配器之前,我们需要定义日志管理的操作接口
public interface LogDbOpeApi { /* * 新增日志 * @param 需要新增的日志对象 */ public void createLog(LogBean logbean); } 接下来就要实现适配器了,LogDbOpeApi 接口就相当于 Target 接口,LogFileOperate 就相当于 Adaptee 类 import java.util.List; /* * 适配器对象,将记录日志到文件的功能适配成数据库功能 */ public class LogAdapter implements LogDbOpeApi{ private LogFileOperateApi adaptee; public LogAdapter(LogFileOperateApi adaptee){ this.adaptee = adaptee; } @Override public void createLog(LogBean logbean) { // TODO Auto-generated method stub List<LogBean> list = adaptee.readLogFile(); list.add(logbean); adaptee.writeLogFile(list); } }
最后是客户端代码的实现
import java.util.ArrayList; import java.util.List; public class LogClient { public static void main(String[] args){ LogBean logbean = new LogBean(); logbean.setLogId("1"); logbean.setOpeUserId("michael"); List<LogBean> list = new ArrayList<LogBean>(); LogFileOperateApi logFileApi = new LogFileOperate(""); //创建操作日志的接口对象 LogDbOpeApi api = new LogAdapter(logFileApi); api.createLog(logbean); } }
下面演示累类适配器
import java.util.List; /* * 类适配器对象案例 */ public class ClassAdapter extends LogFileOperate implements LogDbOpeApi{ public ClassAdapter(String logFilename) { super(logFilename); // TODO Auto-generated constructor stub } @Override public void createLog(LogBean logbean) { // TODO Auto-generated method stub List<LogBean> list = this.readLogFile(); list.add(logbean); this.writeLogFile(list); } }
在实现中,主要是适配器的实现与以前不一样,与对象适配器实现同样的功能相比,类适配器在实现上有所改变:
需要继承 LogFileOperate 的实现,然后再实现 LogDbOpeApi 接口;
需要按照继承 LogFileOperate 的要求,提供传入文件路径和名称的构造方法;
不再需要持有 LogFileOperate 的对象,因为适配器本身就是 LogFileOperate 对象的子类;
以前调用被适配对象的方法的地方,全部修改成调用自己的方法。
类适配器和对象适配器的选择
从实现上:类适配器使用对象继承的方式,属于静态的定义方式。对象适配器使用对象组合的方式,属于动态组合的方式;
从工作模式上:类适配器直接继承了 Adaptee,使得适配器不能和 Adaptee 的子类一起工作。对象适配器允许一个 Adapter 和多个 Adaptee,包括 Adaptee 和它所有的子类一起工作;
从定义角度:类适配器可以重定义 Adaptee 的部分行为,相当于子类覆盖父类的部分实现方法。对象适配器要重定义 Adaptee 很困难;
从开发角度:类适配器仅仅引入了一个对象,并不需要额外的引用来间接得到 Adaptee。对象适配器需要额外的引用来间接得到 Adaptee。
总的来说,建议使用对象适配器方式。
相关文章推荐
- Java设计模式之《适配器模式》及应用场景
- Java设计模式之《适配器模式》及应用场景
- Java设计模式之《适配器模式》及应用场景
- Java设计模式之《适配器模式》及应用场景
- Java设计模式之《适配器模式》及应用场景
- Java设计模式之《适配器模式》及应用场景
- Java设计模式之《适配器模式》及应用场景
- 通过例子学设计模式之--适配器模式以及使用场景说明(C++实现)
- java设计模式的应用场景
- ACE中的设计模式应用场景---模式初学者勿入
- 23种设计模式及其应用场景
- 226_尚学堂_高淇_java300集最全视频教程_【GOF23设计模式】_单例模式_应用场景_饿汉式_懒汉式
- ACE中的设计模式应用场景---模式初学者勿入
- Java设计模式之《代理模式》及应用场景
- 设计模式 应用场景
- Java设计模式之《外观模式》及应用场景
- 常用设计模式的应用场景
- ACE中的设计模式应用场景
- 【设计模式】单例模式的常见应用场景
- 设计模式用过哪些,应用场景是什么;单例模式有几种实现方式,代码怎么写?