一、场景问题
新建一个日志数据模型类,也就是要写入文件的数据对象,因为要写入文件,所以需要序列化,使用implements实现Serializable接口(书中此处没有实现,代码运行报错了)
package com.chenxyt.java.test;
import java.io.Serializable;
//日志内容数据模型 序列化
public class LogModel implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
//日志Id
private String logId;
//操作用户
private String operateUser;
//操作时间
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;
}
//打印时将对象转成Sring
public String toString(){
return "logId="+logId+",operateUser="+operateUser+",operateTime="+operateTime+",logContent="+logContent;
}
}
然后创建一个日志操作接口,声明读、写文件的方法
package com.chenxyt.java.test;
import java.util.List;
//日志操作接口 多个内容 采用List存储
public interface LogFileOperateApi {
//读取文件内容
public List<LogModel> readFile();
//写入文件内容
public void writeLogFile(List<LogModel> list);
}
实现上边的方法,实现过程就是文件的读写操作
package com.chenxyt.java.test;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
//日志操作类
public class LogFileOperate implements LogFileOperateApi{
//日志文件的路径
private String LogFileName = "Adapter.log";
public LogFileOperate(String LogFileName) {
// TODO Auto-generated constructor stub
//如果传的不是空 则更新文件名
if(LogFileName!=null&&LogFileName.trim().length()>0){
this.LogFileName = LogFileName;
}
}
@Override
public List<LogModel> readFile() {
// TODO Auto-generated method stub
List<LogModel> list = null;
ObjectInputStream oin = null;
try {
//打开文件
File f = new File(LogFileName);
if(f.exists()){
//文件内容读取
oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(f)));
list = (List<LogModel>)oin.readObject();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
if(oin!=null){
oin.close();
}
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return list;
}
@Override
public void writeLogFile(List<LogModel> list) {
// TODO Auto-generated method stub
File f = new File(LogFileName);
ObjectOutputStream out = null;
try {
//写入文件
out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
out.writeObject(list);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
新建一个测试的客户端类,测试日志读写
package com.chenxyt.java.test;
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
LogModel lgm = new LogModel();
lgm.setLogId("1");
lgm.setOperateUser("chenxyt");
lgm.setOperateTime("2018-3-30 15:08:29");
lgm.setLogContent("This is a Test");
List<LogModel> list = new ArrayList<LogModel>();
list.add(lgm);
LogFileOperateApi opt = new LogFileOperate("");
//写入文件
opt.writeLogFile(list);
//读取文件
List<LogModel> readLog = opt.readFile();
System.out.println("readLog==" + readLog);
}
}
运行结果:
![](https://i-blog.csdnimg.cn/blog_migrate/8e697aa746af5b149bd460cc716114ed.png)
package com.chenxyt.java.test;
import java.util.List;
//数据库操作日志接口
public interface LogDbOperateApi {
//新增日志
public void createLog(LogModel lm);
//修改日志
public void updateLog(LogModel lm);
//删除日志
public void deleteLog(LogModel lm);
//获取所有日志
public List<LogModel> getAllLog();
}
这里简单的写了一下数据库操作日志提供的接口,具体操作暂且不管,只是知道我们现在有两版实现日志功能的操作。
二、解决方案
适配器模式的结构图如下,这里为了便于理解没有采用UML类图:
![](https://i-blog.csdnimg.cn/blog_migrate/ccef1bd51a96c87fa59a1795878690c4.png)
现有的接口:
package com.chenxyt.java.practice;
//客户端使用的接口
public interface Target {
public void request();
}
已经存在的类:
package com.chenxyt.java.practice;
//原来已经存在的方法 需要被适配的方法
public class Adaptee {
public void specificRequest(){
System.out.println("适配成功!");
}
}
适配器:
package com.chenxyt.java.practice;
//适配器的实现
public class Adapter implements Target{
//定义要适配的对象
private Adaptee adaptee;
//构造方法 传入需要适配的对象
public Adapter(Adaptee adaptee) {
// TODO Auto-generated constructor stub
this.adaptee = adaptee;
}
@Override
public void request() {
// TODO Auto-generated method stub
//转调已经实现了的方法,进行适配
adaptee.specificRequest();
}
}
客户端调用:
package com.chenxyt.java.practice;
//使用适配器的客户端
public class Client {
public static void main(String[] args) {
//创建要适配的对象
Adaptee adaptee = new Adaptee();
//定义客户端需要调用的接口对象
Target target = new Adapter(adaptee);
//调用方法
target.request();
}
}
这里我说一下我的理解,实际上所谓的适配器,就是现有接口的一个实现,接口的实现会根据构造器不同而调用不同的方法,也就是向上转型。比如上述客户端中的Target target = new Adapter(adaptee);这里由于使用了Adapter构造器,所以走到了Adapter的实现中,然后这个实现传入了要适配的对象,在接口的方法实现中调用了这个对象的方法。实际上这个接口还有另外一个实现,结合前边日志系统的例子说的话,另外一个实现就是数据库形式的日志管理系统,适配器里实现的就是文件形式的管理系统。
这一部分代码是我自己实现的,忽略了日志操作的细节,以及舍弃了书中关于修改与删除的部分,思想都是一样的,具体业务场景具体分析。自己写的原因是想看一下自己是否知道怎么使用。
package com.chenxyt.java.test;
import java.util.List;
//适配器类
public class Adapter implements LogDbOperateApi{
//要适配的对象
private LogFileOperate logFileOperate;
private List<LogModel> list;
//构造方法,传入要适配的对象
public Adapter(LogFileOperate logFileOperate,List<LogModel> list){
this.logFileOperate = logFileOperate;
this.list = list;
}
@Override
public void createLog(LogModel lm) {
// TODO Auto-generated method stub
logFileOperate.writeLogFile(list);
}
@Override
public void updateLog(LogModel lm) {
// TODO Auto-generated method stub
}
@Override
public void deleteLog(LogModel lm) {
// TODO Auto-generated method stub
}
@Override
public List<LogModel> getAllLog() {
// TODO Auto-generated method stub
return logFileOperate.readFile();
}
}
因为调用形式变了,所以客户端代码需要修改一下,使用现有的接口对象,调用现有的方法。
package com.chenxyt.java.test;
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
LogModel lgm = new LogModel();
//要适配的方法
LogFileOperate logFileOperate = new LogFileOperate("");
lgm.setLogId("1");
lgm.setOperateUser("chenxyt");
lgm.setOperateTime("2018-3-30 17:48:19");
lgm.setLogContent("This is a Test");
List<LogModel> list = new ArrayList<LogModel>();
list.add(lgm);
//定义接口对象 使用适配类 传入要适配的对象跟操作的参数
LogDbOperateApi opt = new Adapter(logFileOperate,list);
//写入文件
opt.createLog(null);
//读取文件
List<LogModel> readLog = opt.getAllLog();
System.out.println("readLog==" + readLog);
}
}
这里我把原来的文件删了,重新运行了一遍,运行结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/1fe0dbe15c48ed358a0b994be73a8f67.png)
package com.chenxyt.java.practice;
public interface Processor{
String name();
Object processFunc(Object input);
}
然后是个Apply类,该类有个静态方法,而该方法只能调用Processor接口类型的参数,在本例中相当于客户端要调用的方法:
package com.chenxyt.java.practice;
public class Apply {
public static void process(Processor p,Object s){
System.out.println("Using Processor" + p.name());
System.out.println(p.processFunc(s));
}
}
然后是一个Filter类,这个类是以前就存在的,相当于本例中的旧版本的日志记录功能,我们不能对其修改,但是要在新的接口中调用这个功能,因此需要适配:
package com.chenxyt.java.practice;
public class Filter{
public String name(){
return getClass().getSimpleName();
}
public Waveform process(Waveform input){
return input;
}
}
这里看似稍微复杂了一些因为这个接口的process方法返回了Waveform类型,实际上就是多了一个返回类型的转换:
package com.chenxyt.java.practice;
public class Waveform {
private static long counter;
private final long id = counter++;
public String toString(){
return "Waveform" + id;
}
}
与本例相比,旧的方法实现(Filter类)还多了几个子类,因为类的继承关系,所以适配基类的接口,同样可以用来适配子类:
package com.chenxyt.java.practice;
public class HighPass extends Filter{
double cutoff;
public HighPass(double cutoff) {
this.cutoff = cutoff;
}
public Waveform process(Waveform input){
return input;
}
}
package com.chenxyt.java.practice;
public class LowPass extends Filter{
double cutoff;
public LowPass(double cutoff) {
this.cutoff = cutoff;
}
public Waveform process(Waveform input){
return input;
}
}
package com.chenxyt.java.practice;
public class BandPass extends Filter {
double lowCutoff,highCutoff;
public BandPass(double lowCutoff,double highCutoff){
this.lowCutoff = lowCutoff;
this.highCutoff = highCutoff;
}
public Waveform process(Waveform input){
return input;
}
}
以上代码涵盖了本例中新接口、旧版本的实现、现有客户端要调用的方法,接下来是客户端和适配器,这里写到了一起:
package com.chenxyt.java.practice;
class FilterAdapter implements Processor{
Filter filter = new Filter();
public FilterAdapter(Filter filter) {
this.filter = filter;
}
@Override
public String name() {
// TODO Auto-generated method stub
return filter.name();
}
public Waveform processFunc(Object input) {
// TODO Auto-generated method stub
return filter.process((Waveform)input);
}
}
public class FilterProcessor{
public static void main(String[] args) {
Waveform w = new Waveform();
Apply.doProcess(new FilterAdapter(new LowPass(1.0)),w);
Apply.doProcess(new FilterAdapter(new HighPass(2.0)),w);
Apply.doProcess(new FilterAdapter(new BandPass(3.0,4.0)),w);
}
}
按照适配器模式的思想梳理一下,建立一个FilterAdapter适配器类,该类实现了Processor接口,然后在实现方法processFunc内部调用了filter.process方法。processFunc是个可以接受协变返回值的方法。main函数里,为了看着清楚一些,我们修改一下:
public static void main(String[] args) {
Waveform w = new Waveform();
Processor p1 = new FilterAdapter(new LowPass(1.0));
Processor p2 = new FilterAdapter(new HighPass(2.0));
Processor p3 = new FilterAdapter(new BandPass(3.0,4.0));
Apply.doProcess(p1,w);
Apply.doProcess(p2,w);
Apply.doProcess(p3,w);
}
Processor接口对象的类型为FilterAdapter,然后调用Apply中方法的时候传递这个对象,经过向上转型、动态加载等一系列操作,编译器就知道应该执行哪个方法了。这样下来,这个适配器模式的示例就理清楚了。
三、模式讲解
四、双向适配
package com.chenxyt.java.test;
import java.util.List;
//DB存储日志的实现 public class LogDbOperate implements LogDbOperateApi {
@Override public void createLog(LogModel lm) { // TODO Auto-generated method stub System.out.println("现在进行数据库存储日志" + lm); } @Override public void updateLog(LogModel lm) { // TODO Auto-generated method stub System.out.println("现在更新数据库日志" + lm); } @Override public void deleteLog(LogModel lm) { // TODO Auto-generated method stub System.out.println("现在删除数据库日志" + lm); } @Override public List<LogModel> getAllLog() { // TODO Auto-generated method stub System.out.println("现在获取数据库日志"); return null; }
}
然后是现实双向适配器,因为是双向适配,所以它实现了两个接口,这里类的构造函数我分开了写,便于后边调用理解,原文中写了一个构造器传入了两个对象参数:
package com.chenxyt.java.test;
import java.util.List;
public class TwoDirectAdapter implements LogDbOperateApi,LogFileOperateApi{
private LogDbOperate logDbOperate;
private LogFileOperate logFileOperate;
public TwoDirectAdapter(LogDbOperate logDbOperate) {
// TODO Auto-generated constructor stub
this.logDbOperate = logDbOperate;
}
public TwoDirectAdapter(LogFileOperate logFileOperate) {
// TODO Auto-generated constructor stub
this.logFileOperate = logFileOperate;
}
@Override
public List<LogModel> readFile() {
// TODO Auto-generated method stub
return logDbOperate.getAllLog();
}
@Override
public void writeLogFile(List<LogModel> list) {
// TODO Auto-generated method stub
for(LogModel lm:list){
logDbOperate.createLog(lm);
}
}
@Override
public void createLog(LogModel lm) {
// TODO Auto-generated method stub
List<LogModel> list = logFileOperate.readFile();
list.add(lm);
logFileOperate.writeLogFile(list);
}
@Override
public void updateLog(LogModel lm) {
// TODO Auto-generated method stub
}
@Override
public void deleteLog(LogModel lm) {
// TODO Auto-generated method stub
}
@Override
public List<LogModel> getAllLog() {
// TODO Auto-generated method stub
return logFileOperate.readFile();
}
}
package com.chenxyt.java.test;
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
LogModel lgm = new LogModel();
//要适配的方法
LogFileOperate logFileOperate = new LogFileOperate("");
LogDbOperate logDbOperate = new LogDbOperate();
lgm.setLogId("1");
lgm.setOperateUser("chenxyt");
lgm.setOperateTime("2018-3-30 17:48:19");
lgm.setLogContent("This is a Test");
List<LogModel> list = new ArrayList<LogModel>();
list.add(lgm);
//定义数据库操作接口 实际使用文件形式
LogDbOperateApi opt = new TwoDirectAdapter(logFileOperate);
//定义文件操作接口 实际使用数据库操作形式
LogFileOperateApi lgf = new TwoDirectAdapter(logDbOperate);
//写入文件
opt.createLog(lgm);
//读取文件
List<LogModel> readLog = lgf.readFile();
System.out.println("readLog==" + readLog);
}
}
五、对象适配器和类适配器
package com.chenxyt.java.test;
import java.util.List;
public class ClassAdapter extends LogFileOperate implements LogDbOperateApi{
public ClassAdapter(String LogFileName) {
super(LogFileName);
// TODO Auto-generated constructor stub
}
@Override
public void createLog(LogModel lm) {
// TODO Auto-generated method stub
List <LogModel> list = this.readFile();
list.add(lm);
this.writeLogFile(list);
}
@Override
public void updateLog(LogModel lm) {
// TODO Auto-generated method stub
}
@Override
public void deleteLog(LogModel lm) {
// TODO Auto-generated method stub
}
@Override
public List<LogModel> getAllLog() {
// TODO Auto-generated method stub
return this.readFile();
}
}