设计模式的应用场景(7)--适配器模式

适配器模式

定义:将一个系统的接口转换成另外一种形式,从而使原来不能直接调用的接口变得可以调用。
优点
适配器模式也是一种包装模式,它与装饰模式同样具有包装的功能,此外,对象适配器模式还具有委托的意思。总的来说,适配器模式属于补偿模式,专用来在系统后期扩展、修改时使用。

缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 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;
    }
}

那么问题来了,两边接口不一样,需要适配器

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。

总的来说,建议使用对象适配器方式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值