Go适配器模式用嵌入+方法委托实现轻量接口转换,只覆盖不匹配方法,需防nil嵌入、context丢失、error未包装三类panic,并通过注册表管理多源适配。

Go 里没有“继承”和“抽象类”,所谓适配器模式,就是用结构体字段 + 方法委托,把一个已有类型“包一层”,让它满足另一个接口——前提是:你手上已经有两个不兼容的接口,又不能改对方代码。
用嵌入+方法委托实现最轻量适配
别写空壳 struct 再手动实现所有方法。Go 的匿名字段天然支持方法提升,直接嵌入原类型,只覆盖签名不一致或缺失的方法即可。
-
type ThirdPartyLogger struct{}提供Write(msg string, level int) -
type Logger interface{ Log(message string) }是你业务要求的接口 -
type LoggerAdapter struct{ *ThirdPartyLogger }—— 匿名嵌入,自动获得Write方法 - 只需显式实现
Log:func (a *LoggerAdapter) Log(msg string) { a.Write(msg, 1) }
如果目标接口比原类型多一个方法,就只实现那一个;其余全部透传。硬编码全量方法体是反模式。
注意三个容易 panic 的地方
适配器不是透明管道,三个位置不校验就会在运行时崩:
立即学习“go语言免费学习笔记(深入)”;
- 嵌入字段为
nil时调用方法直接 panic —— 构造函数里必须加if src == nil { panic("nil source") } - 原接口接收
context.Context,但目标接口没暴露 —— 别丢 context,要么用context.Background()显式降级,要么推动目标接口升级(需协同) - 错误返回位置/类型不一致(比如原方法返回
(int, error),目标要error)—— 必须显式包装:return fmt.Errorf("wrap: %w", err),别裸 return
多个来源时别 if-else,用注册表+工厂
当要适配 "aws-s3"、"minio"、"localfs" 多种后端时,硬编码判断类型再 new 适配器会迅速腐化。
- 定义注册表:
var adapters = make(map[string]func() Fetcher) - 注册构造函数:
adapters["aws-s3"] = func() Fetcher { return &S3Adapter{client: newS3Client()} } - 使用时查表:
factory := adapters[config.Provider]; if factory == nil { panic("unknown provider") }
键名用配置项(如 "aws-s3"),别用 reflect.TypeOf,否则耦合反射且不可配置。
标准库里的真实例子:http.HandlerFunc
它不是语法糖,就是一个典型适配器:type HandlerFunc func(http.ResponseWriter, *http.Request) 是函数类型,而 http.Handler 是接口,要求实现 ServeHTTP 方法。
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) } —— 就这一行,把普通函数“转成”了接口。你写的每个 http.HandleFunc("/", handler),背后都是这个适配器在起作用。
真正难的从来不是怎么写适配器,而是识别出哪里该切一刀:那个“不能改”的第三方类型,和那个“必须满足”的业务接口,中间差的那一层转换逻辑,才是关键。漏掉 context 或 error 包装,上线后第一波压测就可能暴露。


















