欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

结合JDK源码看设计模式——适配器模式

程序员文章站 2022-05-11 17:49:24
定义: 将一个类的接口转换成客户期望的另外一个接口(重点理解适配的这两个字),使得接口不兼容的类可以一起工作适用场景: 详解 首先来从生活中的常见场景来看,一个电源插座输出都是220V,而我们一些电子设备,比如手机,MP3,MP4,所需要的电压不一样,也不可能直接就是220接上,这就需要一个中间的转 ......

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

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

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

1.类适配器

输出的电压类

public class ac220 {
public int outputac220v(){
int output = 220;
system.out.println("输出交流电"+output+"v");
return output;
}
}

5v电压接口

public interface dc5 {
int outputdc5v();
}

适配器类

public class poweradapter extends ac220 implements dc5{

@override
public int outputdc5v() {
int adapterinput=outputac220v();
int adapteroutput = adapterinput/44;
system.out.println("使用poweradapter输入ac:"+adapterinput+"v"+"输出dc:"+adapteroutput+"v");
return adapteroutput;
}
}

测试代码:

public class test {

public static void main(string[] args) {
dc5 dc5=new poweradapter();
system.out.println(dc5.outputdc5v());

}
}

输出结果

 结合JDK源码看设计模式——适配器模式

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

结合JDK源码看设计模式——适配器模式

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

public class poweradapter implements dc5,dc3{
private ac220 ac220=new ac220();
private int adapterinput=ac220.outputac220v();
@override
public int outputdc5v() {

int adapteroutput = adapterinput/44;
system.out.println("使用poweradapter输入ac:"+adapterinput+"v"+"输出dc:"+adapteroutput+"v");
return adapteroutput;
}

@override
public int outputdc3v() {
int adapteroutput=adapterinput/73;
system.out.println("使用poweradapter输入ac:"+adapterinput+"v"+"输出dc:"+adapteroutput+"v");
return adapteroutput;
}
}

uml类图
结合JDK源码看设计模式——适配器模式

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

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

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.xmljavatypeadapter;
import java.util.date;

@xmltype(proporder={"id","name","birthday"}) //指定序列成的xml节点顺序
@xmlaccessortype(value=xmlaccesstype.field) //访问类型改为字段
@xmlrootelement
public class student {
@xmlelement
private string id;
@xmlelement
private string name;
@xmljavatypeadapter(value=dateadapter.class)
@xmlelement
private date birthday;

public string getid() {
return id;
}

public void setid(string id) {
this.id = id;
}

public string getname() {
return name;
}

public void setname(string name) {
this.name = name;
}

public date getbirthday() {
return birthday;
}

public void setbirthday(date birthday) {
this.birthday = birthday;
}

@override
public string tostring() {
return "student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", birthday=" + birthday +
'}';
}
}

date适配器

import java.text.simpledateformat;
import java.util.date;

import javax.xml.bind.annotation.adapters.xmladapter;

public class dateadapter extends xmladapter<string, date> {
//反序列化成日期对象date
@override
public date unmarshal(string str) throws exception {
simpledateformat format = getsimpledateformat("yyyy-mm hh:mm:ss");
return str==null ? null:format.parse(str);
}
//序列化成xml
@override
public string marshal(date date) throws exception {
simpledateformat format = getsimpledateformat("yyyy-mm hh:mm:ss");
return date==null ? "":format.format(date);
}
private simpledateformat getsimpledateformat(string pattern){
simpledateformat format = new simpledateformat(pattern);
return format;
}
}

编写测试类

import javax.xml.bind.jaxbcontext;

import javax.xml.bind.marshaller;
import javax.xml.bind.unmarshaller;
import java.io.*;
import java.util.date;

public class test {

public static void main(string[] args) {
dateadapter da=new dateadapter();
student stu = new student();
stu.setid("1");
stu.setname("方块人");
stu.setbirthday(new date());
try {
jaxbcontext context = jaxbcontext.newinstance(student.class);
marshaller marshaller = context.createmarshaller();
marshaller.setproperty(marshaller.jaxb_formatted_output, true);
marshaller.setproperty(marshaller.jaxb_encoding, "utf-8");
//marshaller.marshal(stu, system.out);
stringwriter writer = new stringwriter();
marshaller.marshal(stu, writer);
system.out.println(writer.tostring());
//反序列化
unmarshaller unmarshaller = context.createunmarshaller();
stringreader reader = new stringreader(writer.tostring());
student stu2 = (student) unmarshaller.unmarshal(reader);

} catch (exception e) {
e.getmessage();
e.printstacktrace();
}
}
}

输出结果:
结合JDK源码看设计模式——适配器模式

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

结合JDK源码看设计模式——适配器模式

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

xmladapter源码:

public abstract class xmladapter<valuetype,boundtype> {

/**
* do-nothing constructor for the derived classes.
*/
protected xmladapter() {}

/**
* convert a value type to a bound type.
*
* @param v
* the value to be converted. can be null.
* @throws exception
* if there's an error during the conversion. the caller is responsible for
* reporting the error to the user through {@link javax.xml.bind.validationeventhandler}.
*/
public abstract boundtype unmarshal(valuetype v) throws exception;

/**
* convert a bound type to a value type.
*
* @param v
* the value to be convereted. can be null.
* @throws exception
* if there's an error during the conversion. the caller is responsible for
* reporting the error to the user through {@link javax.xml.bind.validationeventhandler}.
*/
public abstract valuetype marshal(boundtype v) throws exception;
}

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

总结:

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