【Go笔记】接口与设计模式

接口的特征

  • 隐藏细节
  • 控制系统复杂性
  • 权限控制
  • 接口必须实现所有的方法

接口的使用

  • 隐形声明 - 即只要实现接口内的所有方法,则实现了该接口
  • 接口本身可以作为一个父类使用,即可以接受所有实现了该接口的数据类型,然后调用接口内定义了的方法
  • 一个类型可以同时实现多个接口
  • 鼓励使用组合而不是继承的方式来实现OOP
  • 使用s, ok := i.(Type)断言来获取结构内存储的类型与数据
  • 使用switch e.(Type)来断言空接口中的数据类型
  • 接口之间可以比较

接口的底层原理

  • 首先将类型的方法与接口的签名方法按照相同的方法排序,然后再来一一对应判断是否实现
  • 因为接口的结构体中的数据部分是一个指向内存数据的地址,所以当栈上的数据被分配到接口的时候,必然会在堆中开辟一个新的内存空间来存储,从而内存逃逸
  • 接口的调用成本比函数要大,但是依然可以忽略不计,且设计者鼓励开发者使用接口
  • 接口的调用开销主要是函数的寻址过程,其中涉及到CPU的分支预测导致的性能损耗。不过就算有性能损耗,不仅损耗极小,而且现代CPU都有缓存,常用接口及其对应函数已经被保存到L1缓存中供快速查询
  • 两个接口之间也可以转换,前提是来源接口的签名方法必须可以覆盖目标接口的签名方法
  • 空接口可以承载任何数据类型,但是有内存逃逸、eface创建、堆gc等性能损耗,相比接口要高不少,因此在某些高性能场景中需要被考虑到
  • 接口的仨陷阱
    • 当接口中存储的是值,结构体是指针的时候,动态调用无法通过编译
    • 将类型切片转换为接口切片时无法通过编译 - 因为批量转换接口是效率非常低的操作
    • 接口值为nil的时候,接口本身不为nil,比如
1
2
3
4
5
6
7
8
9
func do() *os.PathError {
return nil
}

func wrapDo() error {
return do()
}

wrapDo() == nil // false

总结

  • 接口的Go中的地位很重要,所有大规模程序设计都需要设计接口
  • 相比直接调用函数,接口会有内存逃逸,创建接口对象,gc等性能消耗,但是总体而言接口赋值次数很少,其性能损耗可以忽略不计
  • 接口决定了Go语言面向组合的程序设计模式,是与其他编程语言最显著的差别
  • 尽量以更少的方法签名来定义接口,通过组合的方式构造更加具体的结构

【Go笔记】接口与设计模式
https://study.0x535a.cn/go-note/go-interface-pattern/
Author
Stephen Zeng
Posted on
August 22, 2025
Licensed under