适配器模式(Adapter)

252 阅读5分钟

1.模式的定义:

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原来由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式的分类:

  • 通过继承实现Adapter (继承Adaptee)---类适配器
  • 通过委让实现Adapter (包含Adaptee对象)---对象适配器

结构图: 这里写图片描述

2.模式的UML图

这里写图片描述 Client: 调用自己需要的领域接口Target

Target: 定义客户端需要的特定领域相关的接口

Adaptee: 已经存在的接口,通常能满足客户端的功能要求,但接口与客户端的要求特定领域接口不一致,需要被适配

Adapter: 适配器,把Adaptee适配成Client需要的Target

相关代码:

/**
 * 使用适配器的客户端
 */
public class Client {	
	public static void main(String[] args) {
		//创建需被适配的对象
		Adaptee adaptee = new Adaptee();
		//创建客户端需要调用的接口对象
		Target target = new Adapter(adaptee);
		//请求处理
		target.request();
	}
}

/**
 * 定义客户端使用的接口,与特定领域相关
 */
public interface Target {
	/**
	 * 示意方法,客户端请求处理的方法
	 */
	public void request();
}

/**
 * 适配器
 */
public class Adapter implements Target {
	/**
	 * 持有需要被适配的接口对象
	 */
	private Adaptee adaptee;
	/**
	 * 构造方法,传入需要被适配的对象
	 * @param adaptee 需要被适配的对象
	 */
	public Adapter(Adaptee adaptee) {
		this.adaptee = adaptee;
	}

	public void request() {
		//可能转调已经实现了的方法,进行适配
		adaptee.specificRequest();
	}
}


/**
 * 已经存在的接口,这个接口需要被适配
 */
public class Adaptee {
	/**
	 * 示意方法,原本已经存在,已经实现的方法
	 */
	public void specificRequest() {
		//具体的功能处理
	}
}



3. 研磨设计模式

转接线可以把电源的接口适配成为新的硬盘所需要的接口,转接线的角色就是适配器(Adapter)

适配器模式的定义:将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式的本质:转换匹配,复用功能。

对象适配器和类适配器的权衡:

  1. 类适配器使用类继承的方式,对象适配器使用对象组合的方式
  2. 对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。
  3. 对于对象适配器,需要额外的引用来间接得到Adaptee。 对于对象适配器,要重定义Adaptee的行为比较困难
  4. 建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。

1)缺省适配器

缺省适配(Default Adapter)模式为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。   就是一个抽象类对功能接口的所有功能做空实现 然后子类继承这个抽象类 这样就可以对部分功能进行实现或拓展了

2) 日志管理系统

public class Client {
	public static void main(String[] args) {
		//准备日志内容,也就是测试的数据
		LogModel lm1 = new LogModel();
		lm1.setLogId("001");
		lm1.setOperateUser("admin");
		lm1.setOperateTime("2010-03-02 10:08:18");
		lm1.setLogContent("这是一个测试");
		
//		List<LogModel> list = new ArrayList<LogModel>();
//		list.add(lm1);

		//创建操作日志文件的对象
		LogFileOperateApi logFileApi = new LogFileOperate("");
		
		//创建新版的操作日志的接口对象
		LogDbOperateApi api = new Adapter(logFileApi); 
		
		//保存日志文件
		api.createLog(lm1);
		
		//读取日志文件的内容
		List<LogModel> allLog = api.getAllLog();
		System.out.println("allLog="+allLog);
	}
}


/**
 * 适配器对象,把记录日志到文件的功能适配成第二版需要的增删改查的功能
 */
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);
	}
}


/**
 * 定义操作日志的应用接口,为了示例的简单,
 * 只是简单的定义了增删改查的方法
 */
public interface LogDbOperateApi {
	/**
	 * 新增日志
	 * @param lm 需要新增的日志对象
	 */
	public void createLog(LogModel lm);
	/**
	 * 修改日志
	 * @param lm 需要修改的日志对象
	 */
	public void updateLog(LogModel lm);
	/**
	 * 删除日志
	 * @param lm 需要删除的日志对象
	 */
	public void removeLog(LogModel lm);
	/**
	 * 获取所有的日志
	 * @return 所有的日志对象
	 */
	public List<LogModel> getAllLog();
}


/**
 * 实现对日志文件的操作
 */
public class LogFileOperate implements LogFileOperateApi{
	/**
	 * 日志文件的路径和文件名称,默认是当前classpath下的AdapterLog.log
	 */
	private String logFilePathName = "AdapterLog.log";	
	/**
	 * 构造方法,传入文件的路径和名称
	 * @param logFilePathName 文件的路径和名称
	 */
	public LogFileOperate(String logFilePathName) {
		//先判断是否传入了文件的路径和名称,如果是,
		//就重新设置操作的日志文件的路径和名称
		if(logFilePathName!=null && logFilePathName.trim().length()>0){
			this.logFilePathName = logFilePathName;
		}
	}
	public  List<LogModel> readLogFile() {
		List<LogModel> list = null;
		ObjectInputStream oin = null;
		try {
			File f = new File(logFilePathName);
			if(f.exists()){
				oin = new ObjectInputStream(
						new FileInputStream(f));
				list = (List<LogModel>)oin.readObject();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				if(oin!=null){
					oin.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return list;
	}

	public void writeLogFile(List<LogModel> list){
		File f = new File(logFilePathName);
		ObjectOutputStream oout = null;
		try {
			oout = new ObjectOutputStream(
					new FileOutputStream(f));
			oout.writeObject(list);			
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				oout.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

/**
 * 日志文件操作接口
 */
public interface LogFileOperateApi {
	/**
	 * 读取日志文件,从文件里面获取存储的日志列表对象
	 * @return 存储的日志列表对象
	 */
	public List<LogModel> readLogFile();
	/**
	 * 写日志文件,把日志列表写出到日志文件中去
	 * @param list 要写到日志文件的日志列表
	 */
	public void writeLogFile(List<LogModel> list);
}

/**
 * 日志数据对象
 */
public class LogModel implements Serializable{
	/**
	 * 日志编号
	 */
	private String logId;
	/**
	 * 操作人员
	 */
	private String operateUser;
	/**
	 * 操作时间,以yyyy-MM-dd HH:mm:ss的格式记录
	 */
	private String operateTime;	
	/**
	 * 日志内容
	 */
	private String logContent;
	
	public String getLogId() {
		return logId;
	}
	public void setLogId(String logId) {
		this.logId = logId;
	}
	public String getOperateUser() {
		return operateUser;
	}
	public void setOperateUser(String operateUser) {
		this.operateUser = operateUser;
	}
	public String getOperateTime() {
		return operateTime;
	}
	public void setOperateTime(String operateTime) {
		this.operateTime = operateTime;
	}
	public String getLogContent() {
		return logContent;
	}
	public void setLogContent(String logContent) {
		this.logContent = logContent;
	}
	
	public String toString(){
		return "logId="+logId+",operateUser="+operateUser+",operateTime"+operateTime+",logContent="+logContent;
	}
}