关于 go-interfaces
面向 Go 开发者的接口设计技能,涵盖类型断言、嵌入和接收者选择,并提供检查接口合规性的脚本与参考资料。
主要功能
- 01由使用方定义接口
- 02具体类型与接口返回建议
- 03安全类型断言与类型分支
- 04嵌入与接收者类型建议
- 05编译时接口检查
适用场景
- 01设计包 API 和构造函数
- 02检查类型是否满足接口
- 03审查公开结构体中的嵌入
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cxuu/ golang-skills --skill go-interfaces安装后在 Claude Code 中输入/go-interfaces确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/EMBEDDING.mdRECEIVER-TYPE.mdscripts/check-interface-compliance.gocheck-interface-compliance.sh详细介绍
Go 接口与组合
资源路由
scripts/check-interface-compliance.sh- 作为启发式检查运行,找出可能需要编译时断言的导出接口。scripts/check-interface-compliance.go- 由check-interface-compliance.sh调用的实现辅助程序;更改方法集分析时修改此文件。references/EMBEDDING.md- 在公共 API 中嵌入接口或结构体时阅读。references/RECEIVER-TYPE.md- 指针/值接收者影响接口满足条件时阅读。
接受接口,返回具体类型
接口应属于使用值的包,而不是实现接口的包。构造函数应返回具体类型(通常是指针或结构体),这样添加新方法时无需重构。
// Good: consumer defines the interface it needs
package consumer
type Thinger interface { Thing() bool }
func Foo(t Thinger) string { ... }
// Good: producer returns concrete type
package producer
type Thinger struct{ ... }
func (t Thinger) Thing() bool { ... }
func NewThinger() Thinger { return Thinger{ ... } }
// Bad: producer defines and returns its own interface
package producer
type Thinger interface { Thing() bool }
type defaultThinger struct{ ... }
func NewThinger() Thinger { return defaultThinger{ ... } }
不要在使用接口之前定义接口。 如果没有真实的使用示例,就很难判断接口是否真的有必要。
通用性:隐藏实现,公开接口
如果某个类型的存在只是为了实现接口,且除该接口外没有其他导出方法,那么构造函数应返回接口,以隐藏实现:
func NewHash() hash.Hash32 {
return &myHash{} // unexported type
}
优点:实现可以更改而不影响调用方;替换算法只需更改构造函数调用。
类型断言:逗号 ok 惯用法
如果不进行检查,断言失败会导致运行时 panic。务必使用逗号 ok 惯用法进行安全测试:
str, ok := value.(string)
if ok {
fmt.Printf("string value is: %q\n", str)
}
检查某个值是否实现了接口:
if _, ok := val.(json.Marshaler); ok {
fmt.Printf("value %v implements json.Marshaler\n", val)
}
类型 switch
惯用做法是重复使用变量名(t := t.(type))——在每个 case 分支中,该变量都具有正确的类型。如果一个 case 列出多种类型(case int, int64:),该变量的类型就是接口类型。
嵌入
避免在公共结构体中嵌入类型——内部类型的完整方法集会成为公共 API 的一部分。请改用非导出字段。
接口满足性检查
使用空白标识符赋值,在编译时验证某个类型是否实现了接口:
var _ json.Marshaler = (*RawMessage)(nil)
如果 *RawMessage 未实现 json.Marshaler,这会导致编译错误。
在以下情况下使用此模式:
- 没有静态转换可自动验证接口
- 类型必须满足某个接口才能确保行为正确(例如,自定义 JSON 编码)
- 接口发生更改时应导致编译失败,而不是悄然降级
不要为每个接口都添加此类检查——只在没有其他静态转换可以捕获错误时添加。
验证:定义接口或实现后,运行
bash scripts/check-interface-compliance.sh,以确认所有具体类型都有编译时var _ I = (*T)(nil)检查。
接收者类型
如果不确定,请使用指针接收者。不要在同一个类型上混用接收者类型——如果有任何方法需要指针,就让所有方法都使用指针。仅对小型不可变类型(Point、time.Time)或基本类型使用值接收者。
快速参考
| 概念 | 模式 | 说明 |
|---|---|---|
| 接口由使用方定义 | 在使用处定义接口 | 不要在实现接口的包中定义 |
| 安全的类型断言 | v, ok := x.(Type) | 返回零值 + false |
| 类型 switch | switch v := x.(type) | 变量在每个 case 中具有正确的类型 |
| 接口嵌入 | type RW interface { Reader; Writer } | 方法的并集 |
| 结构体嵌入 | type S struct { *T } | 提升 T 的方法 |
| 接口检查 | var _ I = (*T)(nil) | 编译时验证 |
| 通用性 | 构造函数返回接口 | 隐藏实现 |
相关技能
- 接口命名:命名接口(
-er后缀约定)或选择接收者名称时,请参阅 go-naming - 错误类型:实现
error接口、自定义错误类型或使用errors.As匹配时,请参阅 go-error-handling - 泛型与接口:判断是否需要泛型,或接口是否已足够时,请参阅 go-generics
- 函数选项:使用基于接口的 Option 模式来实现灵活的构造函数时,请参阅 go-functional-options
- 防御性边界:当接口断言只是更广泛的 API 边界加固工作的一部分时,请参阅 go-defensive