Go 语言入门教程
接口组合与嵌入
本教程共 80 篇 · 第 45 篇 · 更新于 2026-07-27 · 约 6 分钟阅读
GoGo 入门教程接口组合嵌入接口嵌入
45. 接口组合与嵌入
本节目标:学会接口嵌入接口构建大接口,理解接口嵌入结构体的模式,掌握常见的组合设计。
接口嵌入接口
接口可以嵌入其他接口,把多个小接口组合成一个大接口:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// ReadWriter 组合了 Reader 和 Writer
type ReadWriter interface {
Reader
Writer
}
// ReadWriteCloser 组合了三个接口
type ReadWriteCloser interface {
Reader
Writer
Closer
}
满足 ReadWriteCloser 的类型必须同时实现 Read、Write、Close 三个方法。
这就是 Go 标准库 io 包的设计方式。io.ReadWriter、io.ReadWriteCloser 都是通过组合小接口得到的。
Note接口组合就是把多个接口的方法集合并起来。组合后的接口拥有所有被组合接口的方法。
小接口原则
Go 社区推崇「小接口」理念。标准库里最常用的接口都很小:
| 接口 | 方法数 | 说明 |
|---|---|---|
io.Reader | 1 | Read |
io.Writer | 1 | Write |
fmt.Stringer | 1 | String |
error | 1 | Error |
sort.Interface | 3 | Len/Less/Swap |
小接口的好处:
- 容易满足,一个类型实现一个方法就行
- 组合性强,需要大接口时拼起来
- 职责单一,语义清晰
Tip定义接口时问自己:这个接口能再拆小吗?如果方法超过 5 个,考虑拆成多个小接口。
接口嵌入结构体
接口不仅能嵌入接口,也能嵌入结构体。虽然不太常见,但在某些设计模式下有用:
package main
import "fmt"
type Logger interface {
Log(msg string)
}
type ConsoleLogger struct{}
func (c ConsoleLogger) Log(msg string) {
fmt.Println("[LOG]", msg)
}
// 结构体嵌入接口
type Service struct {
Logger // 嵌入 Logger 接口
Name string
}
func (s *Service) DoWork() {
s.Log("开始工作: " + s.Name)
// 干活...
s.Log("工作完成: " + s.Name)
}
func main() {
s := &Service{
Logger: ConsoleLogger{},
Name: "MyService",
}
s.DoWork()
// [LOG] 开始工作: MyService
// [LOG] 工作完成: MyService
}
嵌入接口后,结构体获得了接口的方法。但接口本身没有实现,需要在创建结构体时传入具体的实现。
Warning如果忘了赋值嵌入的接口,调用方法会 panic(nil 接口解引用):
s := &Service{Name: "oops"}
s.DoWork() // panic: nil pointer dereference
常见组合模式
装饰器模式
通过嵌入接口,可以在不修改原类型的情况下扩展功能:
package main
import "fmt"
type Notifier interface {
Send(msg string) error
}
type EmailNotifier struct{}
func (e EmailNotifier) Send(msg string) error {
fmt.Println("邮件:", msg)
return nil
}
// 装饰器:在发送前后加日志
type LogNotifier struct {
Notifier // 嵌入原接口
}
func (l LogNotifier) Send(msg string) error {
fmt.Println("[准备发送]")
err := l.Notifier.Send(msg) // 调用被装饰的方法
fmt.Println("[发送完成]")
return err
}
func main() {
var n Notifier = LogNotifier{EmailNotifier{}}
n.Send("你好")
// [准备发送]
// 邮件: 你好
// [发送完成]
}
LogNotifier 嵌入了 Notifier 接口,调用方无感知地获得了日志功能,但接口不变。
默认实现模式
结构体嵌入接口可以提供「默认实现」,子类型只需覆盖需要定制的方法:
type BaseHandler struct{}
func (b BaseHandler) Before() { fmt.Println("前置处理") }
func (b BaseHandler) After() { fmt.Println("后置处理") }
func (b BaseHandler) Handle() { fmt.Println("默认处理") }
type MyHandler struct {
BaseHandler // 嵌入,获得默认实现
}
// 只覆盖需要定制的方法
func (m MyHandler) Handle() {
fmt.Println("自定义处理")
}
func main() {
h := MyHandler{}
h.Before() // 前置处理(继承的)
h.Handle() // 自定义处理(覆盖的)
h.After() // 后置处理(继承的)
}
小结
接口组合和嵌入的要点:
- 接口嵌入接口 = 方法集合合并,构建大接口
- 小接口原则:接口越小越好,用组合构建复杂接口
- 接口嵌入结构体 = 依赖注入,运行时传入实现
- 嵌入接口可实现装饰器模式
下一章介绍 Go 标准库里最常用的几个接口。