定义:
将一个类的接口转换成客户期望的另外一个接口(重点理解适配的这两个字),使得接口不兼容的类可以一起工作
适用场景:
- 已经存在的类,它的方法和需求不匹配的时候
- 在软件维护阶段考虑的设计模式
详解
首先来从生活中的常见场景来看,一个电源插座输出都是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());
- }
- }
输出结果

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

还是和装饰者有区别的,当然你要是通过装饰者来实现上述功能一样能实现
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类图

从继承变成了组合,这就是对象适配
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();
- }
- }
- }
输出结果:

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

这样就不是我们想要的格式
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;
- }
有两个方法需要实现,一个是反序列化,另外一个是序列化。相信你看懂了适配器模式之后,也能理解这个类中的方法含义
总结:
适配器模式更多的是提供不同接口给不同的厂家,适配器我们知道怎么实现之后,更多的是在适用场景中去思考。这样就会对适配器模式有更加深刻的理解。