惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
L
LangChain Blog
GbyAI
GbyAI
F
Fortinet All Blogs
腾讯CDC
Last Week in AI
Last Week in AI
A
About on SuperTechFans
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - Franky
B
Blog
D
Docker
G
Google Developers Blog
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tailwind CSS Blog
宝玉的分享
宝玉的分享
U
Unit 42
Blog — PlanetScale
Blog — PlanetScale
B
Blog RSS Feed

Java技术经验分享

生产故障处理SOP分享 | Java技术经验分享 系统稳定性建设实践总结【转载】 | Java技术经验分享 valine访问leancloud国际版异常,评论失效修复 | Java技术经验分享 匆匆忙忙的2021 | Java技术经验分享 聊一下换工作 | Java技术经验分享 IoT系列(2):WIFI设备常见配网方案介绍 | Java技术经验分享 IoT系列(1):什么是物联网 | Java技术经验分享 Java8特性2 - StreamApi | Java技术经验分享 Java8特性1 - lambda表达式&函数式接口 | Java技术经验分享 设计模式(4)-建造者模式 | Java技术经验分享 设计模式(3)-原型模式与浅拷贝和深拷贝 | Java技术经验分享 设计模式(2)-工厂模式图文介绍 | Java技术经验分享 设计模式(1)-带你了解3类8种单例模式 | Java技术经验分享 Java时间处理5---Java8中时区相关类库介绍 | Java技术经验分享 Java时间处理4---Java8中LocalDate、LocalTime、LocalDateTime介绍 | Java技术经验分享 Java时间处理3---Java8中Instant、Duration、Period、Clock介绍 | Java技术经验分享 一些有意思的问答 | Java技术经验分享 Nacos系列博客说明 | Java技术经验分享 菜鸡程序员的2019年度总结 | Java技术经验分享 Java中“附近的人”实现方案讨论及代码实现 | Java技术经验分享 Java时间处理2----时区TimeZone类方法探究(Java8以前) | Java技术经验分享 Java时间处理1----Date和Calendar方法探究(Java8以前) | Java技术经验分享 FastJson中JSONString、JavaBean、JSONObject、JSONArray的转换关系及API示例 | Java技术经验分享 2019.11软考软件设计师归来心得体会及复习备考指南 | Java技术经验分享 你还没用过“约定式提交”吗?那你赶紧来补补知识吧 | Java技术经验分享 教你如何看懂UML中的类图及类图中的关系 | Java技术经验分享 设计模式总览 | Java技术经验分享 萌新入门Github请看这里,学不会远程教 | Java技术经验分享 Hexo的工作原理探究 | Java技术经验分享 Hexo-theme-butterfly修改调整记录教程 | Java技术经验分享
设计模式(5)-适配器模式 | Java技术经验分享
文章作者: LarsCheng · 2020-10-11 · via Java技术经验分享

定义

适配器模式:将一个类的接口转换为调用方希望的另一个接口,使得原本不兼容的接口变得可兼容共同工作

举一个生活中的例子来解释适配器模式如下:

角色和分类

适配器模式种分为3大角色

  • 目标接口:当前系统业务所期待的接口,抽象类或者接口
  • 适配者类:要被适配的类,原本不兼容的类
  • 适配器类:一个转换器,通过继承或引用适配者对象,把适配者接口转换成目标接口,使得客户按照目标接口的格式访问适配者

适配器模式分为3种:

  • 类适配器模式
  • 对象适配器模式
  • 接口适配器模式

下面分别对他们进行介绍

类适配器模式

手机充电器为例来介绍类适配器模式:充电器将220V交流电转换为5V直流电这一过程。其中的角色如下

  • 输出5v电压:目标接口,兼容手机充电
  • 电源插座:适配者类,要被适配,不适合手机直接充电
  • 手机充电器:适配器类,将220V不可用的电压转换为手机可用的充电电压

简易的类图结构如下:

代码实现

目标接口,定义一个将220转换为5v的接口,作为一个标准,提供给各个厂商的电源适配器使用

1
2
3
public interface IOutput5V {
int output5v();
}

被适配类,电源插座,提供220v的直流电,不能被手机直接使用

1
2
3
4
5
6
7
public class Output220V {
public int output220(){
int src = 220;
System.out.println("电源输出电压:220V");
return src;
}
}

适配器类,继承了被适配类,实现目标接口具体的适配转换逻辑

1
2
3
4
5
6
7
8
9
10
public class Adapter220To5 extends Output220V implements IOutput5V {
@Override
public int output5v() {
int src = output220();

int dts = src / 44;
System.out.println("充电器适配后电压:" + dts);
return dts;
}
}

以上便完成了主要的角色实现,编写手机的充电方法进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Phone {

public void charging(IOutput5V iOutput5V){
if (iOutput5V.output5v()==5){
System.out.println("电压5v,开始充电");
}else {
System.err.println("电压不符,无法充电");
}
}
public static void main(String[] args) {
new Phone().charging(new Adapter220To5());
}
}

运行后输出结果如下:

1
2
3
电源输出电压:220V
充电器适配后电压:5V
电压5v,开始充电

类适配器模式说明

适配器的实现过程中是继承了被适配类同时实现目标接口的方式,这样的原因是受Java单继承的限制,所以在类适配器模式下算是一个小小的缺点,使用继承大大的增加了适配器的复杂度。

对象适配器模式

基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承待适配类,而是持有待适配类的实例,以解决兼容性的问题。 即:持有待适配类,实现目标接口,完成兼容性适配

依然按照上文的场景和角色只有适配器类的变动,类图关系如下

从原本的继承被适配类转变为持有被适配类的实例。涉及到代码修改的只有适配器类:Adapter220To5.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Adapter220To5  implements IOutput5V {
private Output220V output220;

public Adapter220To5 setOutput220(Output220V output220) {
this.output220 = output220;
return this;
}

@Override
public int output5v() {
int src = output220.output220();

int dts = src / 44;
System.out.println("充电器适配后电压:" + dts);
return dts;
}
}

适配器类在需要持有被适配类的实例,所以在Phone充电方法中传入被适配类的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Phone {

public void charging(IOutput5V iOutput5V){
if (iOutput5V.output5v()==5){
System.out.println("电压5v,开始充电");
}else {
System.err.println("电压不符,无法充电");
}
}
public static void main(String[] args) {

new Phone().charging(new Adapter220To5().setOutput220(new Output220V()));
}
}

对象适配器模式说明

  • 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。
  • 根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承被适配类的局限性问题,也不再要求目标接口角色必须是接口。

接口适配器模式

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求

接口适配器模式适用于一个接口不想使用其所有的方法的情况

简单的类图结构如下:

代码实现

假定现在的目标接口定义了输出5v、输出20v、输出60v的方法

1
2
3
4
5
6
7
8
public interface IOutput {

int output5();

int output20();

int output60();
}

定义抽象类默认实现目标接口,并持有待适配的对象实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class AbsAdapter implements IOutput{
protected Output220V output220;

public AbsAdapter(Output220V output220V) {
this.output220 = output220V;
}

@Override
public int output5() {
return 0;
}

@Override
public int output20() {
return 0;
}

@Override
public int output60() {
return 0;
}
}

当需要使用到输出5v的转换时,或者需要使用输出10v转换时,使用匿名内部类的方式实现内部适配细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Phone {

public void charging(IOutput iOutput){
if (iOutput.output5()==5){
System.out.println("电压5v,开始充电");
}else {
System.err.println("电压不符,无法充电");
}
}
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter(new Output220V()) {
@Override
public int output5() {
int src = output220.output220();

int dts = src / 44;
System.out.println("充电器适配后电压:" + dts);
return dts;
}
};
new Phone().charging(absAdapter);
}
}

输出结果:

1
2
3
电源输出电压:220V
充电器适配后电压:5
电压5v,开始充电

适配器模式总结

主要优点:

  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
  • 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
  • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

适用场景:

  • 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
  • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

适配器模式在源码中的使用