经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
结合JDK源码看设计模式——适配器模式
来源:cnblogs  作者:方块人  时间:2019/4/8 12:45:36  对本文有异议

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

  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. @Override
  3. public int outputDC5V() {
  4. int adapterInput=outputAC220V();
  5. int adapterOutput = adapterInput/44;
  6. System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V");
  7. return adapterOutput;
  8. }
  9. }

测试代码:

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

输出结果

 

  可能很多人看到这就会问,这不就是装饰者模式吗?这个不就相当于扩展功能吗,其实呢这两个模式所处的阶段不同,一个是在软件设计的时候需要考虑的,这个呢是在软件后续维护的时候所考虑的。第二个其实就是最大的区别:装饰者和被装饰者之间的接口相同,而适配器和被适配器之间的接口是不相同的(当然有些特殊情况是相同的)。假如我是另外一个电子设备,我需要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. int adapterOutput = adapterInput/44;
  7. System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V");
  8. return adapterOutput;
  9. }
  10. @Override
  11. public int outputDC3V() {
  12. int adapterOutput=adapterInput/73;
  13. System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V");
  14. return adapterOutput;
  15. }
  16. }

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. @XmlType(propOrder={"id","name","birthDay"}) //指定序列成的xml节点顺序
  5. @XmlAccessorType(value=XmlAccessType.FIELD) //访问类型改为字段
  6. @XmlRootElement
  7. public class Student {
  8. @XmlElement
  9. private String id;
  10. @XmlElement
  11. private String name;
  12. @XmlJavaTypeAdapter(value=DateAdapter.class)
  13. @XmlElement
  14. private Date birthDay;
  15. public String getId() {
  16. return id;
  17. }
  18. public void setId(String id) {
  19. this.id = id;
  20. }
  21. public String getName() {
  22. return name;
  23. }
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27. public Date getBirthDay() {
  28. return birthDay;
  29. }
  30. public void setBirthDay(Date birthDay) {
  31. this.birthDay = birthDay;
  32. }
  33. @Override
  34. public String toString() {
  35. return "Student{" +
  36. "id='" + id + '\'' +
  37. ", name='" + name + '\'' +
  38. ", birthDay=" + birthDay +
  39. '}';
  40. }
  41. }

Date适配器

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

编写测试类

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

输出结果:

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

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

XmlAdapter源码:

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

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

总结:

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

原文链接:http://www.cnblogs.com/Cubemen/p/10661419.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号