6. 适配器模式(Adapter)

问题:已有接口和目标接口不兼容,不想改老代码。

核心:写一个适配层,把旧接口转换成新接口。

// 旧接口:华氏温度
class OldWeatherAPI {
  getTemp(): number { return 98.6; }
}

// 适配器:转成摄氏度
class CelsiusAdapter {
  #oldAPI: OldWeatherAPI;
  constructor(api: OldWeatherAPI) { this.#oldAPI = api; }
  getTempC(): number { return (this.#oldAPI.getTemp() - 32) * 5 / 9; }
}

const adapter = new CelsiusAdapter(new OldWeatherAPI());
console.log(adapter.getTempC().toFixed(1)); // 37.0
package adapter

// 旧接口:华氏温度
type OldWeatherAPI struct{}

func (o OldWeatherAPI) GetTempF() float64 { return 98.6 }

// 目标接口:摄氏温度
type CelsiusProvider interface {
  GetTempC() float64
}

// 适配器
type CelsiusAdapter struct {
  old OldWeatherAPI
}

func NewCelsiusAdapter(old OldWeatherAPI) CelsiusProvider {
  return &CelsiusAdapter{old: old}
}

func (a *CelsiusAdapter) GetTempC() float64 {
  return (a.old.GetTempF() - 32) * 5 / 9
}

实战场景

  • 老接口迁移:旧系统返回字段名、单位或错误结构与新系统不一致,适配器把旧响应转换成新接口期望的格式。
  • 第三方 SDK 包装:支付、短信、地图、对象存储 SDK 的方法风格各不相同,适配成项目内部统一接口。
  • 单位转换:温度、货币、长度、时区、文件大小等外部数据进入系统时,适配器统一转换成内部标准单位。
  • 数据库替换:把旧 repository 接口适配到新的 ORM 或查询层,让业务代码不用一次性跟着重写。
  • UI 组件封装:不同组件库的 value/onChangemodelValue/update:modelValue、表单校验接口不一致,可以写适配层统一。
  • 消息格式桥接:Kafka、RabbitMQ、Webhook、Server-Sent Events 的消息结构不同,适配成内部统一事件对象。
  • Auth 集成:OAuth、JWT、Session、企业 SSO 返回的用户信息格式不同,适配成统一的 CurrentUser
  • 文件导入:不同客户上传的 CSV 列名和编码不一致,适配器把它们转换成标准领域对象。
  • 测试替身:真实服务接口很复杂,测试中用适配器把简单 fake 对象包装成生产接口。
  • 渐进式重构:新旧模块并存时,先用适配器隔离差异,等调用方稳定后再慢慢替换内部实现。