Go中适配器模式靠组合+接口隐式实现,用结构体持有第三方对象指针并转发调用,不嵌入、不导出、无状态,函数场景可用闭包封装,核心是参数映射与错误转换。

Go 语言中处理接口不兼容,**不用改旧代码、不依赖继承、不强求第三方配合**,靠的是「组合 + 接口隐式实现」——这是适配器模式在 Go 里最自然、最轻量的落地方式。
为什么必须用结构体组合而不是重写方法?
因为 Go 没有类继承,也无法让 ThirdPartyLogger 直接“变成”Logger。强行复制逻辑会破坏单一职责,也丧失对原服务更新的响应能力。组合是唯一能安全复用已有行为的方式。
- 适配器结构体只持有(
*ThirdPartyLogger)而非复制其字段或逻辑 - 目标接口方法(如
Log(message string))内部只做参数映射和调用转发 - 所有错误处理、重试、日志埋点等增强逻辑,应放在上层业务或中间件,而非适配器内
如何设计适配器结构体字段?
字段声明要体现“谁被包装、谁被转换”,避免裸指针或值类型误用。
- 用指针字段(
*ThirdPartyLogger)持有被适配者:保证与原实例状态一致,且符合多数第三方库的使用约定 - 不导出被适配者字段(如
thirdParty *ThirdPartyLogger而非ThirdParty *ThirdPartyLogger):防止调用方绕过适配逻辑直接调用旧方法 - 不嵌入被适配者(即避免
type Adapter struct { *ThirdPartyLogger }):否则会意外暴露Write等原始方法,违反接口契约
函数适配器适合什么场景?
当被适配对象是函数(如 func(string) error)而非结构体时,结构体包装反而冗余,此时闭包或轻量封装更合适。
立即学习“go语言免费学习笔记(深入)”;
技术上面应用了三层结构,AJAX框架,URL重写等基础的开发。并用了动软的代码生成器及数据访问类,加进了一些自己用到的小功能,算是整理了一些自己的操作类。系统设计上面说不出用什么模式,大体设计是后台分两级分类,设置好一级之后,再设置二级并选择栏目类型,如内容,列表,上传文件,新窗口等。这样就可以生成无限多个二级分类,也就是网站栏目。对于扩展性来说,如果有新的需求可以直接加一个栏目类型并新加功能操作
type Writer interface {
Write([]byte) (int, error)
}
func sendEmail(content string) error {
// 第三方发信逻辑
}
// 函数适配器:无需定义新结构体
type EmailWriter struct {
sendFunc func(string) error
}
func (e *EmailWriter) Write(p []byte) (int, error) {
err := e.sendFunc(string(p))
return len(p), err
}
// 使用
writer := &EmailWriter{sendEmail}
io.WriteString(writer, "alert: disk full")
注意:sendFunc 必须是可赋值、可测试的变量(不能是未导出包内函数),否则无法在单元测试中 mock。
最容易被忽略的陷阱:适配器不该有状态
适配器不是业务逻辑容器。一旦你在 FileLoggerAdapter 里加了缓冲池、计数器或配置缓存,它就不再是“适配器”,而成了“混合体”。后续替换实现(比如换成 CloudLoggerAdapter)时,这些状态逻辑就得重复搬移或重写。
真正该关注的,是参数怎么转(float64 → int)、错误怎么映射("timeout" → context.DeadlineExceeded)、默认值填在哪(to 收件人硬编码为 "admin@example.com")——这些才是适配器该干的事。









