在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
介绍
适配器模式就像旅行插座转换器,Type-c转VGA转接口一样,将原本不兼容的东西转换适配从而可以一起工作。
手机想要投影到投影仪上,由于手机是Type-c接口,投影仪是VGA接口。不能直接投影,需要一个适配器,将视频信号从Type-c口转到VGA口,最后才能输出到大屏幕上。
适配器模式结构图
如上图所示,Client不能直接访问Adaptee。Adapter是适配器,它将Adaptee转换成Client能访问的接口。所以通过适配器Adapter,用户端就可以访问Adaptee。
使用场景
想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。
样例代码实现
有一个适配器,一号口是typec口,二号口是vga口,只有将视频信号从typec口输入,转换输出到vga口,才能和投影仪对接,实现手机屏幕投影到投影仪上的任务。
涉及的物品有:手机、适配器、投影仪。
定义一个手机,它有个typec口,这是视频源。
package com.icefire.adapter;
/**
* @author icefire
* 定义一个手机Phone,它有一个Typec接口。
*
*/
public class Phone {
public void typecPhone() {
System.out.println("信息从Typec口的手机输出。");
}
}
定义一个vga接口
package com.icefire.adapter;
/**
* @author icefire
* 定义一个VGA接口。
*
*/
public interface Vga {
void vgaInterface();
}
实现一个适配器
适配器实现方式分三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
类的适配器模式:通过继承特性来实现适配器功能。
package com.icefire.adapter;
/**
*
* 实现一个Type-c转VGA适配器,
* 适配器实现方式有三种,这是第一种实现方式。
* @author icefire
*
*/
public class Typec2Vga1 extends Phone implements Vga{
@Override
public void vgaInterface() {
// TODO Auto-generated method stub
typecPhone();
System.out.println("接收到Type-c口信息,信息转换成VGA接口中...");
System.out.println("信息已转换成VGA接口,显示屏可以对接。");
}
}
对象的适配器模式:通过组合方式来实现适配器功能。
package com.icefire.adapter;
/**
*
* 实现一个Type-c转VGA适配器,
* 适配器实现方式有三种,这是第二种实现方式。
* @author icefire
*
*/
public class Typec2Vga2 implements Vga{
private Phone phone;
public Typec2Vga2(Phone phone) {
// TODO Auto-generated constructor stub
this.phone = phone;
}
@Override
public void vgaInterface() {
// TODO Auto-generated method stub
if(phone != null) {
phone.typecPhone();
System.out.println("接收到Type-c口信息,信息转换成VGA接口中...");
System.out.println("信息已转换成VGA接口,显示屏可以对接。");
}
}
}
接口的适配器模式:借助抽象类来实现适配器功能。
定义三个接口
package com.icefire.adapter;
/**
* 定义接口
* @author icefire
*
*/
public interface Target {
void typec();
void typec2vga();
void typec2hdmi();
}
定义一个抽象类
package com.icefire.adapter;
/**
* 定义一个抽象类
* @author icefire
*
*/
public abstract class Adapter implements Target{
public void typec() { }
public void typec2vga() { }
public void typec2hdmi() { }
}
实现一个VGA适配器
package com.icefire.adapter;
/**
*
* 实现一个VGA适配器,同理还可以实现一个HDMI适配器
* 适配器实现方式有三种,这是第三种实现方式。
* @author icefire
*
*/
public class VgaAdapter extends Adapter{
public void typec() {
System.out.println("信息从Typec口的手机输出。");
}
public void typec2vga() {
System.out.println("接收到Type-c口信息,信息转换成VGA接口中...");
System.out.println("信息已转换成VGA接口,显示屏可以对接。");
}
}
定义一个显示屏,用来测试上面实现的三个适配器
package com.icefire.adapter;
/**
* 定义一个显示屏
* 与适配器对接
* @author icefire
*
*/
public class Screen {
public static void main(String[] args) {
//第一种适配器用法
System.out.println("-------------第一种适配器------------");
Vga vga = new Typec2Vga1();
vga.vgaInterface();//适配器将typec转换成vga
System.out.println("显示屏对接适配器,手机成功投影到显示屏!");
//第二种适配器用法
System.out.println("-------------第二种适配器------------");
Typec2Vga2 typec2Vga1 = new Typec2Vga2(new Phone());
typec2Vga1.vgaInterface();//适配器将typec转换成vga
System.out.println("显示屏对接适配器,手机成功投影到显示屏!");
//第三种适配器用法
System.out.println("-------------第三种适配器------------");
VgaAdapter vgaAdapter = new VgaAdapter();
vgaAdapter.typec();
vgaAdapter.typec2vga();//适配器将typec转换成vga
System.out.println("显示屏对接适配器,手机成功投影到显示屏!");
}
}
输出结果展示
Connected to the target VM, address: '127.0.0.1:63984', transport: 'socket'
---------------第一种适配器---------------
信息从Typec口的手机输出。
接收到Type-c口信息,信息转换成VGA接口中...
信息已转换成VGA接口,显示屏可以对接。
显示屏对接,手机成功投影到显示屏!
---------------第二种适配器---------------
信息从Typec口的手机输出。
接收到Type-c口信息,信息转换成VGA接口中...
信息已转换成VGA接口,显示屏可以对接。
显示屏对接,手机成功投影到显尿屏!
---------------第三种适配器---------------
信息从Typec口的手机输出。
接收到Type-c口信息,信息转换成VGA接口中...
信息已转换成VGA接口,显示屏可以对接。
显示屏对接,手机成功投影到显示屏!
Disconnected from the target VM, address: '127.0.0.1:63984', transport: 'socket'
总结
适配器模式在源码中的应用:
- JDK源码的IO模块用到,例如 java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream)。
- mybatis源码日志模块用到对象适配器模式。
适配器模式将一个接口转为另外一个接口。它有三种实现方式:
- 当希望将一个类转换为满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
- 当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Typec2Vga2 类,持有原类的一个实例,在Typec2Vga2 类的方法中,调用实例的方法就行。
- 当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现所有方法,我们写别的类的时候,继承抽象类即可。