优先用组合而非继承,因组合松耦合、易测试;需统一厂商响应结构,映射为内部约定字段;应为各厂商写独立适配器类,避免if-elif耦合;须将底层异常转为统一自定义异常并保留原始traceback。

适配器模式该用类继承还是组合?
优先用组合,不是继承。继承会强制绑定到某个具体厂商类的生命周期和接口细节,一旦厂商改名方法或加参数,你的子类立刻崩;组合则只依赖厂商类的公开行为,松耦合,也方便 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": {...}},客户端代码没法直接受用。适配器必须做字段映射和状态归一化。
立即学习“Python免费学习笔记(深入)”;
关键点在于定义你内部约定的输出结构(比如统一用 status、payload、error_message),再把厂商原始响应“翻译”过去:
-
VendorA的"status" == "success"→ 映射为status=True -
VendorB的"code" == 0→ 同样映射为status=True - 所有异常响应都提取出可读的
error_message,哪怕原始是空字符串或堆栈片段
别在业务层做这些判断——放在适配器里,业务代码只认 adapter.fetch_user(id) 返回的标准化字典。
要不要为每个厂商写独立适配器类?
要,但别重复造轮子。共性逻辑(如重试、超时封装、日志埋点)抽成基类或工具函数;差异逻辑(字段解析、鉴权头构造、错误码映射)才放到具体适配器里。
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
常见陷阱是试图用一个“万能适配器”加一堆 if-elif 判断厂商类型,结果:if vendor_type == "A": ... elif vendor_type == "B": ...——这等于把所有厂商耦合进一个类,新增厂商就得改这个大类,违反开闭原则。
更稳的做法:
- 定义抽象接口
AbstractAPIAdapter,声明get()、post()等方法 - 为每家厂商写独立实现:
VendorAAPIAdapter、VendorBAPIAdapter - 工厂函数按配置返回对应实例:
get_adapter(vendor="a")→ 返回VendorAAPIAdapter(...)
适配器里要不要处理网络异常?
要,而且得区分处理。厂商 SDK 自己抛的异常(比如 VendorAConnectionError)和标准库异常(requests.Timeout)语义不同,但上层业务只需要知道“这次调用失败了,是否重试?”
适配器应把底层异常转为统一的自定义异常,例如:
- 网络超时 → 抛
APITimeoutError - 认证失败 → 抛
APIAuthError - 厂商返回 404 → 抛
APINotFoundError
这样业务层可以用一致的 except APITimeoutError: 做重试,不用关心底层是 requests 还是 urllib3。
注意:不要吞掉原始异常的 traceback,至少保留 raise APITimeoutError(...) from original_exc,否则排查时找不到根因。
真实项目里最常被忽略的是厂商 SDK 自身的重试机制和你的适配器重试叠加,导致请求被发三次——得关掉厂商 SDK 的自动重试,只在适配器层统一控制。

















