Go 中 error 是内建接口 type error interface{ Error() string },所有实现该方法的类型都可作错误值;标准库 errors.New 返回 *errorString 实例,函数通常将 error 作为最后一个返回值,并用 if err != nil 判断。

func 类型比单方法接口更轻、更直接,除非你明确需要状态、组合或扩展性,否则别写 interface{ Do() error } —— 直接用 func() error。
什么时候该用 func() 而不是 interface{ Do() error }
判断标准非常实际:调用方只执行一次、不关心是谁做的、也不需要后续再问“它叫什么”或“还能不能重试”。
- 函数签名稳定且无副作用,比如
func(int) string或func() error - 测试时想快速传个闭包打桩,比如
app.Run = func() error { return nil } - 结构体字段只存行为逻辑,不带配置、连接池、日志器等附属状态
- 没有其他类型要共用这个契约(比如 HTTP/gRPC/本地三种实现都叫
Do)
interface{ Do() error } 真正值得写的三个信号
不是“将来可能加方法”,而是当前已有不可忽略的语义差异或约束。
- 实现体必须持有资源:比如一个
DBDelegate里封装了*sql.DB和重试策略 - 需要与其他接口组合:比如你的类型同时要满足
io.Closer和Doer,那Doer就得是接口才能嵌入 - 调用方要反射检查或动态加载:某些插件系统依赖接口名做路由,
func类型无法被reflect.TypeOf统一识别
避免 nil 接口 panic 的硬编码习惯
哪怕你决定用接口,也别让它的零值逃出构造函数——这是 Go 接口最常崩的点。
- 字段声明后,务必在 NewXXX 里赋值,不要留空:
delegate Doer→ 必须初始化为具体实现或默认空结构体 - 函数返回接口时,所有错误分支也要返回非 nil 值,
if err != nil { return nil, err }是危险写法 - 测试中注入 mock 时,确认它实现了全部方法;用
var _ Doer = (*MockDoer)(nil)在包级做编译期校验
命名和组合的实际边界
Go 社区用 -er 后缀不是为了好看,是为表达「谁在做这件事」。但别因此硬凑名字。
立即学习“go语言免费学习笔记(深入)”;
- 如果只有一个调用点只用
Read,就定义Reader;另一个地方同时用Read和Close,再组合ReadCloser - 别提前定义
Processor包含Process/Validate/Log—— 这些大概率不会一起被调用 - logr 的
Logger是特例:它看起来像单方法接口,但内部承载了层级、上下文、键值对等元信息,所以必须是接口
func 还是 interface,而是每次写新类型前,先看清楚调用方到底在用什么、怎么用、会不会变。接口不是设计出来的,是被用出来的。


















