必须用适配器模式当出现类型不匹配编译错误且无法修改被调用方代码时,典型场景包括接入第三方SDK、新旧系统对接、数据格式转换,关键判断标准是转换逻辑是否需与业务逻辑解耦。

什么时候必须用适配器模式?
当你遇到 cannot use ... as ... value in assignment: wrong type 这类编译错误,且不能改被调用方代码时,适配器就是最直接的解法。典型场景有三类:
- 接入第三方 SDK(如阿里云短信
SendSmsvs 腾讯云SendSMS),但业务层只认统一的SMSSender.Send - 旧系统模块(比如
OldCalculator.Add)要对接新计算库(NewMathLib.Sum),又不想动原有调用链 - 数据格式不一致:下游返回
[]byte,而你定义的接口要求string或json.RawMessage
关键判断标准不是“能不能写 if-else”,而是“这个转换逻辑是否该和业务逻辑解耦”。如果到处散落 string(x.Display()),就该抽成适配器。
结构体适配器怎么写才不踩坑?
Go 没有继承,所以对象适配器靠组合实现,但匿名嵌入(embedding)容易误用。常见错误是把被适配者暴露出去,导致调用方绕过适配逻辑直接调用原方法。
- ✅ 正确做法:用具名字段封装,禁止导出被适配者字段(如
display *LegacyDisplay,不是*LegacyDisplay) - ❌ 错误写法:
type Adapter struct{ *LegacyDisplay }—— 这会让adapter.Display()直接生效,破坏封装 - 工厂函数推荐加校验:比如传入
nil时 panic 或返回 error,避免运行时 panic
示例中 DisplayAdapter 的 Print() 方法里调用 da.display.Display(),而不是 da.Display(),就是为控制入口唯一性。
函数类型适配器比结构体更轻量?
是的,尤其适合 HTTP 中间件、回调函数等一次性转换场景。但要注意隐式接口满足带来的“假实现”风险。
- Go 接口是隐式实现,
HandlerFuncAdapter只要实现了ServeHTTP就算满足http.Handler,但若漏写ServeHTTP方法,编译不报错,运行时报nil pointer dereference - 函数适配器无法持有状态,想带 context 或配置得靠闭包,容易引发内存泄漏(比如闭包捕获了大对象)
- 调试时堆栈会多一层,日志打点需注意
runtime.Caller层级偏移
像 http.HandlerFunc(func(...){}) 本身就是个函数适配器,Go 标准库大量使用,说明它在简单场景下足够可靠。
适配器该不该加单元测试?
应该,而且只需测两件事:输入是否被正确转发、输出是否符合目标接口契约。不用测被适配者本身。
- 重点覆盖边界值:比如空字符串、
nil输入、超长 byte slice - 避免 mock 被适配者——直接用真实实例,因为适配器本意就是桥接真实依赖
- 如果适配逻辑含错误转换(如 JSON unmarshal 失败),确保
error被透传或按约定包装,别吞掉
最容易被忽略的是并发安全:多个 goroutine 同时调用适配器时,若内部缓存了非线程安全的状态(比如 map 未加锁),就会静默出错。










