1、接口适配基础:什么是接口适配、适配器模式原理、接口适配的常见场景
各位同学,咱们今天聊聊接口适配。说实话,这玩意儿听起来有点学术,但说白了,就是「让两个不兼容的东西能一起工作」。我做了十几年系统,几乎每个项目都会碰到这类问题。
你想想看,现实世界里,你拿个 Type-C 充电器去给老款 Micro-USB 手机充电,中间缺个转接头,就充不进去。软件系统也一样。两个模块接口对不上,数据就传不过去。这时候,适配器就派上用场了。
1.1 什么是接口适配
接口适配,核心就一句话:在不修改原有代码的前提下,让两个不兼容的接口协同工作。
我记得刚入行那会儿,接手一个遗留系统。老系统输出 XML,新模块只认 JSON。改老系统?风险太大。改新模块?业务逻辑全耦合在里面。最后我加了一层适配器,两边都没动,问题就解决了。
核心原则:
- 对现有代码零侵入
- 调用方无需感知底层变化
- 适配层可独立测试、独立部署
适配的本质,就是做一层「翻译」。把 A 的请求翻译成 B 能理解的格式,再把 B 的响应翻译回 A 能读懂的格式。嗯,就这么简单。
1.2 适配器模式原理
适配器模式是 GoF 23 种设计模式之一。它分两种:类适配器和对象适配器。我个人更常用对象适配器,因为组合优于继承,灵活性更高。
咱们直接看代码,比讲理论直观得多。
对象适配器示例(Java)
// 目标接口:新系统期望的接口
public interface NewSystem {
String processData(String input);
}
// 适配者:老系统的实现
public class LegacySystem {
public String handleRequest(String xmlData) {
// 老系统只处理 XML
return "<response>OK</response>";
}
}
// 适配器:把老系统包装成新系统
public class LegacyAdapter implements NewSystem {
private LegacySystem legacy;
public LegacyAdapter(LegacySystem legacy) {
this.legacy = legacy;
}
@Override
public String processData(String input) {
// 1. 把 JSON 转成 XML
String xmlInput = convertJsonToXml(input);
// 2. 调用老系统
String xmlOutput = legacy.handleRequest(xmlInput);
// 3. 把 XML 转回 JSON
return convertXmlToJson(xmlOutput);
}
private String convertJsonToXml(String json) {
// 实际转换逻辑
return "<request>" + json + "</request>";
}
private String convertXmlToJson(String xml) {
// 实际转换逻辑
return "{ \"status\": \"OK\" }";
}
}
你看,新系统调用 processData,完全不知道背后是个老古董在干活。适配器把脏活累活全扛了。
我的习惯:适配器里不要塞业务逻辑。它只做格式转换和接口映射。业务逻辑一进去,适配器就变成了「四不像」,后期维护会非常痛苦。
类适配器示例(Java)
// 类适配器:通过继承实现
public class ClassAdapter extends LegacySystem implements NewSystem {
@Override
public String processData(String input) {
String xmlInput = convertJsonToXml(input);
String xmlOutput = handleRequest(xmlInput); // 直接调用父类方法
return convertXmlToJson(xmlOutput);
}
// 转换方法同上
}
类适配器代码更少,但 Java 单继承的限制让它不够灵活。我一般只在适配逻辑非常简单时才用。
| 对比维度 | 对象适配器 | 类适配器 |
|---|---|---|
| 实现方式 | 组合(持有适配者引用) | 继承(继承适配者类) |
| 灵活性 | 高,可适配多个适配者 | 低,只能适配一个父类 |
| 耦合度 | 低,面向接口编程 | 高,依赖具体父类 |
| 我推荐 | ✅ 优先使用 | ⚠️ 谨慎使用 |
1.3 接口适配的常见场景
场景太多了。我挑几个最典型的说说。
场景一:第三方 SDK 集成
每个第三方支付、推送、地图 SDK 都有自己的接口风格。你不可能让业务代码去适配每个 SDK。加一层适配器,业务只跟你的抽象接口打交道。换 SDK 时,只改适配器,不动业务。
我曾经在一个电商项目里,对接过四家物流公司。每家接口都不一样。我写了个 LogisticsAdapter,统一成 createShipment()、trackOrder() 几个方法。后来换物流商,两天就搞定了。
场景二:旧系统迁移
老系统跑得好好的,但接口太老旧。新系统要接进来,又不敢动老代码。适配器就是最好的「中间人」。我在金融项目里干过这事——核心账务系统是 2005 年的 C++ 写的,新风控系统是 Java。适配器扛了两年,直到老系统彻底下线。
场景三:多数据源统一查询
公司有 MySQL、Redis、Elasticsearch,查询方式各不相同。业务层希望一个 search(keyword) 搞定所有。适配器模式正好用上:每个数据源一个适配器,统一返回标准结果集。
避坑指南:我曾经在适配器里做了数据聚合和排序,结果性能惨不忍睹。适配器只做接口适配,不做数据加工。数据聚合是服务层的活,别混在一起。
1.4 适配器模式的核心结构图
下面这张图,是我画了无数遍的。你把它记牢,适配器模式就懂了八成。
这张图你多看两眼。Client 只认识 Target 接口,Adapter 实现了 Target,背后调用 Adaptee。这就是适配器模式的全部秘密。
1.5 什么时候不该用适配器
适配器虽好,但不是万能药。我见过不少滥用的情况。
- 接口差异太大:两边语义完全不同,硬适配只会让代码变得诡异。这时候该重构就重构。
- 性能敏感路径:适配器多了一层调用和转换,高频场景下可能成为瓶颈。我曾经在网关层用适配器做协议转换,QPS 一上去就扛不住,后来改成直接解析。
- 可以改源码时:如果两边代码都是你的,直接改接口比加适配器更干净。适配器是为「不能改」的场景准备的。
我的判断标准:问自己一个问题——「半年后别人看这段代码,能一眼明白为什么加适配器吗?」如果答案是否定的,那可能选错了方案。