发布于2026-07-05 阅读(0)
扫一扫,手机访问
在Python项目中对接不同厂商的API接口,几乎是每个后端开发都会遇到的场景。适配器模式在这里扮演的,就是那个把「方言」翻译成「普通话」的角色。问题在于,适配器的实现方式差异很大,踩过的坑也不少,从继承组合的选择,到响应结构的统一,再到异常处理的边界,每一步都有值得推敲的地方。

结论很明确:优先用组合,而不是继承。继承会强制让你的适配器绑定到某个具体厂商类的生命周期和接口细节上,一旦对方改了方法名、加了参数,你的子类立马崩掉。组合则只依赖厂商类的公开行为,松耦合,测试的时候也方便 mock。
一个典型的反面案例是写出 class MyAdapter(OldVendorAPI),结果发现 OldVendorAPI 的 send_request() 在新版本里改成了 execute(),而继承来的旧方法根本没被重写——调用时直接抛 AttributeError。正确的做法,是让适配器持有厂商的实例:
class VendorAAdapter:
def __init__(self, vendor_instance):
self._vendor = vendor_instance # 组合,不是继承
各家厂商返回的字段可以说是五花八门:VendorA 返回 {"status": "success", "data": {...}},VendorB 返回 {"code": 0, "result": {...}}。业务代码如果直接处理这些原始响应,那维护成本堪称灾难。
关键在于定义好你内部约定的输出结构——比如统一用 status、payload、error_message 这三个字段,然后把各厂商的原始响应「翻译」过来:
VendorA 的 "status" == "success" → 映射为 status=TrueVendorB 的 "code" == 0 → 同样映射为 status=Trueerror_message,哪怕原始数据是个空字符串或者堆栈片段这些映射逻辑不要散落在业务层,全都封装在适配器里。业务代码只认 adapter.fetch_user(id) 返回的标准化字典,干净利落。
当然要,但别重复造轮子。共性的逻辑——比如重试、超时封装、日志埋点——抽成基类或工具函数。真正需要放到具体适配器里的,是那些差异逻辑:字段解析、鉴权头构造、错误码映射。
一个常见的陷阱是试图写一个「万能适配器」,在里面加一堆 if-elif 判断厂商类型。结果就是 if vendor_type == "A": ... elif vendor_type == "B": ...。把所有厂商耦合进一个类,新增厂商就得改这个大类,严重违反开闭原则。
更稳妥的做法是这样的
AbstractAPIAdapter,声明 get()、post() 等方法VendorAAPIAdapter、VendorBAPIAdapterget_adapter(vendor="a") → 返回 VendorAAPIAdapter(...)要处理,而且得区分对待。厂商 SDK 自己抛的异常——比如 VendorAConnectionError——和标准库异常(例如 requests.Timeout)语义不同,但上层业务其实只关心一件事:这次调用失败了,是否应该重试?
适配器的职责,是把底层异常统一转化成自定义的异常体系:
APITimeoutErrorAPIAuthErrorAPINotFoundError这样一来,业务层可以用一致的 except APITimeoutError: 做重试,不用关心底层是 requests 还是 urllib3。需要注意的是,不要吞掉原始异常的 traceback,至少保留 raise APITimeoutError(...) from original_exc,否则排查问题的时候找不到根因。
真实项目里还有一个容易被忽略的细节:厂商 SDK 自身内置的重试机制,和你的适配器层重试可能会叠加,导致同一个请求被莫名其妙地发送三次。一个比较稳妥的做法,是关掉厂商 SDK 的自动重试,只在适配器层统一控制。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8