1 初步认识
适配器模式的定义
将一个类的接口转成客户期望的另外一个接口。适配器模式使得原本由于接口不匹配而不能一起工作的那些类可以一起工作。
大白话
适配器模式就像旅行插座转换器(图1)
2 适配器模式结构图
如图所示,Client不能直接访问Adaptee。Adapter是适配器,它将Adaptee转换成Client能访问的接口。所以通过适配器Adapter,用户端就可以访问Adaptee。
3 使用场景例子
手机想要投影到投影仪上,由于手机是Type-c接口,投影仪是VGA接口。不能直接投影,需要一个适配器,将视频信号从Type-c口转到VGA口,最后才能输出到大屏幕上。
4 适配器模式在使用场景的具体实现
如图所示,这有一个适配器,一号口是typec口,二号口是vga口,只有将视频信号从typec口输入,转换输出到vga口,才能和投影仪对接,实现手机屏幕投影到投影仪上的任务。涉及的物品有:手机、适配器、投影仪。
4.1 定义一个手机,它有个typec口,这是视频源。
package com.example.designmode.adapter;
/**
* <h3>design-mode</h3>
* <p>定义一个手机Phone,它有一个Typec接口。</p>
*
* @author : ZhangYuJie
* @date : 2022-02-13 17:43
**/
public class Phone {
public void typecPhone() {
System.out.println("信息从Typec口的手机输出。");
}
}
4.2 定义一个vga接口
package com.example.designmode.adapter;
/**
* <h3>design-mode</h3>
* <p>定义一个VGA接口。</p>
*
* @author : ZhangYuJie
* @date : 2022-02-13 17:44
**/
public interface Vga {
void vgaInterface();
}
实现一个适配器,适配器实现方式分三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
4.3.1 类的适配器模式
原理:通过继承特性来实现适配器功能。
package com.example.designmode.adapter;
/**
* <h3>design-mode</h3>
* <p>实现一个Type-c转VGA适配器, *适配器实现方式有三种,这是第一种实现方式。</p>
*
* @author : ZhangYuJie
* @date : 2022-02-13 17:44
**/
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接口,显示屏可以对接。");
}
}
4.3.2 对象的适配器模式
原理:通过组合方式来实现适配器功能。
package com.example.designmode.adapter;
/**
* <h3>design-mode</h3>
* <p>实现一个Type-c转VGA适配器, * 适配器实现方式有三种,这是第二种实现方式。</p>
*
* @author : ZhangYuJie
* @date : 2022-02-13 17:47
**/
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接口,显示屏可以对接。");
}
}
}
4.3.3 接口的适配器模式
原理:借助抽象类来实现适配器功能。
定义三个接口
package com.example.designmode.adapter;
/**
* <h3>design-mode</h3>
* <p> 定义接口</p>
*
* @author : ZhangYuJie
* @date : 2022-02-13 17:48
**/
public interface Target {
void typec();
void typec2vga();
void typec2hdmi();
}
定义一个抽象类
package com.example.designmode.adapter;
/**
* <h3>design-mode</h3>
* <p>定义一个抽象类</p>
*
* @author : ZhangYuJie
* @date : 2022-02-13 17:48
**/
public abstract class Adapter implements Target {
public void typec() {
}
public void typec2vga() {
}
public void typec2hdmi() {
}
}
实现一个VGA适配器
package com.example.designmode.adapter;
/**
* <h3>design-mode</h3>
* <p> 实现一个VGA适配器,同理还可以实现一个HDMI适配器 * 适配器实现方式有三种,这是第三种实现方式。</p>
*
* @author : ZhangYuJie
* @date : 2022-02-13 17:49
**/
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接口,显示屏可以对接。");
}
}
4.4 定义一个显示屏,用来测试上面实现的三个适配器
package com.example.designmode.adapter;
/**
* <h3>design-mode</h3>
* <p>定义一个显示屏
* * 与适配器对接</p>
*
* @author : ZhangYuJie
* @date : 2022-02-13 17:50
**/
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("显示屏对接适配器,手机成功投影到显示屏!");
}
}
4.5 测试结果
"C:\Program Files\Java\jdk-11.0.11\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2020.1\lib\idea_rt.jar=54679:D:\IntelliJ IDEA 2020.1\bin" -Dfile.encoding=UTF-8 -classpath D:\workspace2021\design-mode\target\classes;D:\Repository\org\springframework\boot\spring-boot-starter-web\2.6.2\spring-boot-starter-web-2.6.2.jar;D:\Repository\org\springframework\boot\spring-boot-starter\2.6.2\spring-boot-starter-2.6.2.jar;D:\Repository\org\springframework\boot\spring-boot-starter-logging\2.6.2\spring-boot-starter-logging-2.6.2.jar;D:\Repository\ch\qos\logback\logback-classic\1.2.9\logback-classic-1.2.9.jar;D:\Repository\ch\qos\logback\logback-core\1.2.9\logback-core-1.2.9.jar;D:\Repository\org\apache\logging\log4j\log4j-to-slf4j\2.17.0\log4j-to-slf4j-2.17.0.jar;D:\Repository\org\apache\logging\log4j\log4j-api\2.17.0\log4j-api-2.17.0.jar;D:\Repository\org\slf4j\jul-to-slf4j\1.7.32\jul-to-slf4j-1.7.32.jar;D:\Repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Repository\org\yaml\snakeyaml\1.29\snakeyaml-1.29.jar;D:\Repository\org\springframework\boot\spring-boot-starter-json\2.6.2\spring-boot-starter-json-2.6.2.jar;D:\Repository\com\fasterxml\jackson\core\jackson-databind\2.13.1\jackson-databind-2.13.1.jar;D:\Repository\com\fasterxml\jackson\core\jackson-annotations\2.13.1\jackson-annotations-2.13.1.jar;D:\Repository\com\fasterxml\jackson\core\jackson-core\2.13.1\jackson-core-2.13.1.jar;D:\Repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.1\jackson-datatype-jdk8-2.13.1.jar;D:\Repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.1\jackson-datatype-jsr310-2.13.1.jar;D:\Repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.1\jackson-module-parameter-names-2.13.1.jar;D:\Repository\org\springframework\boot\spring-boot-starter-tomcat\2.6.2\spring-boot-starter-tomcat-2.6.2.jar;D:\Repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.56\tomcat-embed-core-9.0.56.jar;D:\Repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.56\tomcat-embed-el-9.0.56.jar;D:\Repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.56\tomcat-embed-websocket-9.0.56.jar;D:\Repository\org\springframework\spring-web\5.3.14\spring-web-5.3.14.jar;D:\Repository\org\springframework\spring-beans\5.3.14\spring-beans-5.3.14.jar;D:\Repository\org\springframework\spring-webmvc\5.3.14\spring-webmvc-5.3.14.jar;D:\Repository\org\springframework\spring-aop\5.3.14\spring-aop-5.3.14.jar;D:\Repository\org\springframework\spring-context\5.3.14\spring-context-5.3.14.jar;D:\Repository\org\springframework\spring-expression\5.3.14\spring-expression-5.3.14.jar;D:\Repository\org\springframework\boot\spring-boot-devtools\2.6.2\spring-boot-devtools-2.6.2.jar;D:\Repository\org\springframework\boot\spring-boot\2.6.2\spring-boot-2.6.2.jar;D:\Repository\org\springframework\boot\spring-boot-autoconfigure\2.6.2\spring-boot-autoconfigure-2.6.2.jar;D:\Repository\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;D:\Repository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Repository\org\springframework\spring-core\5.3.14\spring-core-5.3.14.jar;D:\Repository\org\springframework\spring-jcl\5.3.14\spring-jcl-5.3.14.jar com.example.designmode.adapter.Screen
-------------第一种适配器------------
信息从Typec口的手机输出。
接收到Type-c口信息,信息转换成VGA接口中...
信息已转换成VGA接口,显示屏可以对接。
显示屏对接适配器,手机成功投影到显示屏!
-------------第二种适配器------------
信息从Typec口的手机输出。
接收到Type-c口信息,信息转换成VGA接口中...
信息已转换成VGA接口,显示屏可以对接。
显示屏对接适配器,手机成功投影到显示屏!
-------------第三种适配器------------
信息从Typec口的手机输出。
接收到Type-c口信息,信息转换成VGA接口中...
信息已转换成VGA接口,显示屏可以对接。
显示屏对接适配器,手机成功投影到显示屏!
进程已结束,退出代码为 0
5 小结
5.1 适配器模式在源码中的应用:
(1)JDK源码的IO模块用到,例如 java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream)。
(2)mybatis源码日志模块用到对象适配器模式。
5.1 适配器模式将一个接口转为另外一个接口。它有三种实现方式:
(1)当希望将一个类转换为满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可,例如4.3.1。
(2) 当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Typec2Vga2 类,持有原类的一个实例,在Typec2Vga2 类的方法中,调用实例的方法就行,例如4.3.2对象的适配器模式。
(3)当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现所有方法,我们写别的类的时候,继承抽象类即可,例如4.3.3接口的适配器模式。