适配器模式详解:结构型模式在迁移中的应用

适配器模式,说白了就是让两个不兼容的接口能在一起工作。我在数据迁移项目里,遇到过无数次这样的场景:老系统SDK返回一个XML结构,新系统偏要JSON格式;或者旧接口参数叫userId,新接口非要accountId。直接改老系统?不现实。改新系统?人家是标准产品。这时候适配器模式就是你的救星。

为什么迁移中需要适配器?

你可能觉得,不就是包一层转换吗?嗯,没错,但关键是这一层怎么设计才能不影响回滚。我见过不少团队直接在业务代码里写if-else判断新旧版本,结果回滚时拆得七零八落。

适配器的核心价值其实就三点:

  • 解耦:迁移中的老系统和新系统不需要互相知道对方的存在
  • 隔离:适配层独立部署,回滚时只动这一层,不动上下游
  • 可测:适配器可以单独写单元测试,验证数据转换正确性

我的经验:有一次做银行核心系统迁移,老系统的账户状态字段用的是0/1/2枚举,新系统是字符串"ACTIVE""FROZEN""CLOSED"。我们写了一个适配器,上线后发现有历史数据漏掉了3这个值(老系统文档没写全)。幸好适配器里加了校验和日志,没让脏数据进新系统,三天就修完了。

两种实现:类适配器 vs 对象适配器

维度 类适配器(继承) 对象适配器(组合)
实现方式 适配器继承Adaptee,实现Target接口 适配器持有Adaptee实例,实现Target接口
灵活性 低,继承是静态绑定 高,运行时动态替换Adaptee
适用场景 Adaptee接口稳定,且你愿意强耦合 需要支持多种Adaptee,或需要回滚切换
回滚友好度 差,继承链改动影响大 好,切换Adaptee即可实现版本回退

我个人习惯在数据迁移中用对象适配器。为什么?你想想,回滚的时候只需要替换适配器内部持有的Adaptee实例,甚至可以通过配置中心动态切换,完全不需要改代码。

# 对象适配器示例 —— 用组合实现SDK版本兼容
class OldSdkClient:
    def get_user_info(self, uid):
        return {"name": "张三", "balance": 100.00}

class NewSdkClient:
    def query_user(self, user_id):
        return {"nickname": "张三", "available_balance": 100.00}

class UserAdapter:
    """适配器:将新SDK接口转换为旧SDK接口格式"""
    def __init__(self, new_client):
        self._client = new_client  # 持有新客户端实例

    def get_user_info(self, uid):
        raw = self._client.query_user(uid)
        # 转换字段名和格式
        return {
            "name": raw["nickname"],
            "balance": raw["available_balance"]
        }

# 使用
old_client = OldSdkClient()
new_client = NewSdkClient()
adapter = UserAdapter(new_client)

# 业务代码完全不用改,还是调用 get_user_info
result = adapter.get_user_info(123)
print(result)  # {'name': '张三', 'balance': 100.0}

避坑指南:我曾经踩过一个坑——适配器里做了数据清洗,比如去空格、转大写,结果回滚后发现老系统根本不洗数据,造成线上数据不一致。后来我规定:适配器只做格式转换,不做业务清洗。清洗逻辑放在独立的数据管道里,和适配器解耦。

SVG:适配器模式在迁移中的三层结构

适配器模式结构图 Target(目标接口) get_user_info(uid) Adaptee(源对象) query_user(user_id) Adapter(对象适配器) 组合Adaptee + 实现Target 内部调用Adaptee并转换 实现目标接口 组合并调用 持有引用 💡 迁移中的适配器角色 • 对象适配器通过组合适应多种源端,切换Adaptee即可实现数据源回滚 • 适配器不修改源和目标代码,是迁移桥头堡的典型模式

适配器在回滚设计中的关键作用

数据迁移最怕什么?切不过去,或者切过去之后发现有问题。如果适配器设计得巧妙,回滚就是一次配置变更:

  1. 切流前:适配器指向老系统SDK(相当于直通模式)
  2. 灰度期:适配器按比例转发部分流量到新系统(这时适配器做转换)
  3. 发现问题:将适配器内部的NewClient切换回OldClient,瞬间回滚,业务无感
  4. 确认稳定:移除适配器或改为直连新系统(这时适配器退化成了转发层)

注意:适配器里千万不要写业务逻辑。业务规则会变,适配器一旦承载了业务,回滚时就会面临“回滚了适配器但业务规则也要跟着回滚”的连锁问题。我见过最惨的一个项目,适配器里写了200行业务转换代码,最后回滚时根本不敢动,硬着头皮修数据修了一周。

适配器模式的六大最佳实践

  • 接口最小化:适配器只暴露迁移需要的接口,不要图方便一股脑全部适配
  • 无状态设计:适配器内部不能缓存任何业务数据,否则回滚后缓存数据和新系统不一致
  • 可观测性:每个适配器调用都要打印耗时、入参、出参(脱敏),方便排查问题
  • 降级开关:通过配置中心控制适配器的“增强模式”(转换)和“透传模式”(直通)
  • 版本记录:每次适配器发布,记录适配的源版本和目标版本,方便查历史
  • 单元测试:针对每一种源输入,验证适配后的输出是否符合目标要求

我个人的一个小技巧:在做适配器单元测试时,我会把老系统的真实历史数据(脱敏后)做成测试夹具(Fixture)。这样不仅能验证适配器是否正确,还能提前发现老系统里没文档化过的边界值,避免上线翻车。