您的位置:首页 > 其它

设计模式的应用场景(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。

总的来说,建议使用对象适配器方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: