#适配器模式
#一个接口不兼容的代价
2022年,我们团队对接了第三个支付渠道,结果代码里多了一堆这样的代码:
// 每个渠道都写一个适配器类,最后变成了"适配器地狱"
class AlipayAdapter { void pay() {} }
class WechatPayAdapter { void pay() {} }
class UnionPayAdapter { void pay() {} }
class MeituanPayAdapter { void pay() {} }
class BaiduPayAdapter { void pay() {} }
// ... 8 个适配器,每个都不一样问题是:这 8 个适配器接口各不相同,调用方必须知道每个渠道的接口细节。
适配器模式解决的就是这个问题:让不兼容的接口变得兼容。
#二、适配器模式核心🔴
#2.1 什么是适配器
适配器模式:将一个类的接口转换成客户端所期望的另一种接口,使原本不兼容的类可以合作。
现有接口 (Adaptee) 目标接口 (Target)
┌─────────────┐ ┌─────────────┐
│ specific │ ┌────┐ │ │
│ request() │───▶│适配器│──▶│ request() │
└─────────────┘ └────┘ └─────────────┘#2.2 类适配器 vs 对象适配器
// 目标接口
interface Target {
void request();
}
// 被适配者
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee: specific request");
}
}
// 方式一:类适配器(通过继承)
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
// 调用父类方法
specificRequest();
}
}
// 方式二:对象适配器(通过组合)
class ObjectAdapter implements Target {
private final Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}#2.3 Java 集合框架中的适配器
// Arrays.asList:数组 → List
String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr); // 适配器
// Collections.enumeration:迭代器 → 枚举
Iterator<String> it = list.iterator();
Enumeration<String> enumeration = Collections.enumeration(it);
// InputStreamReader:字节流 → 字符流
Reader reader = new InputStreamReader(inputStream); // 适配器#三、REST API 适配器🟡
#3.1 外部系统适配
// 外部第三方 API(接口不兼容)
class ThirdPartyLegacyAPI {
public LegacyResponse getUserData(Map<String, Object> params) {
// 返回 Map 结构的旧格式
return null;
}
}
// 目标接口
interface UserDataProvider {
UserDTO getUserById(Long id);
}
// 适配器
class ThirdPartyAPIAdapter implements UserDataProvider {
private final ThirdPartyLegacyAPI legacyAPI;
@Override
public UserDTO getUserById(Long id) {
Map<String, Object> params = new HashMap<>();
params.put("user_id", id);
params.put("format", "json");
LegacyResponse response = legacyAPI.getUserData(params);
return new UserDTO(
(String) response.get("user_name"),
(String) response.get("email"),
(Integer) response.get("age")
);
}
}#3.2 多数据源适配
interface DataSource {
User findUser(Long id);
Order findOrder(Long id);
}
// MySQL 数据源适配
class MySQLDataSource implements DataSource {
private final JdbcTemplate jdbcTemplate;
@Override
public User findUser(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
userRowMapper,
id
);
}
}
// MongoDB 数据源适配
class MongoDataSource implements DataSource {
private final MongoTemplate mongoTemplate;
@Override
public User findUser(Long id) {
return mongoTemplate.findById(id, User.class);
}
}#四、生产避坑🟡
#4.1 适配器中的异常处理
// ❌ 错误:直接抛出未转换的异常
class BadAdapter implements Target {
@Override
public void request() {
try {
adaptee.specificOperation();
} catch (LegacyException e) {
throw e; // 调用方无法处理
}
}
}
// ✅ 正确:转换为新异常
class GoodAdapter implements Target {
@Override
public void request() {
try {
adaptee.specificOperation();
} catch (LegacyException e) {
throw new ServiceUnavailableException(
"External service failed: " + e.getMessage(), e
);
}
}
}#4.2 适配器不是万能的
// ❌ 错误:用适配器弥补 API 设计缺陷
// 如果原有接口设计有问题,应该重构而不是适配
class OverlyComplexAdapter implements Target {
@Override
public void request() {
// 大量逻辑来弥合两个不兼容的接口
// 说明接口设计本身就有问题
}
}#五、面试总结
| 级别 | 期望回答 |
|---|---|
| P5 | 能写出类适配器和对象适配器 |
| P6 | 能说出 Java 集合框架中的适配器应用 |
| P7 | 能设计复杂系统的适配器架构 |