适配器模式
定义:
《设计模式》一书中是这样给适配器模式定义的:将一个类的接口转换成客户希望的另外 一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 适配器模式将一个类的接口,转化成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。
生活实例:
- 充电器,美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。
- 笔记本电脑接交流电源时需要一个电源适配器
- 计算机访问照相机的 SD 内存卡时需要一个读卡器等。
- 在 LINUX 上运行 WINDOWS 程序。
组成:
分类:
在《设计模式》一书中将适配器模式分为类适配器模式和对象适配器模式。区别仅在于适
配器角色对于被适配角色的适配是通过继承完成的还是通过组合来完成的。
案例 1
需求:
- 笔记本想连接投影仪,但是笔记本自身的接口是HDMI,投影仪的接口是VGA的
- 为解决这个问题,我们需要一个适配器,把VGA的接口转为HDMI,通过这个适配器就可以实现笔记本电脑和投影仪连接了
类适配器模式:
- 投影仪 Projector
//投影仪
public class Projector {
public void dataLine() {
System.out.println("投影仪使用VGA接口的数据线----播放中-----");
}
}
- HDMI接口
//HDMI接口
public interface HDMIPort {
void useHDMIPort();
}
- 数据转换器 DataConversionLine
//数据转换器
public class DataConversionLine {
public void vgaToHdmi() {
System.out.println("将VGA数据传输转为HDMI");
}
}
- 笔记本电脑 LaptopComputer
类适配器:-----继承完成
public class LaptopComputer extends DataConversionLine implements HDMIPort{
@Override
public void useHDMIPort() {
System.out.println("手提电脑使用的HDMI接口----工作中-----");
}
}
- 测试 Test01
public class Test01 {
public static void main(String[] args) {
//投影仪
Projector projector = new Projector();
projector.dataLine();
//手提电脑
LaptopComputer laptopComputer = new LaptopComputer();
laptopComputer.vgaToHdmi();
laptopComputer.useHDMIPort();
}
}
对象适配器模式:
- 笔记本电脑 LaptopComputer02
类适配器:-----组合完成
public class LaptopComputer02 implements HDMIPort{
private DataConversionLine dataConversionLine;
public LaptopComputer02() {
super();
// TODO Auto-generated constructor stub
}
public LaptopComputer02(DataConversionLine dataConversionLine) {
super();
this.dataConversionLine = dataConversionLine;
}
@Override
public void useHDMIPort() {
System.out.println("手提电脑使用的HDMI接口----工作中-----");
}
public DataConversionLine getDataConversionLine() {
return dataConversionLine;
}
public void setDataConversionLine(DataConversionLine dataConversionLine) {
this.dataConversionLine = dataConversionLine;
}
}
- 测试 Test02
public class Test02 {
public static void main(String[] args) {
//投影仪
Projector projector = new Projector();
projector.dataLine();
//转化器
DataConversionLine dataConversionLine = new DataConversionLine();
//手提电脑
LaptopComputer02 laptopComputer = new LaptopComputer02(dataConversionLine);
laptopComputer.getDataConversionLine().vgaToHdmi();
laptopComputer.useHDMIPort();
}
}
源码中的应用
1. IO 转换流:
缓冲流指定编码格式
public class test01 {
public static void main(String[] args) throws IOException {
String srcPath = "D:\\JavaDevelop\\idea_workplace\\iodemo\\demo.txt";
String diePath = "D:\\JavaDevelop\\idea_workplace\\iodemo\\demo02.txt";
String encoding = "utf-8";
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(srcPath),encoding));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(diePath),encoding));
String data;
while ((data=bufferedReader.readLine())!=null){
bufferedWriter.write(data);
bufferedWriter.newLine();
}
bufferedReader.close();
bufferedWriter.close();
}
}
2. SqlServer odbc
缺省适配模式
在 java 中有一种叫做“缺省适配模式”的应用,它和我们所讲的适配器模式是完全的两种东
西。缺省适配模式是为一个接口提供缺省的实现,这样子类型就可以从缺省适配模式中进行扩展,避免了从原有接口中扩展时要实现一些自己不关心的接口。
- 有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了缺省适配模式,(接口的适配器模式),借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系
3. java.awt.event 中的XXXAdapter
public class Test03 {
public static void main(String[] args) {
Frame f = new Frame();
f.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
});
}
}
WindowAdapter 适配器
public class Test03 {
public static void main(String[] args) {
Frame f = new Frame();
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
super.windowClosed(e);
}
});
}
}
抽象类 WindowAdapter 继承了WindowListener接口中的所有方法
我们 构建WindowAdapter时,只需要重写自己需要的方法就行
4.Spring源码中适配器模式体现
- 在Spring的Aop中,使用Advice(通知)来增强被代理类的功能,Advice的类型有:BeforeAdvice、AfterReturningAdvice、ThreowSadvice。
- 每种Advice都有对应的拦截器,MethodBeforeAdviceInterceptor
AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。 - 各种不同类型的Interceptor,通过适配器统一对外提供接口,如下类图所示:client —> target —>
adapter —> interceptor —> advice。最终调用不同的advice来实现被代理类的增强
总结:
使用场景:
- 有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
- 系统需要复用现有类,而该类的接口不符合系统的需求,可以使用适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
- 多个组件功能类似,但接口不统一且可能会经常切换时,可使用适配器模式,使得客户端可以以统一的接口使用它们
优点:
- 可以让任何两个没有关联的类一起运行。灵活性好。
- 提高了类的复用。
- 增加了类的透明度。
- 目标类和适配器类解耦,提高程序的扩展性。
缺点:
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。增加系统的复杂性。
- 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
- 对类适配器来说,更换适配器的实现过程比较复杂。
注意事项:
- 适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
- 建议尽量使用对象的适配器模式,多用合成/聚合、少用继承。