适配器模式详解:结构型模式在迁移中的应用
适配器模式,说白了就是让两个不兼容的接口能在一起工作。我在数据迁移项目里,遇到过无数次这样的场景:老系统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:适配器模式在迁移中的三层结构
适配器在回滚设计中的关键作用
数据迁移最怕什么?切不过去,或者切过去之后发现有问题。如果适配器设计得巧妙,回滚就是一次配置变更:
- 切流前:适配器指向老系统SDK(相当于直通模式)
- 灰度期:适配器按比例转发部分流量到新系统(这时适配器做转换)
- 发现问题:将适配器内部的NewClient切换回OldClient,瞬间回滚,业务无感
- 确认稳定:移除适配器或改为直连新系统(这时适配器退化成了转发层)
注意:适配器里千万不要写业务逻辑。业务规则会变,适配器一旦承载了业务,回滚时就会面临“回滚了适配器但业务规则也要跟着回滚”的连锁问题。我见过最惨的一个项目,适配器里写了200行业务转换代码,最后回滚时根本不敢动,硬着头皮修数据修了一周。
适配器模式的六大最佳实践
- 接口最小化:适配器只暴露迁移需要的接口,不要图方便一股脑全部适配
- 无状态设计:适配器内部不能缓存任何业务数据,否则回滚后缓存数据和新系统不一致
- 可观测性:每个适配器调用都要打印耗时、入参、出参(脱敏),方便排查问题
- 降级开关:通过配置中心控制适配器的“增强模式”(转换)和“透传模式”(直通)
- 版本记录:每次适配器发布,记录适配的源版本和目标版本,方便查历史
- 单元测试:针对每一种源输入,验证适配后的输出是否符合目标要求
我个人的一个小技巧:在做适配器单元测试时,我会把老系统的真实历史数据(脱敏后)做成测试夹具(Fixture)。这样不仅能验证适配器是否正确,还能提前发现老系统里没文档化过的边界值,避免上线翻车。