定义:
  将一个类的接口转换成客户期望的另外一个接口(重点理解适配的这两个字),使得接口不兼容的类可以一起工作
适用场景:

  1. 已经存在的类,它的方法和需求不匹配的时候
  2. 在软件维护阶段考虑的设计模式

详解
  首先来从生活中的常见场景来看,一个电源插座输出都是220V,而我们一些电子设备,比如手机,MP3,MP4,所需要的电压不一样,也不可能直接就是220接上,这就需要一个中间的转换器,每个厂家不同,对应的充电线也有可能不同。这个不同的充电线就可以理解为一个适配器。而220V的输出电压可以看做是我们做好的一套系统,只不过对应到不同客户就需要不同的适配器。下面分为三个模块来讲解

1.类适配器

输出的电压类

  1. public class AC220 {
  2. public int outputAC220V(){
  3. int output = 220;
  4. System.out.println("输出交流电"+output+"V");
  5. return output;
  6. }
  7. }

5V电压接口

  1. public interface DC5 {
  2. int outputDC5V();
  3. }

适配器类

  1. public class PowerAdapter extends AC220 implements DC5{
  2.  
  3. @Override
  4. public int outputDC5V() {
  5. int adapterInput=outputAC220V();
  6. int adapterOutput = adapterInput/44;
  7. System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V");
  8. return adapterOutput;
  9. }
  10. }

测试代码:

  1. public class Test {
  2.  
  3. public static void main(String[] args) {
  4. DC5 dc5=new PowerAdapter();
  5. System.out.println(dc5.outputDC5V());
  6.  
  7. }
  8. }

输出结果

 

  可能很多人看到这就会问,这不就是装饰者模式吗?这个不就相当于扩展功能吗,其实呢这两个模式所处的阶段不同,一个是在软件设计的时候需要考虑的,这个呢是在软件后续维护的时候所考虑的。第二个其实就是最大的区别:装饰者和被装饰者之间的接口相同,而适配器和被适配器之间的接口是不相同的(当然有些特殊情况是相同的)。假如我是另外一个电子设备,我需要3V的接口,那么我们只需要再定义一个DC3接口,修改PowerAdapter里面的适配方法。但是我这个厂家并不再需要5V的接口了,所以我这边有的其实只是DC3不是DC5.看看现在的UML类图

  还是和装饰者有区别的,当然你要是通过装饰者来实现上述功能一样能实现
2.对象适配器
  上述UML图中我们可以看出AC220和PowerAdapter连接太过紧密了,如果我们这是个很复杂的系统,那这样做无疑让我们的适配器加载会很慢,毕竟子类要想初始化完,就必须要父类先初始化完,所以我们可以不用继承,而使用成员变量的方式来解决,即修改PowerAdapter的代码

  1. public class PowerAdapter implements DC5,DC3{
  2. private AC220 ac220=new AC220();
  3. private int adapterInput=ac220.outputAC220V();
  4. @Override
  5. public int outputDC5V() {
  6.  
  7. int adapterOutput = adapterInput/44;
  8. System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V");
  9. return adapterOutput;
  10. }
  11.  
  12. @Override
  13. public int outputDC3V() {
  14. int adapterOutput=adapterInput/73;
  15. System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V");
  16. return adapterOutput;
  17. }
  18. }

UML类图

  从继承变成了组合,这就是对象适配
3.JDK解读
  XmlAdapter就是一个最典型的适配器,下面我们来看代码具体分析

定义一个学生类,将学生类序列化成xml文件

  1. import javax.xml.bind.annotation.*;
  2. import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
  3. import java.util.Date;
  4.  
  5. @XmlType(propOrder={"id","name","birthDay"}) //指定序列成的xml节点顺序
  6. @XmlAccessorType(value=XmlAccessType.FIELD) //访问类型改为字段
  7. @XmlRootElement
  8. public class Student {
  9. @XmlElement
  10. private String id;
  11. @XmlElement
  12. private String name;
  13. @XmlJavaTypeAdapter(value=DateAdapter.class)
  14. @XmlElement
  15. private Date birthDay;
  16.  
  17. public String getId() {
  18. return id;
  19. }
  20.  
  21. public void setId(String id) {
  22. this.id = id;
  23. }
  24.  
  25. public String getName() {
  26. return name;
  27. }
  28.  
  29. public void setName(String name) {
  30. this.name = name;
  31. }
  32.  
  33. public Date getBirthDay() {
  34. return birthDay;
  35. }
  36.  
  37. public void setBirthDay(Date birthDay) {
  38. this.birthDay = birthDay;
  39. }
  40.  
  41. @Override
  42. public String toString() {
  43. return "Student{" +
  44. "id='" + id + '\'' +
  45. ", name='" + name + '\'' +
  46. ", birthDay=" + birthDay +
  47. '}';
  48. }
  49. }

Date适配器

  1. import java.text.SimpleDateFormat;
  2. import java.util.Date;
  3.  
  4. import javax.xml.bind.annotation.adapters.XmlAdapter;
  5.  
  6. public class DateAdapter extends XmlAdapter<String, Date> {
  7. //反序列化成日期对象Date
  8. @Override
  9. public Date unmarshal(String str) throws Exception {
  10. SimpleDateFormat format = getSimpleDateFormat("yyyy-MM HH:mm:ss");
  11. return str==null ? null:format.parse(str);
  12. }
  13. //序列化成xmL
  14. @Override
  15. public String marshal(Date date) throws Exception {
  16. SimpleDateFormat format = getSimpleDateFormat("yyyy-MM HH:mm:ss");
  17. return date==null ? "":format.format(date);
  18. }
  19. private SimpleDateFormat getSimpleDateFormat(String pattern){
  20. SimpleDateFormat format = new SimpleDateFormat(pattern);
  21. return format;
  22. }
  23. }

编写测试类

  1. import javax.xml.bind.JAXBContext;
  2.  
  3. import javax.xml.bind.Marshaller;
  4. import javax.xml.bind.Unmarshaller;
  5. import java.io.*;
  6. import java.util.Date;
  7.  
  8. public class Test {
  9.  
  10. public static void main(String[] args) {
  11. DateAdapter da=new DateAdapter();
  12. Student stu = new Student();
  13. stu.setId("1");
  14. stu.setName("方块人");
  15. stu.setBirthDay(new Date());
  16. try {
  17. JAXBContext context = JAXBContext.newInstance(Student.class);
  18. Marshaller marshaller = context.createMarshaller();
  19. marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
  20. marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
  21. //marshaller.marshal(stu, System.out);
  22. StringWriter writer = new StringWriter();
  23. marshaller.marshal(stu, writer);
  24. System.out.println(writer.toString());
  25. //反序列化
  26. Unmarshaller unmarshaller = context.createUnmarshaller();
  27. StringReader reader = new StringReader(writer.toString());
  28. Student stu2 = (Student) unmarshaller.unmarshal(reader);
  29.  
  30. } catch (Exception e) {
  31. e.getMessage();
  32. e.printStackTrace();
  33. }
  34. }
  35. }

输出结果:

  注意看birthDay这个Date类型,我们已经在转化成xml文件的时候将日期格式变化成功,如果是没有进行日期适配器转换的话的输出结果是

  这样就不是我们想要的格式

XmlAdapter源码:

  1. public abstract class XmlAdapter<ValueType,BoundType> {
  2.  
  3. /**
  4. * Do-nothing constructor for the derived classes.
  5. */
  6. protected XmlAdapter() {}
  7.  
  8. /**
  9. * Convert a value type to a bound type.
  10. *
  11. * @param v
  12. * The value to be converted. Can be null.
  13. * @throws Exception
  14. * if there's an error during the conversion. The caller is responsible for
  15. * reporting the error to the user through {@link javax.xml.bind.ValidationEventHandler}.
  16. */
  17. public abstract BoundType unmarshal(ValueType v) throws Exception;
  18.  
  19. /**
  20. * Convert a bound type to a value type.
  21. *
  22. * @param v
  23. * The value to be convereted. Can be null.
  24. * @throws Exception
  25. * if there's an error during the conversion. The caller is responsible for
  26. * reporting the error to the user through {@link javax.xml.bind.ValidationEventHandler}.
  27. */
  28. public abstract ValueType marshal(BoundType v) throws Exception;
  29. }

  有两个方法需要实现,一个是反序列化,另外一个是序列化。相信你看懂了适配器模式之后,也能理解这个类中的方法含义

总结:

  适配器模式更多的是提供不同接口给不同的厂家,适配器我们知道怎么实现之后,更多的是在适用场景中去思考。这样就会对适配器模式有更加深刻的理解。

结合JDK源码看设计模式——适配器模式的更多相关文章

  1. 结合JDK源码看设计模式——桥接模式

    前言: 在我们还没学习框架之前,肯定都学过JDBC.百度百科对JDBC是这样介绍的[JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Jav ...

  2. 结合JDK源码看设计模式——单例模式

    定义: 保证一个类仅有一个实例,并提供一个全局访问点 适用场景: 确保任何情况下这个对象只有一个实例 详解: 私有构造器 单利模式中的线程安全+延时加载 序列化和反序列化安全, 防止反射攻击 结合JD ...

  3. 结合JDK源码看设计模式——原型模式

    定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比较 ...

  4. 结合JDK源码看设计模式——简单工厂、工厂方法、抽象工厂

    三种工厂模式的详解: 简单工厂模式: 适用场景:工厂类负责创建的对象较少,客户端只关心传入工厂类的参数,对于如何创建对象的逻辑不关心 缺点:如果要新加产品,就需要修改工厂类的判断逻辑,违背软件设计中的 ...

  5. 结合JDK源码看设计模式——模板方法模式

    前言: 相信很多人都听过一个问题:把大象关进冰箱门,需要几步? 第一,把冰箱门打开:第二,把大象放进去:第三,把冰箱门关上.我们可以看见,这个问题的答案回答的很有步骤.接下来我们介绍一种设计模式--模 ...

  6. 结合JDK源码看设计模式——迭代器模式

    前言: Iterator翻译过来就是迭代器的意思.在前面的工厂模式中就介绍过了iterator,不过当时介绍的是方法,现在从Iterator接口的设计来看,似乎又是一种设计模式,下面我们就来讲讲迭代器 ...

  7. 结合JDK源码看设计模式——享元模式

    前言 在说享元模式之前,你一定见到过这样的面试题 public class Test { public static void main(String[] args) { Integer a=Inte ...

  8. 结合JDK源码看设计模式——建造者模式

    概念: 将一个复杂对象的构建与它的表示分离.使得同样构建过程可以创建不同表示适用场景: 一个对象有很多属性的情况下 想把复杂的对象创建和使用分离 优点: 封装性好,扩展性好 详解: 工厂模式注重把这个 ...

  9. 结合JDK源码看设计模式——观察者模式

    前言: 现在我们生活中已经离不开微信,QQ等交流软件,这对于我们来说不仅是交流,更有在朋友圈中或空间中进行分享自己的生活,同时也可以通过这个渠道知道别人的生活.我们在看朋友圈的时候其实我们扮演的就是一 ...

随机推荐

  1. ELK 架构之 Elasticsearch 和 Kibana 安装配置

    阅读目录: 1. ELK Stack 简介 2. 环境准备 3. 安装 Elasticsearch 4. 安装 Kibana 5. Kibana 使用 6. Elasticsearch 命令 最近在开 ...

  2. webpack bug及解决方案

    1.webpack打包后z-index失效 解决方案:z-index设置成行内样式,例如:root.style.cssText = 'z-index:100000 !important;';

  3. java线程间通信之通过管道进行通信

    管道流PipeStream是一种特殊的流,用于在不同线程间直接传送数据,而不需要借助临时文件之类的东西. jdk中提供了四个类来使线程间可以通信: 1)PipedInputStream和PipedOu ...

  4. Java bean和json互转时,屏蔽某个属性

    有的时候我们把java bean 转换成json的时候,希望屏蔽掉某个属性,这时可以在java bean的属性上加上@JsonIgnore注解,在com.fasterxml.jackson.annot ...

  5. Markdown编辑器editor.md的使用---markdown上传图片

    http://kindeditor.org/ 确定下有没有查找替换功能 http://pandao.github.io/editor.md/ http://pandao.github.io/edito ...

  6. 详解Linux文档属性、拥有者、群组、权限、差异

    写在前面 我们都知道Linux是一个支持多用户.多任务的系统,这也是它最优秀的特性,即可能同时有很多人都在系统上进行工作,所以千万不要强制关机,同时,为了保护每个人的隐私和工作环境,针对某一个文档(文 ...

  7. 10. 搭配redis做文章缓存

    redis是一个使用较多的内存键值数据库,这儿的键是字符串类型的标识符,而值可以是字符串.散列.列表.集合和有序集合,也正是因为redis提供了较丰富的值的类型,能够满足不同的使用要求,而且redis ...

  8. QM1_Time value of Money

    总体框架 Time Value  Interest Rate rf: 无风险收益率 (CFA中一般认为是美国短期国债T-bill的收益率) Nominal risk-free rate: 名义无风险税 ...

  9. 用java代码将从数据库中取出的具有父子关系的数据转成json格式

    思路:①.取出数据中的所有父节点放入一个集合中②.取出数据中所有为该父节点的子节点放入另一个集合中③.用到迭代的方法将子节点一层一层的遍历工具类:package com.assasion.test;i ...

  10. 【状态表示】Bzoj1096 [SCOI2008] 着色方案

    Description 有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块.所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n.相邻两个木 ...