引言

由于无法直接使用某个类中的方法而采取的一种中间类转换的策略。将一个类的接口转换成另一个接口,让原本接口不兼容的类可以兼容。

适配器模式可以分为三种:类适配器、对象适配器、接口适配器。它们之间的区别主要体现在适配器角色与被适配角色之间的依赖关系上。如类适配器是通过继承的方式,令适配器继承被适配类。

我们可以将适配器理解为两个不兼容的接口之间的桥梁。这是一种结构型模式。

虽然解决了老接口与新代码之间的兼容问题,但是适配器模式也存在不容忽视的缺点。过多的使用适配器会让系统变得凌乱,不易整体把握,因此,如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

注意,适配器不是在详细设计时添加的,而是解决正在服役的项目的兼容问题时添加的,而且不可以大量使用

一、类适配器

首先描述一下具体场景。

以电压适配为例。假设目前有一个用于输出电压的接口 Voltage,提供一个 output() 输出电压方法,且有一个可以输出220V标准家庭电压的实现类 Voltage220V。目前的系统中有一些家用电器的类,如TV(电视机类)、Fridge(电冰箱类)等,它们有一个charge() 方法,需要依赖 Voltage.output() 提供的电压为其充电。

Voltage220V的output()方法实现了 220V电压的输出功能,所以一切安好,但是今天我们买了一部手机,需要 5V的电压为其充电,但是目前系统中只有220V电压的输出,这时我们该如何解决呢?如下的类图所示:

此时我们最常想到的是再新建一个 Voltage 的实现类,可以叫 Voltage5V,然后实现其output() 方法。如下所示:

这种方法是一种解决思路,这并没有什么问题,实际上,在真正的生产开发中,也有很多是以这种方式解决的。当然,如果真的能够以这种方式解决问题是最好的。

但是,如果系统中 220V 电压是一个标准电压,我们无法新加一个与之等价的类,或者必须要基于已有的实现来解决的话,那么适配器模式就派上了用场,这也是适配器模式的一个主要特点,就是对已有方法的复用。于是,就有了下面这种适配器模式的类图:

代码实现如下:

/*** 电压接口*/
public interface Voltage {/** 电压输出*/int output();
}
/*** 220V电压实现*/
public class Voltage220V implements Voltage {private static final int voltage = 220;@Overridepublic int output() {System.out.println("输出" + voltage + "电压");return voltage;}
}
/*** 220V电压适配器*/
public class Voltage220VAdapter extends Voltage220V implements Voltage {@Overridepublic int output() {int superVoltage = super.output();System.out.println("父类输出电压:" + superVoltage + "V");int adaptedVoltage = superVoltage / 44;System.out.println("已将" + superVoltage + "V电压为" + adaptedVoltage + "V。");return adaptedVoltage;}
}
/*** 手机类*/
public class Phone {/*** 充电需要电压:5V*/public void charge(Voltage voltage) {if (voltage.output() == 5) {System.out.println("电压为5V,正在充电~");} else {System.out.println("电压异常,手机未充电!");}}
}

以上代码是适配器中几个关键角色,其中Voltage220V代表被适配类,Voltage220VAdapter 就代表220V电压的适配器类。以下是测试代码:

public class Test {public static void main(String[] args) {Phone phone = new Phone();phone.charge(new Voltage220VAdapter());}
}
// 输出:
输出220电压
父类输出电压:220V
已将220V电压为5V。
电压为5V,正在充电~

二、对象适配器

对象适配器与类适配器的区别是与被适配器类的关系,由泛化关系(继承)变为依赖关系(组合或聚合)。如下图所示:

可以看到,适配器类Voltage220VAdapter以组合的方式将Voltage220V的一个对象引用到其内部,这在一定程度上避免了因为继承关系带来的较强的耦合度。同时,该适配器类可以更加灵活,去适配更多的电压,如1000V等等。代码如下:

/*** 220V电压适配器*/
public class Voltage220VAdapter implements Voltage {private Voltage220V v220;public Voltage220VAdapter(Voltage220V v220) {this.v220 = v220;}@Overridepublic int output() {int srcVoltage = v220.output();System.out.println("原输出电压:" + srcVoltage + "V");int adaptedVoltage = srcVoltage / 44;System.out.println("已将" + srcVoltage + "V电压为" + adaptedVoltage + "V。");return adaptedVoltage;}
}

对象适配器与类适配器唯一的不同仅仅是对被适配类的依赖关系,由继承变为了组合或聚合,因此只有适配器本身稍有变化,去掉了继承关系,增加了一个 v220 字段。其他的接口都没什么变化。在测试代码中,我们需要为适配器类的构造器传入一个被适配的对象,如下所示:

public class Test {public static void main(String[] args) {Phone phone = new Phone();phone.charge(new Voltage220VAdapter(new Voltage220V()));}
}
// 输出:
输出220电压
原输出电压:220V
已将220V电压为5V。
电压为5V,正在充电~

三、接口适配器

接口适配器模式,也叫缺省适配器模式。

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

接口适配器其实挺好理解的,在很多类库框架中也会用到,经常会看到一些方法仅仅是一个空方法,其实就用到这种接口适配器模式。相比于类适配器或对象适配器,这种接口适配器并不是为了实现某种兼容采用的解决办法,而更多的是为了保证各个子类的功能保持“单一职责原则”,避免将接口中不需要的方法暴露给外部调用者。

Java常用设计模式————适配器模式相关推荐

  1. Java常用设计模式————原型模式(一)

    介绍 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 原型模式用于创建重复的对象,同时又能保证性能.当直接创建对象的代价比较大时,则采用 ...

  2. 初学Java常用设计模式之——原型模式

    声明:转载请附上原文链接 提示:标题序号从3开始,是照应不同设计模式笔记发布的顺序而定的,比如,第上一篇文章 初学Java常用设计模式之--工厂模式 序号从2开始. 标题后面之所以加上了解,是因为相对 ...

  3. java常用设计模式文章收集

    java常用设计模式文章收集 java设计模式--接口模式 java设计模式--抽象工厂模式 java设计模式--工厂模式 Java设计模式--迭代器模式 Java设计模式--原型模式 JAVA设计模 ...

  4. 初学Java常用设计模式之——工厂模式

    声明:转载请附上原文链接 提示:标题序号从2开始,是照应不同设计模式笔记发布的顺序而定的,比如,第上一篇文章 初学Java常用设计模式之--单例模式 序号从1开始 2. 工厂模式(常用) ⼯⼚模式介绍 ...

  5. 初学Java常用设计模式之——装饰器模式

    声明:转载请附上原文链接 提示:标题序号从8开始,是照应不同设计模式笔记发布的顺序而定的,比如,上一篇文章 初学Java常用设计模式之--桥接模式和组合模式 序号从7开始. 8. 装饰器设计模式(重点 ...

  6. Java代码审计-设计模式-适配器模式

    Java设计模式-适配器模式(Adapter Pattern) 目录 什么是适配器模式 适配器模式的3种类型 JavaSE适配器模式的应用 Struts2适配器模式的应用 适配器模式是一种" ...

  7. java常用设计模式11—适配器模式

    适配器模式定义: 把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作. 适配器的使用场景: 1.系统需要使用现有的类,而此类的接口不符合系统的需 ...

  8. java常用设计模式 看了她,改变你的一生~~~

    设计模式: 一个程序员对设计模式的理解: "不懂"为什么要把很简单的东西搞得那么复杂.后来随着软件开发经验的增加才开始明白我所看到的"复杂"恰恰就是设计模式的精 ...

  9. java常用设计模式应用案例

    设计模式: 一个程序员对设计模式的理解: "不懂"为什么要把 很简单的东西搞得那么复杂.后来随着软件开发经验的增加才开始明白我所看到的"复杂"恰恰就是设计模式的 ...

最新文章

  1. 【蓝桥杯】【入门题】【算法提高VIP】1481:剪刀石头布
  2. 【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile 构造函数及相关调用函数 | Android 源码中查找 native 函数 )
  3. python—迭代器
  4. 小店怎么做内容营销?这个家居店铺有诀窍
  5. 防止SQL SERVER的事件探查器跟踪软件
  6. leetcood学习笔记-226- 翻转二叉树
  7. Linux开发相关书籍
  8. 【API进阶之路】研发需求突增3倍,测试团队集体闹离职
  9. 中国剩余定理-模版(互质版)
  10. 天天早上慢跑一小时对身体好吗?
  11. 别怕,“卷积“其实很简单
  12. TechEd2011分享
  13. 计算机科学现代教育,现代教育技术教程-计算机科学教育.pdf
  14. extern 的使用方法介绍
  15. mldn andoird
  16. 【NetFlow】NetFlow V9协议详细分析
  17. ClickHouse原理及使用
  18. [笔记]_ELVE_正则表达式
  19. mysql字符集和校对规则(character sets and collations)详解
  20. 儿童视力档案小程序开发,视力“云管家”

热门文章

  1. C ++中带有示例的llabs()函数
  2. 将本地项目上传到码云(gitee)远程仓库
  3. Xshell连接远程Linux服务器失败Could not connect to ‘192.xxx.xx.xxx‘ (port 22): Connection failed.
  4. wav文件头损坏_Dex文件结构学习
  5. build 之前执行task_浅谈VS编译自定义编译任务—MSBuild Task(csproject)-阿里云开发者社区...
  6. 初学者选黑卡还是微单_3500以内的微单相机好用吗?值得初学者入手吗?
  7. 两种参数类型_布尔参数这些缺点不能忍?不如试试枚举吧
  8. wpf mysql存储过程_MySQL存储过程的创建及调用
  9. Win11蓝屏100%不重启解决方法
  10. Win11新特性:在平板锁屏界面还有酷炫的3D视差效果