结合JDK源码看设计模式——适配器模式
定义:
将一个类的接口转换成客户期望的另外一个接口(重点理解适配的这两个字),使得接口不兼容的类可以一起工作
适用场景:
- 已经存在的类,它的方法和需求不匹配的时候
- 在软件维护阶段考虑的设计模式
详解
首先来从生活中的常见场景来看,一个电源插座输出都是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;
}
有两个方法需要实现,一个是反序列化,另外一个是序列化。相信你看懂了适配器模式之后,也能理解这个类中的方法含义
总结:
适配器模式更多的是提供不同接口给不同的厂家,适配器我们知道怎么实现之后,更多的是在适用场景中去思考。这样就会对适配器模式有更加深刻的理解。
结合JDK源码看设计模式——适配器模式的更多相关文章
- 结合JDK源码看设计模式——桥接模式
前言: 在我们还没学习框架之前,肯定都学过JDBC.百度百科对JDBC是这样介绍的[JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Jav ...
- 结合JDK源码看设计模式——单例模式
定义: 保证一个类仅有一个实例,并提供一个全局访问点 适用场景: 确保任何情况下这个对象只有一个实例 详解: 私有构造器 单利模式中的线程安全+延时加载 序列化和反序列化安全, 防止反射攻击 结合JD ...
- 结合JDK源码看设计模式——原型模式
定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比较 ...
- 结合JDK源码看设计模式——简单工厂、工厂方法、抽象工厂
三种工厂模式的详解: 简单工厂模式: 适用场景:工厂类负责创建的对象较少,客户端只关心传入工厂类的参数,对于如何创建对象的逻辑不关心 缺点:如果要新加产品,就需要修改工厂类的判断逻辑,违背软件设计中的 ...
- 结合JDK源码看设计模式——模板方法模式
前言: 相信很多人都听过一个问题:把大象关进冰箱门,需要几步? 第一,把冰箱门打开:第二,把大象放进去:第三,把冰箱门关上.我们可以看见,这个问题的答案回答的很有步骤.接下来我们介绍一种设计模式--模 ...
- 结合JDK源码看设计模式——迭代器模式
前言: Iterator翻译过来就是迭代器的意思.在前面的工厂模式中就介绍过了iterator,不过当时介绍的是方法,现在从Iterator接口的设计来看,似乎又是一种设计模式,下面我们就来讲讲迭代器 ...
- 结合JDK源码看设计模式——享元模式
前言 在说享元模式之前,你一定见到过这样的面试题 public class Test { public static void main(String[] args) { Integer a=Inte ...
- 结合JDK源码看设计模式——建造者模式
概念: 将一个复杂对象的构建与它的表示分离.使得同样构建过程可以创建不同表示适用场景: 一个对象有很多属性的情况下 想把复杂的对象创建和使用分离 优点: 封装性好,扩展性好 详解: 工厂模式注重把这个 ...
- 结合JDK源码看设计模式——观察者模式
前言: 现在我们生活中已经离不开微信,QQ等交流软件,这对于我们来说不仅是交流,更有在朋友圈中或空间中进行分享自己的生活,同时也可以通过这个渠道知道别人的生活.我们在看朋友圈的时候其实我们扮演的就是一 ...
随机推荐
- 连接到放置本地yum源服务器之前的注意事项
1.确认系统防火墙关闭 2.启动httpd服务 service httpd start 如果提示没有httpd服务: 安装httpd服务 yum install -y httpd 作者:Daley Z ...
- 通过jdbc完成单表的curd操作以及对JDBCUtils的封装
概述:jdbc是oracle公司制定的一套规范(一套接口),驱动是jdbc的实现类,由数据库厂商提供.所以我们可以通过一套规范实现对不同的数据库操作(多态) jdbc的作用:连接数据库,发送sql语句 ...
- HTML5总结
HTML的定义 HyperText Markup Language 超文本标记语言 超级文本标记语言是标准通用标记语言下的一个应用,也是一种规范,一种标准, 它通过标记符号来标记要显示的网页中的各个部 ...
- PAT1009:Product of Polynomials
1009. Product of Polynomials (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yu ...
- 初识函数库libpcap
由于工作上的需要,最近简单学习了抓包函数库libpcap,顺便记下笔记,方便以后查看 一.libpcap简介 libpcap(Packet Capture Library),即数据包捕获函数库, ...
- mybatis设置callSettersOnNulls解决返回字段不全的问题
Spring+MyBatis开发过程中,在xxMapper.xml配置文件进行select查询时resultType="map",如果要查询的字段是空值,在返回的map中会出现找不 ...
- Linux kernel的中断子系统之(九):tasklet
返回目录:<ARM-Linux中断系统>. 总结: 二介绍了tasklet存在的意义. 三介绍了通过tasklet_struct来抽想一个tasklet,每个CPU维护一个tasklet链 ...
- Thymeleaf利用layout.html文件生成页面布局框架
1.layout.html文件 生成布局 <!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www ...
- 关于局域网 手机连接apache服务器报403forbidden错误参考如下2017.6.6
http://www.glbwl.com/wampServer-403-forbidden.html
- 利用pytesser识别图形验证码
简单识别 1.一般思路 验证码识别的一般思路为: 图片降噪 图片切割 图像文本输出 1.1 图片降噪 所谓降噪就是把不需要的信息通通去除,比如背景,干扰线,干扰像素等等,只剩下需要识别的文字,让图片变 ...