适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换为客户端期望的另一个接口,从而使原本不兼容的类能够协同工作。它类似于现实生活中的电源适配器,起到接口转换的作用。以下是对适配器模式的详细介绍,包括定义、实现方式、优缺点、应用场景和示例代码(以 Java 为例,但概念适用于多种语言)。
1. 定义
- 适配器模式:通过创建一个适配器类,将一个不兼容的接口转换为目标接口,使客户端可以通过目标接口使用不兼容的类。
- 核心目标:
- 解决接口不兼容问题。
- 允许现有类与新系统无缝协作。
- 符合开闭原则(对扩展开放,对修改关闭)。
- 关键特点:
- 目标接口(Target):客户端期望的接口。
- 被适配者(Adaptee):需要适配的现有类,拥有不兼容的接口。
- 适配器(Adapter):实现目标接口,封装被适配者的功能。
2. 适配器模式的类型
适配器模式有两种主要实现方式:
2.1 类适配器(通过继承)
- 特点:适配器类通过继承被适配者类并实现目标接口。
- 适用场景:需要直接继承被适配者的功能。
- 缺点:Java 不支持多重继承,限制了灵活性。
2.2 对象适配器(通过组合)
- 特点:适配器类通过持有被适配者的实例并实现目标接口。
- 适用场景:更灵活,推荐在大多数场景中使用。
- 优点:符合组合优于继承的原则,易于扩展。
3. 实现方式
以下以 Java 展示类适配器和对象适配器的实现。
3.1 类适配器
// 目标接口
interface Target {
void request();
}
// 被适配者
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specific request");
}
}
// 适配器(继承 Adaptee,实现 Target)
class Adapter extends Adaptee implements Target {
@Override
public void request() {
// 调用被适配者的方法
specificRequest();
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Target target = new Adapter();
target.request(); // 输出: Adaptee's specific request
}
}
- 说明:
Adapter
继承Adaptee
并实现Target
接口。request()
方法调用specificRequest()
,实现接口转换。
3.2 对象适配器(推荐)
// 目标接口
interface Target {
void request();
}
// 被适配者
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specific request");
}
}
// 适配器(持有 Adaptee 实例,实现 Target)
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// 调用被适配者的方法
adaptee.specificRequest();
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request(); // 输出: Adaptee's specific request
}
}
- 说明:
Adapter
通过组合持有Adaptee
实例。- 更灵活,支持动态替换被适配者。
4. 优缺点
优点
- 接口兼容:使不兼容的类能够协同工作。
- 复用现有代码:无需修改原有类即可适配新接口。
- 灵活性(对象适配器):通过组合支持动态适配。
- 符合开闭原则:新增适配器无需修改现有代码。
缺点
- 增加复杂性:引入额外的适配器类,增加代码量。
- 类适配器局限:受限于单继承(在 Java 等语言中)。
- 性能开销:适配器可能增加少量调用开销。
5. 应用场景
- 遗留系统集成:将旧系统的接口适配到新系统中。
- 第三方库适配:将第三方库的接口转换为项目所需的接口。
- 接口标准化:统一不同模块的接口调用方式。
- 跨平台开发:适配不同平台的 API(如数据库驱动、UI 框架)。
示例场景:
- 数据库驱动:JDBC 适配不同数据库的驱动接口。
- 日志框架:将旧的日志系统(如
java.util.logging
)适配到新框架(如 SLF4J)。
6. 实际示例:电源适配器
模拟电源适配器,将 220V 电压转换为 5V 电压。
// 目标接口(客户端需要 5V)
interface PowerTarget {
void output5V();
}
// 被适配者(提供 220V)
class AC220V {
public void output220V() {
System.out.println("Output 220V");
}
}
// 适配器(对象适配器)
class PowerAdapter implements PowerTarget {
private AC220V ac220v;
public PowerAdapter(AC220V ac220v) {
this.ac220v = ac220v;
}
@Override
public void output5V() {
ac220v.output220V();
System.out.println("Adapted to 5V");
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
AC220V ac220v = new AC220V();
PowerTarget adapter = new PowerAdapter(ac220v);
adapter.output5V();
}
}
输出:
Output 220V
Adapted to 5V
7. 在其他语言中的实现
Objective-C 对象适配器
// Target.h
@protocol Target <NSObject>
- (void)request;
@end
// Adaptee.h
@interface Adaptee : NSObject
- (void)specificRequest;
@end
// Adaptee.m
@implementation Adaptee
- (void)specificRequest {
NSLog(@"Adaptee's specific request");
}
@end
// Adapter.h
@interface Adapter : NSObject <Target>
- (instancetype)initWithAdaptee:(Adaptee *)adaptee;
@end
// Adapter.m
@implementation Adapter {
Adaptee *_adaptee;
}
- (instancetype)initWithAdaptee:(Adaptee *)adaptee {
self = [super init];
if (self) {
_adaptee = adaptee;
}
return self;
}
- (void)request {
[_adaptee specificRequest];
}
@end
// 使用示例
int main(int argc, const char * argv[]) {
@autoreleasepool {
Adaptee *adaptee = [[Adaptee alloc] init];
Adapter *adapter = [[Adapter alloc] initWithAdaptee:adaptee];
[adapter request]; // 输出: Adaptee's specific request
}
return 0;
}
JavaScript 对象适配器
// 被适配者
class Adaptee {
specificRequest() {
console.log("Adaptee's specific request");
}
}
// 目标接口(通过函数模拟)
function Target() {
this.request = function() {};
}
// 适配器
class Adapter extends Target {
constructor(adaptee) {
super();
this.adaptee = adaptee;
}
request() {
this.adaptee.specificRequest();
}
}
// 使用
const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
adapter.request(); // 输出: Adaptee's specific request
8. 与其他模式的对比
- 适配器模式 vs 装饰器模式:
- 适配器模式:转换接口,使不兼容类协作。
- 装饰器模式:增强功能,保持接口不变。
- 适配器模式 vs 桥接模式:
- 适配器模式:解决现有接口不兼容问题。
- 桥接模式:设计时分离接口与实现,支持独立扩展。
- 适配器模式 vs 代理模式:
- 适配器模式:改变接口。
- 代理模式:控制访问,接口不变。
9. 注意事项
- 对象适配器优先:对象适配器更灵活,推荐在大多数场景中使用。
- 接口清晰:目标接口应简洁,避免过多方法。
- 性能:适配器增加一层调用,注意性能敏感场景。
- 框架支持:现代框架(如 Spring)常通过依赖注入实现适配,减少手动实现。
10. 总结
- 适配器模式:通过适配器类解决接口不兼容问题,支持现有代码复用。
- 两种实现:
- 类适配器:通过继承(受限于单继承)。
- 对象适配器:通过组合(更灵活,推荐)。
- 应用场景:遗留系统集成、第三方库适配、接口标准化。
- 推荐实践:优先使用对象适配器,保持接口简洁,结合依赖注入框架。
如果你需要更详细的代码示例(特定语言或场景,如 iOS UIKit 适配)、与其他模式的对比,或深入探讨某实现方式,请告诉我!