返回列表

深入理解设计模式:适配器(Adapter)

发布于 ·

深入理解设计模式:适配器(Adapter)

什么是适配器模式?

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户期望的另一个接口。适配器让原本由于接口不兼容而不能一起工作的类可以协同工作。

这个模式的核心思想是:转换接口。就像我们生活中常见的电源适配器,可以将不同国家的插头标准转换为适合中国插座的标准,让来自不同国家的电器设备能够在同一套供电系统中正常工作。

适配器的核心要素

1. 目标接口(Target Interface)

这是客户端期望使用的接口。

2. 适配者(Adaptee)

这是需要被适配的现有类,其接口与目标接口不兼容。

3. 适配器(Adapter)

这是核心角色,负责将适配者的接口转换成目标接口。

实际应用场景

场景一:第三方库集成

假设我们需要使用一个老版本的日志库,但它提供的接口与我们项目要求的接口不一致:

// 老版本日志库
interface OldLogger {
    void writeLog(String message, int level);
}

// 新版本项目需要的接口
interface NewLogger {
void logInfo(String message);
void logError(String message);
}

// 适配器实现
class LoggerAdapter implements NewLogger {
private OldLogger oldLogger;

public LoggerAdapter(OldLogger oldLogger) {
this.oldLogger = oldLogger;
}

@Override
public void logInfo(String message) {
// 将INFO级别映射为level=1
oldLogger.writeLog(message, 1);
}

@Override
public void logError(String message) {
// 将ERROR级别映射为level=5
oldLogger.writeLog(message, 5);
}
}

场景二:数据库驱动兼容性

不同数据库厂商提供的JDBC驱动接口可能略有差异,我们可以使用适配器来统一接口:

// MySQL驱动接口
interface MySQLDriver {
    ResultSet executeQueryMySQL(String sql);
    boolean connectMySQL(String url, String user, String password);
}

// PostgreSQL驱动接口
interface PostgreSQLDriver {
QueryResult executeQueryPG(String sql);
Connection connectPG(String connString);
}

// 通用数据库接口
interface UniversalDatabase {
DataSet query(String sql);
boolean connect(String connectionInfo);
}

// MySQL适配器
class MySQLAdapter implements UniversalDatabase {
private MySQLDriver mysqlDriver;

public MySQLAdapter(MySQLDriver driver) {
this.mysqlDriver = driver;
}

@Override
public DataSet query(String sql) {
ResultSet rs = mysqlDriver.executeQueryMySQL(sql);
return convertResultSetToDataSet(rs);
}

@Override
public boolean connect(String connectionInfo) {
// 解析连接信息并调用MySQL驱动
String[] parts = connectionInfo.split(":");
return mysqlDriver.connectMySQL(parts[0], parts[1], parts[2]);
}
}

适配器的实现方式

1. 对象适配器(常用)

通过组合的方式实现适配器:

from abc import ABC, abstractmethod

目标接口

class MediaPlayer(ABC): @abstractmethod def play(self, audiotype: str, filename: str): pass

适配者

class AdvancedMediaPlayer: def playvlc(self, filename: str): print(f"Playing vlc file: {filename}") def playmp4(self, filename: str): print(f"Playing mp4 file: {filename}")

适配器

class MediaAdapter(MediaPlayer): def init(self, audiotype: str): self.advancedplayer = None if audiotype == "vlc": self.advancedplayer = AdvancedMediaPlayer() elif audiotype == "mp4": self.advancedplayer = AdvancedMediaPlayer() def play(self, audiotype: str, filename: str): if audiotype == "vlc": self.advancedplayer.playvlc(filename) elif audiotype == "mp4": self.advancedplayer.playmp4(filename)

客户端代码

class AudioPlayer(MediaPlayer): def init(self): self.mediaadapter = None def play(self, audiotype: str, filename: str): # 对于支持的格式直接播放 if audiotype in ["mp3", "wav"]: print(f"Playing {audiotype} file: {filename}") # 对于不支持的格式,使用适配器 else: self.mediaadapter = MediaAdapter(audiotype) self.mediaadapter.play(audiotype, file_name)

2. 类适配器

通过继承的方式实现适配器(需要支持多重继承的语言):

// TypeScript中的类适配器示例
interface Target {
    request(): string;
}

// 需要适配的类
class Adaptee {
specificRequest(): string {
return ".eetpadA eht fo roivaheb laicepS";
}
}

// 类适配器
class Adapter extends Adaptee implements Target {
public request(): string {
// 转换接口
const result = this.specificRequest();
return Adapted: ${result.split("").reverse().join("")};
}
}

// 使用
function clientCode(target: Target): void {
console.log(target.request());
}

const adapter = new Adapter();
clientCode(adapter); // 输出: "Adapted: Seppahc eht fo .tcejorP"

适配器模式的优缺点

优点

  1. 解耦性:客户端不需要知道适配者的具体实现
  2. 灵活性:可以在运行时切换不同的适配器
  3. 复用性:可以将现有的适配者集成到新的系统中
  4. 开闭原则:对扩展开放,对修改关闭

缺点

  1. 复杂性增加:增加了中间层,可能导致系统变得复杂
  2. 性能开销:额外的封装层可能带来微小的性能损耗
  3. 过度使用风险:如果系统设计得当,可能根本不需要适配器

何时使用适配器模式?

  • 需要使用现有的类,但其接口不符合你的需求
  • 创建一个可复用的类,该类可以与不相关的类或不可预见的类协同工作
  • 想要使用已经存在的子类,但无法修改其接口以匹配父类接口
  • 需要转换不同数据格式或协议

实际应用建议

  1. 优先考虑接口重构:在引入适配器之前,先考虑是否可以通过重构使接口一致
  2. 保持简单:只在真正需要时才使用适配器模式
  3. 文档化:清晰地记录适配器的作用和转换逻辑
  4. 测试覆盖:确保适配器的转换逻辑得到充分测试
适配器模式虽然看似简单,但在系统集成、框架扩展和第三方库集成等场景中发挥着重要作用。正确使用适配器模式可以让我们的系统更加灵活和可维护。