首页 / Go 语言入门教程 / 接口组合与嵌入

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 的类型必须同时实现 ReadWriteClose 三个方法。

这就是 Go 标准库 io 包的设计方式。io.ReadWriterio.ReadWriteCloser 都是通过组合小接口得到的。

Note

接口组合就是把多个接口的方法集合并起来。组合后的接口拥有所有被组合接口的方法。

小接口原则

Go 社区推崇「小接口」理念。标准库里最常用的接口都很小:

接口方法数说明
io.Reader1Read
io.Writer1Write
fmt.Stringer1String
error1Error
sort.Interface3Len/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 标准库里最常用的几个接口。