go中写适配器是为解决已有代码不可修改但需满足新接口的硬性问题,通过组合转发+转译方法实现协议对齐,而非套用设计模式;必须严格匹配方法签名,嵌入+覆盖可简化实现,单方法接口优先用函数适配器。

Go 里写 adapter 不是为了“套设计模式”,而是为了解决一个具体问题:已有代码不能改,但新接口又必须满足。直接硬编码转换或到处 if-else 调用,很快会让逻辑散落、难以测试、一改全崩。
为什么不用继承而用组合?
Go 没有子类继承,但接口是隐式实现的——只要结构体方法集满足接口定义,就自动符合。所以适配器不是“让 A 变成 B 的子类”,而是“让 A 的实例能被当成 B 用”。关键在组合:
-
Adapter结构体里嵌入或持有Adaptee(被适配对象)的引用,不复制行为,只转发+转译 - 所有方法都实现在
Adapter上,Adaptee完全无感知,也不需要改一行代码 - 如果
Adaptee是第三方库类型(比如*zap.Logger),你甚至没法给它加方法,组合是唯一可行路径
参数类型不一致时怎么处理?
最常见坑:目标接口收 string,被适配者要 int 或 map[string]interface{}。这时适配器不是简单透传,得做明确转换,且要处理边界情况:
- 别在
Adapter方法里写业务逻辑(比如校验、重试、缓存),那属于上层职责;适配器只做协议对齐 - 类型转换失败时,优先返回明确错误(如
fmt.Errorf("invalid level: %v", level)),而不是静默 fallback 或 panic - 浮点/整数互转注意精度丢失,比如
float64→int要考虑四舍五入还是截断,最好由调用方决定,适配器只提供可选策略字段
多个 Adaptee 共享同一 Target 接口怎么办?
比如你要统一 Logger 接口,但后端可能是 zap.Logger、logrus.Entry、甚至 io.Writer。不要为每个都写一个独立 XXXAdapter 结构体——容易重复、难维护。
- 用泛型约束适配器:定义
type LoggerAdapter[T LogWriter] struct { inner T },再让T实现统一的写日志方法 - 或者更轻量:直接用函数类型适配,比如
type Logger func(string),然后封装func(z *zap.Logger) Logger { return func(s string) { z.Info(s) } } - 避免把
switch type写进适配器内部——那是工厂的事;适配器只负责“一对一”桥接
真正难的不是写一个 Adapter,而是判断什么时候不该写:如果两个接口差异太大(比如一个流式读、一个批量查),强行适配只会让中间层越来越厚。这时候该重构接口,而不是堆适配器。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











