学习目标
从工程角度理解 **interface 在 Go 中的真实地位**
掌握 Go 接口的核心设计原则(尤其是“小接口”)
正确使用接口进行解耦,而不是制造复杂度
学会在真实项目中“该不该定义接口”的判断方法
一、为什么 interface 是 Go 的灵魂
解耦、扩展、测试、架构边界的核心工具
没有 interface 的 Go 项目:
– 依赖实现
– 难测试
– 难扩展
– 难演进
二、interface 的本质
1. interface 是“行为集合”
type Reader interface {
Read(p []byte) (n int, err error)
}说明:
- interface 只描述“能做什么”
- 不关心“怎么做”
2. 隐式实现(极其重要)
type FileReader struct{}
func (f *FileReader) Read(p []byte) (int, error) {
return 0, nil
}var r Reader = &FileReader{}结论:
实现者不需要知道接口的存在
三、小接口原则(Go 接口设计的核心)
1. 好接口的特征
- 方法少(1~3 个最理想)
- 语义清晰
- 名字来自“使用者视角”
2. 标准库示例
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}它们组合成:
type ReadWriter interface {
Reader
Writer
}结论:
小接口 + 组合 = 强表达力
四、不要“提前设计接口”
错误示例(新手常犯)
type UserService interface {
CreateUser()
UpdateUser()
DeleteUser()
GetUser()
}问题:
- 接口太大
- 不一定真的需要
- 强迫实现者实现无关方法
正确思路
接口应该由“调用方”定义,而不是实现方
五、接口是为“使用者”设计的
示例:错误的接口位置
// 在 service 包中定义
type UserRepo interface {
Save()
}正确做法:
// 在使用 repo 的地方定义
type UserSaver interface {
Save()
}结论:
谁依赖谁,接口就应该放在哪里
六、interface 与 struct 的关系
1. struct 是实现,interface 是能力
type EmailSender struct{}
func (e *EmailSender) Send(msg string) error {
return nil
}type Sender interface {
Send(msg string) error
}func Notify(s Sender) {
s.Send("hello")
}2. 面向接口编程
func NewService(sender Sender) *Service {
return &Service{sender: sender}
}结论:
不依赖具体类型,只依赖能力
七、interface 与指针的高频陷阱
1. 指针接收者必须用指针断言
type Foo struct{}
func (f *Foo) Do() {}
var i interface{} = &Foo{}_, ok1 := i.(Foo) // false
_, ok2 := i.(*Foo) // true2. nil interface 陷阱(极其重要)
var f *Foo = nil
var i interface{} = f
fmt.Println(i == nil) // false原因:
- interface = 类型 + 值
- 类型存在,接口就非 nil
八、interface 在测试中的巨大价值
1. 使用 mock 实现
type Sender interface {
Send(msg string) error
}
type MockSender struct{}
func (m *MockSender) Send(msg string) error {
fmt.Println("mock send:", msg)
return nil
}func TestNotify() {
Notify(&MockSender{})
}九、什么时候应该用 interface?
推荐使用 interface 的场景
- 多种实现
- 需要 mock
- 边界层(repo / adapter / client)
- 框架、插件系统
不推荐使用 interface 的场景
- 只有一个实现
- 非边界内部逻辑
- 数据结构(DTO / Model)
十、自检问题(进入 Day 6 前必须能回答)
- 为什么 Go 强调小接口?
- 接口应该由谁来定义?
- interface 和 struct 的边界在哪里?
- nil interface 为什么容易出 bug?
- 什么情况下“不要用 interface”?
预告:Day 6
Day 6|类型断言与 type switch(interface 的“解包”)
你将深入理解:
- interface 的运行期结构
- 类型断言为什么会 panic
- type switch 的工程价值
- 如何写“安全、不脆弱”的断言代码