适配器模式

一个接口不兼容的代价

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能设计复杂系统的适配器架构