适配器模式¶
分类:结构型模式。对应《Head First 设计模式》第 7 章;原来混在命令模式页面中的适配器摘记整理于此。
类结构¶
classDiagram
direction TB
class Client
class Target {
<<interface>>
+request()
}
class Adapter {
+request()
}
class Adaptee {
+specificRequest()
}
Target <|.. Adapter
Client ..> Target : 使用
Adapter --> Adaptee : 适配
角色定义¶
| 角色 | 定义 |
|---|---|
| Client(客户端) | 依赖目标接口发起请求,无需了解被适配者的接口。 |
| Target(目标接口) | 定义客户端期望的操作及其行为约定。 |
| Adapter(对象适配器) | 实现目标接口,持有被适配者,将请求转换后委托给它。 |
| Adaptee(被适配者) | 提供已有功能,其接口与客户端期望不兼容,由适配器调用。 |
定义与用途¶
适配器模式将一个类的接口转换成客户期望的另一种接口,使原本因接口不兼容而无法合作的类能够一起工作。
OO 适配器和真实世界的转接头承担类似的职责。客户继续调用自己熟悉的接口,适配器在内部转换调用方式,并委托给已有对象。
例如,客户期望一个具有 quack()、fly() 的鸭子接口,已有火鸡对象提供 gobble() 和短距离飞行。适配器可以把 quack() 转成 gobble(),并通过多次短距离飞行来实现客户期望的 fly()。
参与对象¶
| 角色 | 职责 |
|---|---|
| 目标接口(Target) | 定义客户需要调用的操作 |
| 被适配者(Adaptee) | 提供已有功能,但接口与客户期望不一致 |
| 适配器(Adapter) | 实现目标接口,并把调用转换、委托给被适配者 |
对象适配器通过组合持有被适配者。转换时除了方法名,还可能涉及参数、返回值和行为语义;需要确认转换后的行为符合目标接口的约定。
与其他模式的区别¶
Head First 示例:让火鸡接受鸭子的调用方式¶
《Head First 设计模式》第 7 章的鸭子与火鸡示例中,客户端通过 Duck 调用 quack() 和 fly(),已有的 Turkey 却提供 gobble() 和短距离飞行。作者 2020 年第二版配套代码 adapter/ducks 用 TurkeyAdapter 把火鸡包装成客户端可以接收的鸭子类型。
适配不仅转换方法名:quack() 转交给火鸡的 gobble(),一次 fly() 则触发五次火鸡飞行。这是配套代码为示例设定的行为映射;MallardDuck 和包装后的火鸡都能传入同一个 testDuck(Duck duck)。
| 具体类名 | 对应角色 | 职责 |
|---|---|---|
Duck |
目标接口 | 定义客户端期望的 quack() 和 fly()。 |
MallardDuck |
目标接口的原生实现 | 直接实现鸭子的叫声和飞行动作,供入口对照调用。 |
Turkey |
被适配者接口 | 定义已有的 gobble() 和 fly()。 |
WildTurkey |
具体被适配者 | 实现火鸡叫声和短距离飞行。 |
TurkeyAdapter |
对象适配器 | 实现 Duck,持有 Turkey turkey 并转换调用。 |
DuckTestDrive |
客户端与示例入口 | 创建对象,通过 testDuck(Duck duck) 测试鸭子接口。 |
示例 UML¶
classDiagram
direction TB
class Duck {
<<interface>>
+quack()
+fly()
}
class MallardDuck {
+quack()
+fly()
}
class Turkey {
<<interface>>
+gobble()
+fly()
}
class WildTurkey {
+gobble()
+fly()
}
class TurkeyAdapter {
Turkey turkey
+TurkeyAdapter(Turkey turkey)
+quack()
+fly()
}
class DuckTestDrive {
+main(String[] args)$
testDuck(Duck duck)$
}
Duck <|.. MallardDuck
Duck <|.. TurkeyAdapter
Turkey <|.. WildTurkey
TurkeyAdapter --> Turkey : turkey 委托
DuckTestDrive ..> Duck : testDuck 接收
DuckTestDrive ..> TurkeyAdapter : 创建适配器
DuckTestDrive ..> WildTurkey : 创建被适配者
图中保留火鸡适配鸭子的方向,省略反向的 DuckAdapter 及入口中的无人机挑战代码。TurkeyAdapter 没有继承 WildTurkey,而是持有 Turkey 接口,因此属于对象适配器。
协作过程¶
DuckTestDrive创建MallardDuck和WildTurkey,把后者传给TurkeyAdapter的构造器。- 客户端先将普通鸭子、再将适配器传给
testDuck(Duck duck);两次都只使用Duck的方法。 - 测试方法调用适配器的
quack(),适配器通过字段turkey调用gobble()。 - 测试方法调用适配器的
fly(),适配器循环五次调用turkey.fly(),完成示例约定的飞行转换。
固定版本源码: