08_适配器模式 Adapter Pattern
1. 基本信息
中文名称: 适配器模式
英文名称: Adapter Pattern
模式类型: 结构型设计模式
Rust 中常见实现方式: trait 实现适配、包装结构体(wrapper)、From/Into trait、Deref trait
适配器模式的核心是:将一个接口转换成调用方期望的接口,使原本不兼容的接口能够协同工作。
在 Rust 中,适配器模式通常通过 包装结构体 + trait 实现,或者直接利用标准库的 From/Into trait 做类型转换。
2. 模式核心思想
适配器模式的核心思想是:
通过包装或转换,把不兼容的接口变成调用方可以使用的接口,而无需修改原始类型。
换句话说:
- 调用方使用统一接口
- 被适配对象不需要修改
- 适配器负责转换或适配行为
3. 这个模式解决什么问题
在实际开发中,适配器模式解决以下问题:
系统中存在老旧接口或第三方库接口,与当前调用方接口不兼容。
新增的模块接口与现有接口不同,需要兼容旧系统。
调用方无法修改被适配对象的代码。
希望统一调用接口,减少条件判断或重复包装代码。
例如:
- 文件读写接口兼容不同库
- 网络客户端统一接口
- 外部 API 返回类型不符合内部业务接口
- 第三方 crate 提供不同函数签名
适配器模式可以隐藏接口差异,使系统内部调用一致。
4. 不使用这个模式会怎样
如果不使用适配器模式,可能出现:
调用方需要直接修改或特殊处理老接口。
系统中出现大量 if/else 判断接口类型。
逻辑散落,维护成本高。
添加新类型或库时,调用方需要修改多处代码。
5. 传统面向对象中的实现思路
在传统 OOP 中,适配器模式通常有两种形式:
- 类适配器(通过继承)
- Adapter 继承自被适配类,同时实现目标接口。
- 对象适配器(通过组合)
- Adapter 包含一个被适配对象,并实现目标接口,调用内部对象的方法并做转换。
结构示意:
Target (调用方期望接口)
Adapter (实现 Target 接口, 包含 Adaptee)
Adaptee (原始接口)
调用方只依赖 Target 接口,Adapter 做转换。
6. Rust 中的实现思路
6.1 Rust 中通常怎么实现
在 Rust 中,适配器模式通常用 包装结构体 + trait 来实现。
// 被适配对象
struct OldPrinter;
impl OldPrinter {
fn print_old(&self, msg: &str) {
println!("旧打印机: {}", msg);
}
}
// 目标接口
trait Printer {
fn print(&self, msg: &str);
}
// 适配器
struct PrinterAdapter {
adaptee: OldPrinter,
}
impl Printer for PrinterAdapter {
fn print(&self, msg: &str) {
// 调用旧接口并适配参数
self.adaptee.print_old(msg);
}
}
fn main() {
let old_printer = OldPrinter;
let adapter = PrinterAdapter { adaptee: old_printer };
adapter.print("Hello Rust Adapter!");
}
另一种方式是利用 From/Into trait 做类型转换:
struct Celsius(f64);
struct Fahrenheit(f64);
impl From<Fahrenheit> for Celsius {
fn from(f: Fahrenheit) -> Self {
Celsius((f.0 - 32.0) * 5.0 / 9.0)
}
}
fn main() {
let f = Fahrenheit(212.0);
let c: Celsius = f.into();
println!("212°F = {:.2}°C", c.0);
}
6.2 和传统 OOP 写法相比有什么不同
- Rust 不依赖类继承体系,适配器通过 组合 + trait 实现接口适配,而不是继承。
- 类型转换可以利用标准库的
From/Intotrait,无需显式 Adapter 类。 - Rust 静态类型检查保证调用方接口和适配器接口一致,减少运行时错误。
- 不需要虚函数或多态指针,静态分发即可实现零成本适配。
6.3 Rust 中是否有更自然的替代写法
- 对简单参数或类型适配,可以直接用 From/Into 或结构体更新语法。
- 对复杂行为适配,推荐包装结构体 + trait 实现。
- 对闭包场景,可以直接传递闭包实现行为替换,减少显式 Adapter 结构体。
7. Rust 中涉及的语言特性
trait
impl
包装结构体(struct + 内部对象)
From / Into trait
泛型(可选)
所有权与借用
闭包(可选)
8. 性能与工程代价
- 静态适配器(包装 + trait)几乎零成本,编译器可内联。
- 使用
Box<dyn Trait>动态分发时会有一次虚函数调用开销。 - From/Into 转换非常轻量,适合类型转换场景。
- 包装结构体过多可能增加代码层级,但不会增加运行时开销。
9. 典型应用场景
老旧接口与新系统接口不兼容,需要适配
第三方库返回类型与内部类型不一致
不同平台或模块的实现差异封装
类型或参数需要转换以统一接口
行为或方法调用需要中间适配处理
10. 和相似模式的区别
- 装饰器模式:装饰器模式在原对象上增加功能,不改变接口;适配器模式改变接口使调用方能使用。
- 代理模式:代理模式控制访问或增加逻辑;适配器模式关注接口兼容。
- 工厂模式:工厂模式负责对象创建;适配器模式不创建对象,而是转换已有对象接口。
- 策略模式:策略模式关注可替换算法或行为;适配器模式关注接口兼容和适配。
11. 使用该模式的优点
调用方无需修改即可使用不兼容接口
可复用现有代码,无需修改老接口
与 trait 结合可保持类型安全
支持静态或动态适配
易于扩展新适配器
12. 使用该模式的代价
包装层增加代码层级
动态适配(Box<dyn Trait>)有轻微虚函数开销
复杂适配逻辑可能增加维护成本
13. 什么时候不应该使用
原接口已经兼容调用方,无需适配
适配逻辑过于简单,直接转换即可
不需要复用现有对象或接口
14. 一个简单例子思路
场景: 老打印机接口与新打印系统不兼容
- 老打印机提供
print_old(&str)方法 - 新系统需要
Printer::print(&str)trait 接口 - 创建 Adapter 包装老打印机,impl Printer trait
- 调用方通过 Adapter 调用统一接口
15. 总结一句话
适配器模式的本质是:将不兼容的接口转换成调用方期望的接口,使系统可以复用现有对象而无需修改,同时保持类型安全。在 Rust 中,包装结构体 + trait 或 From/Into 是最常用实现方式。