Logo
Section 1 / 7

适配器模式

适配器模式类似于电源适配器,它能让您的笔记本电脑在美国和英国都能正常工作,即使两地的电压不同。

导入

适配器模式

适配器模式类似于电源适配器,它能让您的笔记本电脑在美国和英国都能正常工作,即使两地的电压不同。

什么是适配器模式?

当两位国家元首使用不同语言会面时,通常会有一位语言翻译坐在中间,将对话内容进行实时翻译,从而实现沟通。适配器模式与此类似:它位于两个原本无法协同工作的不兼容类之间,使它们能够共同工作。另一个例子是,从美国购买的电子产品在印度使用时,由于两国提供给用户的电源电压不同,需要一个物理适配器来升压或降压以匹配设备需求。软件中的适配器模式正是基于这一思想。

正式定义中,适配器模式指的是 通过将一个类的接口转换为客户所期望的另一个接口,使得原本因接口不兼容而不能一起工作的类可以协同工作。

类图

该类图包含以下实体:

  • 目标接口(Target)
  • 客户端(Client)
  • 被适配者(Adaptee)
  • 适配器(Adapter)
Class Diagram
Class Diagram

示例

我们继续以飞机为例。你的软件原本只处理喷气式飞机,但现在突然需要支持本地的一家热气球公司。完全重写软件并不可行。更复杂的是,热气球公司已经提供了表示热气球的类,但这些类与你用于表示现代飞机的 IAircraft 接口不兼容。我们将使用适配器模式,让热气球类能够与现有的飞机基础设施协同工作。先来看一下热气球类的定义:

1public class HotAirBalloon { 2 3 String gasUsed = "Helium"; 4 5 void fly(String gasUsed) { 6 // 根据燃料类型执行起飞流程 7 // 后续还有更多代码 8 } 9 10 // 返回热气球飞行所用的气体 11 String inflateWithGas() { 12 return gasUsed; 13 } 14}

不幸的是,HotAirBalloon 类的 fly 方法带有参数,无法直接与 IAircraft 接口配合使用。我们需要一个适配器,使 HotAirBalloon 能够适配 IAircraft 接口。按照设计模式的说法,这个适配器应当实现客户端所依赖的接口,也就是 IAircraft 接口。

1public interface IAircraft { 2 void fly(); 3}

适配器的具体实现如下:

1public class Adapter implements IAircraft { 2 3 HotAirBalloon hotAirBalloon; 4 5 public Adapter(HotAirBalloon hotAirBalloon) { 6 this.hotAirBalloon = hotAirBalloon; 7 } 8 9 @Override 10 public void fly() { 11 String fuelUsed = hotAirBalloon.inflateWithGas(); 12 hotAirBalloon.fly(fuelUsed); 13 } 14}

关于适配器需要注意的关键点包括:

  • 适配器通过组合方式持有被适配对象(即本例中的 HotAirBalloon 对象)。

  • 适配器实现了客户端所熟知并使用的接口,在本例中就是 IAircraft 接口。

现在来看客户端代码:

1 public void main() { 2 3 HotAirBalloon hotAirBalloon = new HotAirBalloon(); 4 Adapter hotAirBalloonAdapter = new Adapter(hotAirBalloon); 5 6 hotAirBalloonAdapter.fly(); 7 }

注意,客户端操作的是实现了 IAircraft 接口的对象。它对 HotAirBalloon 类一无所知,所有底层细节都被适配器屏蔽了。现在,客户端可以让热气球“起飞”,即使其 fly() 方法与 IAircraft 接口规定的签名不一致。

对象适配器

刚才讨论的热气球示例实际上就是一个对象适配器的例子。我们通过将适配器与被适配对象进行组合,使原本不兼容的类得以协作。在 Java 中,由于语言本身不支持多重继承,因此通常只能采用对象适配器的方式。

使用对象组合来进行适配带来了组合的一般优势:设计更加灵活,适配器不仅可以替代被适配者,还能替代其任何子类对象。

类适配器

与对象适配器相对的概念是类适配器。类适配器通过多重继承实现,即适配器同时继承客户端所使用的接口和被适配者的类。这种方式通过继承而非组合来完成适配。但由于 Java 不支持多重继承,这种形式在 Java 中无法直接实现。

通过继承实现适配的一个优点是,可以在适配器中重写行为或添加新的功能。

其他示例

  • 如果有两个应用程序,一个输出 XML 数据,另一个只接受 JSON 输入,那么就需要一个适配器来连接二者,使其无缝协作。

  • Enumeration 是 Java 早期的一个只读接口,仅包含 hasMoreElements 和 nextElement 两个方法。后来 Sun 公司推出集合框架时引入了 Iterator 接口,支持元素删除操作。为了兼容旧代码,可以创建一个适配器类在两者之间转换;由于 Enumeration 是只读的,当尝试删除元素时,适配器可抛出运行时异常。

  • 在 Java API 中,java.io.InputStreamReader 和 java.io.OutputStreamWriter 就是适配器模式的典型例子。

创建型模式

桥接模式