设计模式学习笔记------适配器模式

              适配器模式

一、适配器模式

1.适配器模式结构图

                    

 

 2.适配器模式示例代码

 1 (1)先看看Target接口定义的示例代码如下
 2 /** 
 3  * 定义客户端使用的接口,与特定领域相关
 4  */
 5 public interface Target {
 6     /**
 7      * 示意方法,客户端请求处理方法
 8      */
 9     public void request();
10 }
11 (2)再看看需要被适配的对象定义。示例代码如下:
12 /**
13  * 已经存在的接口,这个接口需要配适配
14  */
15 public class Adaptee{
16     /**
17      * 示意方法,原本已经存在,已经实现的方法
18      */
19     public void specificRequest() {
20         //具体的功能处理
21     }
22 }
23 
24 (3)下面是适配器对象的基本实现。示例代码如下:
25 /**
26  * 适配器
27  */
28 public class Adapter implements Target {
29     /**
30      * 持有需要被适配的接口对象
31      */
32     private Adaptee adaptee;
33     
34     /**
35      * 构造方法,传入需要被适配的对象
36      * @param adaptee
37      */
38     public Adapter(Adaptee adaptee) {
39         this.adaptee = adaptee;
40     }
41     
42     @Override
43     public void request() {
44         //可能转调已经实现了的方法,进行适配
45         adaptee.specificRequest();
46     }
47     
48 }
49 
50 (4)再来看看使用适配器客户端的示例代码如下:
51 /**
52  * 使用适配器的客户端
53  */
54 public class Client {
55     public static void main(String[] args) {
56         //创建需要被适配的对象
57         Adaptee adaptee = new Adaptee();
58         //创建客户端需要调用的接口对象
59         Target target = new Adapter(adaptee);
60         //请求处理
61         target.request();
62     }
63 }

二、具体实现代码---日志管理

  需求一:用户要求日志以文件的形式记录。

  1 /**
  2  * 日志数据对象
  3  * @author abc
  4  *
  5  */
  6 public class LogModel implements Serializable {
  7     private static final long serialVersionUID = 1L;
  8     
  9     /**
 10      * 日志编号
 11      */
 12     private String logId;
 13     /**
 14      * 操作人员
 15      */
 16     private String operateUser;
 17     /**
 18      * 操作时间,以yyyy-MM-dd HH:mm:ss的格式记录
 19      */
 20     private String operateTime;
 21     /**
 22      * 日志内容
 23      */
 24     private String logContent;
 25     public String getLogId() {
 26         return logId;
 27     }
 28     public void setLogId(String logId) {
 29         this.logId = logId;
 30     }
 31     public String getOperateUser() {
 32         return operateUser;
 33     }
 34     public void setOperateUser(String operateUser) {
 35         this.operateUser = operateUser;
 36     }
 37     public String getOperateTime() {
 38         return operateTime;
 39     }
 40     public void setOperateTime(String operateTime) {
 41         this.operateTime = operateTime;
 42     }
 43     public String getLogContent() {
 44         return logContent;
 45     }
 46     public void setLogContent(String logContent) {
 47         this.logContent = logContent;
 48     }
 49     @Override
 50     public String toString() {
 51         return "logId=" + logId + ", operateUser=" + operateUser + ", operateTime=" + operateTime
 52                 + ", logContent=" + logContent;
 53     }
 54     
 55     
 56 }
 57 
 58 /**
 59  * 日志文件操作接口
 60  * @author abc
 61  *
 62  */
 63 public interface LogFileOperate {
 64     
 65     /**
 66      * 读取日志文件,从文件里面获取存储的日志列表对象
 67      * @return 存储的日志列表对象
 68      */
 69     public List<LogModel> readLogFile();
 70     
 71     /**
 72      * 写日子文件,把日志列表写出到日志文件中
 73      * @param list 要写到日志文件的日志列表
 74      */
 75     public void writeLogFile(List<LogModel> list);
 76     
 77 }
 78 
 79 /**
 80  * 日志文件操作接口实现
 81  * @author abc
 82  *
 83  */
 84 public class LogFileOperateImpl implements LogFileOperate {
 85     
 86     /**
 87      * 日志文件的路径和文件名称,默认是当前项目根路劲下的AdapterLog.log
 88      */
 89     private String logFilePathName = "AdapterLog.log";
 90     
 91     /**
 92      * 构造方法,传入文件的路径和名称
 93      * @param logFilePathName 文件路径和名称
 94      */
 95     public LogFileOperateImpl(String logFilePathName) {
 96         if (logFilePathName != null && logFilePathName.trim().length() > 0) {
 97             //判断是否传入了文件的路径和名称,传入就重置路径和名称
 98             this.logFilePathName = logFilePathName;
 99         }
100     }
101 
102     @Override
103     public List<LogModel> readLogFile() {
104         List<LogModel> list = null;
105         ObjectInputStream oin = null;
106         try {
107             File f = new File(logFilePathName);
108             if (f.exists()) {
109                 oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(f)));
110                 list = (List<LogModel>)oin.readObject();
111             }
112         } catch (Exception e) {
113             // TODO Auto-generated catch block
114             e.printStackTrace();
115         } finally {
116             try{
117                 if (oin != null) {
118                     oin.close();
119                     oin = null;
120                 }
121             } catch (IOException e) {
122                 e.printStackTrace();
123             }
124         }
125         return list;
126     }
127 
128     @Override
129     public void writeLogFile(List<LogModel> list) {
130         File f = new File(logFilePathName);
131         ObjectOutputStream oos = null;
132         try {
133             oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
134             oos.writeObject(list);
135         }  catch (IOException e) {
136             // TODO Auto-generated catch block
137             e.printStackTrace();
138         } finally {
139             try {
140                 if (oos != null) {
141                     oos.close();
142                     oos = null;
143                 }
144             } catch (IOException e) {
145                 // TODO Auto-generated catch block
146                 e.printStackTrace();
147             }
148         }
149 
150     }
151 
152 }
153 
154 /**
155  * 客户端测试
156  * @author abc
157  *
158  */
159 public class Client {
160     public static void main(String[] args) {
161         //准备日志内容,测试的数据
162         LogModel model = new LogModel();
163         model.setLogId("002");
164         model.setOperateUser("admin1");
165         model.setOperateTime("2018-01-02 16:16:00");
166         model.setLogContent("这是一个测试");
167         
168         List<LogModel> list = new ArrayList<LogModel>();
169         list.add(model);
170         //创建操作日志文件的对象
171         LogFileOperate logFileOperate = new LogFileOperateImpl("");
172         //保存日志文件
173         logFileOperate.writeLogFile(list);
174         
175         //读取日子文件的内容
176         List<LogModel> readLog = logFileOperate.readLogFile();
177         System.out.println("readLog=" + readLog);
178         
179     }
180 }

  需求二:用户要求日志以数据库的形式管理日志。

/**
 * 定义操作的应用接口
 * @author abc
 *
 */
public interface LogDbOperate {
    
    /**
     * 新增日志
     * @param model 需要新增的日志对象
     */
    public void createLog(LogModel model);
    /**
     * 修改日志
     * @param model 需要修改的日志对象
     */
    public void updateLog(LogModel model);
    /**
     * 删除日志
     * @param model 需要删除的日志对象
     */
    public void removeLog(LogModel model);
    /**
     * 获取所有的日志
     * @return 所有日志对象集合
     */
    public List<LogModel> getAllLog();
}

/**
 * 定义操作的应用接口实现类
 * @author abc
 */
public interface LogDbOperateImpl {
        //具体实现内容      
}

/**
 * 客户端测试
 * @author abc
 *
 */
public class Client {
    public static void main(String[] args) {
        //准备日志内容,测试的数据
        LogModel model = new LogModel();
        model.setLogId("002");
        model.setOperateUser("admin1");
        model.setOperateTime("2018-01-02 16:16:00");
        model.setLogContent("这是一个测试");
        
        List<LogModel> list = new ArrayList<LogModel>();
        list.add(model);
        //创建操作日志文件的对象
        LogFileOperate logFileOperate = new LogFileOperateImpl("");
        
        LogDbOperate logDbOperate  = new Adapter(logFileOperate);
        //保存日志文件
        logDbOperate.createLog(model);
        //读取日志文件的内容
        List<LogModel> allLog = logDbOperate.getAllLog();
        System.out.println("allLog=" + allLog);
    }
}

思路总结:

1.原有存取日志的方式

 

2.现在有了新的基于数据库的实现,新的实现由自己的接口

 

3.现在想要在第二版的实现里面,能够同时兼容第一版的功能,那么就应有一个类来实现第二版的接口,然后在这个类里面去调用已有的功能实现,这个类就是适配器

 

 

整体结构

 

三、认识适配器模式

  1.模式的功能

    适配器模式的主要功能是进行转换匹配,目的是复用已有的功能,而不是来实现新的接口。也就是说,客户端需要的功能应该是已经实现好了的,不需要适配器模式来实现,适配器模式主要负责把不兼容的接口转换成客户端期望的样子就可以了。

    但这并不是说,在适配器里面就不能实现功能。适配器里面可以实现功能,称这种适配器为智能适配器。再说了,在接口匹配和转换的过程中,也有可能需要额外实现一定的功能,才能转换过来,比如需要调整参数以进行匹配等。

  2.Adaptee 和 Target 的关系

    适配器模式中被适配的接口Adaptee和适配成为的接口Target是没有关联的,也就是说,Adaptee和Target中的方法既可以相同,也可以不同。极端情况下两个接口里面方法可能完全不同的,当然这种情况下也可以完全相同。

    这里所说的相同和不同,是指方法定义的名称、参数列表、返回值,以及方法本省的功能都可以相同或不同。    

  3.对象组合

    根据前面的实现,你会发现,适配器的实现方式其实是依靠对象组合的方式。通过给适配器对象组合被适配的对象,然后当客户端调用Targer的时候,适配器会吧相应的功能委托给适配的对象去完成。

  4.适配器模式的调用顺序示意图

   

 

四、双向适配器

 

  1 /**
  2  * 定义操作的应用接口实现
  3  * @author abc
  4  *
  5  */
  6 public class LogDbOperateImpl implements LogDbOperate {
  7 
  8     @Override
  9     public void createLog(LogModel model) {
 10         System.out.println("新增日志:" + model);
 11     }
 12 
 13     @Override
 14     public void updateLog(LogModel model) {
 15         System.out.println("修改日志:" + model);
 16     }
 17 
 18     @Override
 19     public void removeLog(LogModel model) {
 20         System.out.println("删除日志:" + model);
 21     }
 22 
 23     @Override
 24     public List<LogModel> getAllLog() {
 25         System.out.println("获取所有日志");
 26         return null;
 27     }
 28 
 29 }
 30 
 31 /**
 32  * 双向适配器
 33  * @author abc
 34  *
 35  */
 36 public class TwoDirectAdater implements LogDbOperate, LogFileOperate {
 37     
 38     /**
 39      * 持有需要被适配的文件存储日志的接口对象
 40      */
 41     private LogFileOperate logFileOperate;
 42     
 43     /**
 44      * 持有需要被适配的DB存储日志的接口对象
 45      */
 46     private LogDbOperate logDbOperate;
 47     
 48     /**
 49      * 构造方法,传入需要被适配的对象
 50      * @param logFileOperate 持有需要被适配的文件存储日志的接口对象
 51      */
 52     public TwoDirectAdater(LogFileOperate logFileOperate) {
 53         this.logFileOperate = logFileOperate;
 54     }
 55     
 56     /**
 57      * 构造方法,传入需要被适配的对象
 58      * @param logDbOperate 持有需要被适配的DB存储日志的接口对象
 59      */
 60     public TwoDirectAdater(LogDbOperate logDbOperate) {
 61         this.logDbOperate = logDbOperate;
 62     }
 63     
 64     /**
 65      * 构造方法,传入需要被适配的对象
 66      * @param logFileOperate 持有需要被适配的文件存储日志的接口对象
 67      * @param logDbOperate 持有需要被适配的DB存储日志的接口对象
 68      */
 69     public TwoDirectAdater(LogFileOperate logFileOperate, LogDbOperate logDbOperate) {
 70         this.logFileOperate = logFileOperate;
 71         this.logDbOperate = logDbOperate;
 72     }
 73     
 74     /*------------------DB操作的范式适配为文件实现方式的接口---------------------*/
 75     @Override
 76     public List<LogModel> readLogFile() {
 77         return logDbOperate.getAllLog();
 78     }
 79 
 80     @Override
 81     public void writeLogFile(List<LogModel> list) {
 82         //1.删除所有数据库中的数据
 83         //2.循环把现在的数据加入到数据库中
 84         for (LogModel lm : list) {
 85             logDbOperate.createLog(lm);
 86         }
 87     }
 88     
 89     /*-----------------文件操作的方式适配成为DB实现方式的接口---------------------*/
 90     @Override
 91     public void createLog(LogModel model) {
 92         //1.读取文件内容
 93         List<LogModel> list = logFileOperate.readLogFile();
 94         //2.加入新得日志队形
 95         list.add(model);
 96         //3.重新写入文件
 97         logFileOperate.writeLogFile(list);
 98     }
 99 
100     @Override
101     public void updateLog(LogModel model) {
102         //1.读取文件的内容
103         List<LogModel> list = logFileOperate.readLogFile();
104         //2.修改相对应的日志对象
105         for (int i = 0; i < list.size(); i++) {
106             if (list.get(i).getLogId().equals(model.getLogId())) {
107                 list.set(i, model);
108                 break;
109             }
110         }
111     }
112 
113     @Override
114     public void removeLog(LogModel model) {
115         //1.读取文件的内容
116         List<LogModel> list = logFileOperate.readLogFile();
117         //2.删除相对应的日志对象
118         list.remove(model);
119     }
120 
121     @Override
122     public List<LogModel> getAllLog() {
123         return logFileOperate.readLogFile();
124     }
125 
126 }
127 
128 /**
129  * 客户端测试
130  * @author abc
131  *
132  */
133 public class Client {
134     public static void main(String[] args) {
135         //准备日志内容,测试的数据
136         LogModel model = new LogModel();
137         model.setLogId("003");
138         model.setOperateUser("admin2");
139         model.setOperateTime("2018-01-03 16:16:00");
140         model.setLogContent("这是一个测试");
141         
142         List<LogModel> list = new ArrayList<LogModel>();
143         list.add(model);
144         //创建操作日志文件的对象
145         LogFileOperate logFileOperate = new LogFileOperateImpl("");
146         LogDbOperate logDbOperate = new LogDbOperateImpl();
147         
148         //创建经过双向适配后的操作日子的接口对象
149         /*LogFileOperate logFileOperate2 = new TwoDirectAdater(logFileOperate, logDbOperate);
150         LogDbOperate logDbOperate2 = new TwoDirectAdater(logFileOperate, logDbOperate);*/
151         LogFileOperate logFileOperate2 = new TwoDirectAdater(logFileOperate);
152         LogDbOperate logDbOperate2 = new TwoDirectAdater(logDbOperate);
153         
154         //先测试从文件操作适配到数据库操作
155         //虽然用的式数据操作接口,其实是文件操作在实现
156         logDbOperate2.createLog(model);
157         List<LogModel> allLog = logDbOperate2.getAllLog();
158         System.out.println("allLog=" + allLog);
159         
160         //在测试从数据库操作适配到文件操作
161         //是数据库操作实现的
162         logFileOperate2.writeLogFile(list);
163         logFileOperate2.readLogFile();
164     }
165 }

双向适配器实现类Target 和 Adaptee的接口,使得双向适配器可以在Target 或 Adaptee被使用的地方使用,一提供对所有客户的透明性。尤其在两个不同客户需要用不用的方式查看一个对象时适合双向适配器。

五、适配器模式的优缺点

  优点:

    更好的复用性

      如果功能是已经有了的,只是接口不兼容,那么通过适配器模式就可以让这些功能得到更好的复用。

    更好的可扩展性

      在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

  缺点:

    过多的使用适配器,会让系统零乱,不容易进行把握

    比如,命名看到调用A接口,其实内部被适配成了B接口来实现,一个系统如果出现太多这种情况,无异于异常灾难。因此如果不是很必要,可以不使用适配器,而是直接对系统进行重构。

六、何时选用适配器模式

  1.适配器本质:转换匹配,复用功能

  2.何时选用适配器模式

    a.如果你想要的室友一个已经存在的类,但是它的接口不符合你的需求,这种情况可以使用适配器模式,来把已有的实现转换你需要的接口。

    b.如果你想创建一个可以复用的类,这个类可能和一些不兼容的类一起工作,这种情况可以使用适配器模式,到时候需要什么就适配什么。

    c.如果你想使用一些已经存在子类,但是不可能对每一个子类都进行适配,这种情况可以选用对象适配,直接适配这些子类的父类就可以了。

转载于:https://www.cnblogs.com/chensheng0617/p/8178359.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值