每日一Go-7、Go语言接口(Interface)

在 Go 语言中,接口(Interface)是一种强大的类型,它通过隐式实现和鸭子类型(Duck Typing)机制,为多态和代码抽象提供了独特而灵活的解决方案。

鸭子类型(Duck Typing)是一种动态类型风格,它判断一个对象的有效性,不是看它的继承关系或实现的接口,而是看它是否拥有特定方法和属性的集合。这个概念来源于"鸭子测试":"如果它走起路来像鸭子,叫起来也像鸭子,那么它就是一只鸭子。" 换句话说,它关注对象的行为(能做什么),而不是对象的类型。

1、接口定义与隐式实现

Go 语言的接口是一种抽象类型,它定义了一组方法签名的集合,但不包含具体实现。任何类型只要实现了接口中声明的所有方法,就被视为隐式实现了该接口,无需像 Java 那样使用 implements关键字显式声明。

go 复制代码
// 定义汽车接口
type Car interface {
    // 引擎启动
    EngineStart() string
    // 引擎关闭
    TurnOff() string
    // 加速
    Accelerate() string
    // 刹车
    Brake() string
    // 按喇叭
    SoundHorn() string
    // 大灯打开
    TurnOnHeadlights() string
    // 大灯关闭
    TurnOffHeadlights() string
    // 手刹
    HandBrake() string
}

2、具体类型实现接口

下面是'别摸我'和'本厮'对汽车接口的隐式实现:

go 复制代码
// '别摸我'汽车
type Bmw struct {
    Name string
}
func (b Bmw) EngineStart() string {
    return "别摸我" + b.Name + "引擎启动"
}
func (b Bmw) TurnOff() string {
    return "别摸我" + b.Name + "引擎关闭"
}
func (b Bmw) Accelerate() string {
    return "别摸我" + b.Name + "加速"
}
func (b Bmw) Brake() string {
    return "别摸我" + b.Name + "刹车"
}
func (b Bmw) SoundHorn() string {
    return "别摸我" + b.Name + "按喇叭"
}
func (b Bmw) TurnOnHeadlights() string {
    return "别摸我" + b.Name + "大灯打开"
}
func (b Bmw) TurnOffHeadlights() string {
    return "别摸我" + b.Name + "大灯关闭"
}
func (b Bmw) HandBrake() string {
    return "别摸我" + b.Name + "手刹"
}
// '本厮'汽车
type Benz struct {
    Name string
}
func (b Benz) EngineStart() string {
    return "本厮" + b.Name + "引擎启动"
}
func (b Benz) TurnOff() string {
    return "本厮" + b.Name + "引擎关闭"
}
func (b Benz) Accelerate() string {
    return "本厮" + b.Name + "加速"
}
func (b Benz) Brake() string {
    return "本厮" + b.Name + "刹车"
}
func (b Benz) SoundHorn() string {
    return "本厮" + b.Name + "按喇叭"
}
func (b Benz) TurnOnHeadlights() string {
    return "本厮" + b.Name + "大灯打开"
}
func (b Benz) TurnOffHeadlights() string {
    return "本厮" + b.Name + "大灯关闭"
}
func (b Benz) HandBrake() string {
    return "本厮" + b.Name + "手刹"
}
func main() {
    bmw := Bmw{Name: "X100"}
    fmt.Println(bmw.EngineStart())
    fmt.Println(bmw.TurnOnHeadlights())
    fmt.Println(bmw.SoundHorn())
    fmt.Println(bmw.Accelerate())
    fmt.Println(bmw.Brake())
    fmt.Println(bmw.HandBrake())
    fmt.Println(bmw.TurnOffHeadlights())
    fmt.Println(bmw.TurnOff())
    benz := Benz{Name: "S1000"}
    fmt.Println(benz.EngineStart())
    fmt.Println(benz.TurnOnHeadlights())
    fmt.Println(benz.SoundHorn())
    fmt.Println(benz.Accelerate())
    fmt.Println(benz.Brake())
    fmt.Println(benz.HandBrake())
    fmt.Println(benz.TurnOffHeadlights())
    fmt.Println(benz.TurnOff())
}
//输出:
// 别摸我X100引擎启动
// 别摸我X100大灯打开
// 别摸我X100按喇叭
// 别摸我X100加速
// 别摸我X100刹车
// 别摸我X100手刹
// 别摸我X100大灯关闭
// 别摸我X100引擎关闭
// 本厮S1000引擎启动
// 本厮S1000大灯打开
// 本厮S1000按喇叭
// 本厮S1000加速
// 本厮S1000刹车
// 本厮S1000手刹
// 本厮S1000大灯关闭
// 本厮S1000引擎关闭

注意 :Bmw和 Benz类型并没有在任何地方声明它们实现了 Car接口,但 Go 编译器会自动识别它们满足了接口的要求

3、接口实现多态

多态允许我们使用统一的接口类型来处理不同的具体类型。以下是 Car 接口的多态应用:

go 复制代码
// 多态函数,驾驶任何一辆车
func DriveACar(c Car) {
    fmt.Println(c.EngineStart())
    fmt.Println(c.TurnOnHeadlights())
    fmt.Println(c.SoundHorn())
    fmt.Println(c.Accelerate())
    fmt.Println(c.Brake())
    fmt.Println(c.HandBrake())
    fmt.Println(c.TurnOffHeadlights())
    fmt.Println(c.TurnOff())
}
// 多态函数,车辆秀场
func CarShow(cars []Car) {
    for _, car := range cars {
        DriveACar(car)
    }
}
func main() {
    bmw := Bmw{Name: "X200"}
    //单一车辆的多态调用
    DriveACar(bmw)
    benz := Benz{Name: "S2000"}
    //单一车辆的多态调用
    DriveACar(benz)
    // 车辆集合的多态处理
    cars := []Car{bmw, benz}
    CarShow(cars)
}

当调用 DriveACar(bmw)时,Go 语言会在运行时自动确定bmw的具体类型,并调用其对应的 EngineStart(),...,HandBrake()方法,这就是动态分发机制。

4、接口的高级特性

  • 4.1 接口组合

鼓励把接口定义得小而美,然后通过组合构建复杂接口

go 复制代码
type EngineSystem interface {
    // 引擎启动
    EngineStart() string
    // 引擎关闭
    TurnOff() string
    // 加速
    Accelerate() string
}
type BreakSystem interface {
    // 刹车
    Brake() string
    // 手刹
    HandBrake() string
}
type LightSystem interface {
    // 大灯打开
    TurnOnHeadlights() string
    // 大灯关闭
    TurnOffHeadlights() string
}
type Car interface {
    EngineSystem
    BreakSystem
    LightSystem
    // 按喇叭
    SoundHorn() string
}
  • 4.2 空接口 空接口 interface{}可以容纳任何类型,常用于需要处理未知类型的场景
go 复制代码
func JudgeAnyType(i interface{}) {
    // 类型断言
    if car, ok := i.(Car); ok {
        fmt.Println("这是一辆车,可以驾驶")
        DriveACar(car)
        return
    }
    if str, ok := i.(string); ok {
        fmt.Println("这是个字符串", str)
    }
}
func JudgeAnyTypeWay2(i interface{}) {
    // 类型开关(type switch)
    switch v := i.(type) {
    case Car:
        fmt.Println("这是一辆车,可以驾驶")
        DriveACar(v)
    case string:
        fmt.Println("这是个字符串", v)
    case int:
        fmt.Println("这是个整数", v)
    default:
        fmt.Println("无法识别的类型")
    }
}

5、接口的核心作用与设计哲学

  • 解耦与抽象:接口在调用方和实现方之间建立了抽象屏障,使得代码模块之间的依赖关系更加松散。例如,你可以定义一个 DataStore接口,然后为其提供 MySQL、Mongo或内存存储等不同实现,而业务逻辑代码无需修改。
  • 可测试性:通过接口,我们可以轻松创建模拟对象(mock)进行单元测试。例如,测试一个需要数据库操作的函数时,可以传入一个实现了数据库接口的内存模拟对象,而不是真实的数据库连接。
  • 可扩展性:当系统需要添加新功能时,只需创建实现了相应接口的新类型,无需修改现有代码。这种对扩展开放、对修改封闭的设计符合开闭原则。

6、实践时间

把小米su7也加进来试试,源码如下:

go 复制代码
package main
import "fmt"
type EngineSystem interface {
    // 引擎启动
    EngineStart() string
    // 引擎关闭
    TurnOff() string
    // 加速
    Accelerate() string
}
type BreakSystem interface {
    // 刹车
    Brake() string
    // 手刹
    HandBrake() string
}
type LightSystem interface {
    // 大灯打开
    TurnOnHeadlights() string
    // 大灯关闭
    TurnOffHeadlights() string
}
type Car interface {
    EngineSystem
    BreakSystem
    LightSystem
    // 按喇叭
    SoundHorn() string
}
// '别摸我'汽车
type Bmw struct {
    Name string
}
func (b Bmw) EngineStart() string {
    return "别摸我" + b.Name + "引擎启动"
}
func (b Bmw) TurnOff() string {
    return "别摸我" + b.Name + "引擎关闭"
}
func (b Bmw) Accelerate() string {
    return "别摸我" + b.Name + "加速"
}
func (b Bmw) Brake() string {
    return "别摸我" + b.Name + "刹车"
}
func (b Bmw) SoundHorn() string {
    return "别摸我" + b.Name + "按喇叭"
}
func (b Bmw) TurnOnHeadlights() string {
    return "别摸我" + b.Name + "大灯打开"
}
func (b Bmw) TurnOffHeadlights() string {
    return "别摸我" + b.Name + "大灯关闭"
}
func (b Bmw) HandBrake() string {
    return "别摸我" + b.Name + "手刹"
}
// '本厮'汽车
type Benz struct {
    Name string
}
func (b Benz) EngineStart() string {
    return "本厮" + b.Name + "引擎启动"
}
func (b Benz) TurnOff() string {
    return "本厮" + b.Name + "引擎关闭"
}
func (b Benz) Accelerate() string {
    return "本厮" + b.Name + "加速"
}
func (b Benz) Brake() string {
    return "本厮" + b.Name + "刹车"
}
func (b Benz) SoundHorn() string {
    return "本厮" + b.Name + "按喇叭"
}
func (b Benz) TurnOnHeadlights() string {
    return "本厮" + b.Name + "大灯打开"
}
func (b Benz) TurnOffHeadlights() string {
    return "本厮" + b.Name + "大灯关闭"
}
func (b Benz) HandBrake() string {
    return "本厮" + b.Name + "手刹"
}
type XiaomiCar struct {
    Name string
}
func (b XiaomiCar) EngineStart() string {
    return "小米" + b.Name + "引擎启动"
}
func (b XiaomiCar) TurnOff() string {
    return "小米" + b.Name + "引擎关闭"
}
func (b XiaomiCar) Accelerate() string {
    return "小米" + b.Name + "加速"
}
func (b XiaomiCar) Brake() string {
    return "小米" + b.Name + "刹车"
}
func (b XiaomiCar) SoundHorn() string {
    return "小米" + b.Name + "按喇叭"
}
func (b XiaomiCar) TurnOnHeadlights() string {
    return "小米" + b.Name + "大灯打开"
}
func (b XiaomiCar) TurnOffHeadlights() string {
    return "小米" + b.Name + "大灯关闭"
}
func (b XiaomiCar) HandBrake() string {
    return "小米" + b.Name + "手刹"
}
// 多态函数,驾驶任何一辆车
func DriveACar(c Car) {
    fmt.Println(c.EngineStart())
    fmt.Println(c.TurnOnHeadlights())
    fmt.Println(c.SoundHorn())
    fmt.Println(c.Accelerate())
    fmt.Println(c.Brake())
    fmt.Println(c.HandBrake())
    fmt.Println(c.TurnOffHeadlights())
    fmt.Println(c.TurnOff())
}
// 多态函数,车辆秀场
func CarShow(cars []Car) {
    for _, car := range cars {
        DriveACar(car)
    }
}
func JudgeAnyType(i interface{}) {
    // 类型断言
    if car, ok := i.(Car); ok {
        fmt.Println("这是一辆车,可以驾驶")
        DriveACar(car)
        return
    }
    if str, ok := i.(string); ok {
        fmt.Println("这是个字符串", str)
    }
}
func JudgeAnyTypeWay2(i interface{}) {
    // 类型开关(type switch)
    switch v := i.(type) {
    case Car:
        fmt.Println("这是一辆车,可以驾驶")
        DriveACar(v)
    case string:
        fmt.Println("这是个字符串", v)
    case int:
        fmt.Println("这是个整数", v)
    default:
        fmt.Println("无法识别的类型")
    }
}
func main() {
    bmw := Bmw{Name: "X200"}
    //单一车辆的多态调用
    DriveACar(bmw)
    benz := Benz{Name: "S2000"}
    //单一车辆的多态调用
    DriveACar(benz)
    su7 := XiaomiCar{Name: "SU7"}
    DriveACar(su7)
    // 车辆集合的多态处理
    cars := []Car{bmw, benz, su7}
    CarShow(cars)
}

掌握 Go 接口的正确使用方法是编写高质量、可维护 Go 代码的关键,它体现了 Go 语言"组合优于继承"的设计哲学。

相关推荐
SomeBottle10 小时前
【小记】解决校园网中不同单播互通子网间 LocalSend 的发现问题
计算机网络·go·网络编程·学习笔记·计算机基础
且去填词17 小时前
深入理解 GMP 模型:Go 高并发的基石
开发语言·后端·学习·算法·面试·golang·go
大厂技术总监下海1 天前
向量数据库“卷”向何方?从Milvus看“全功能、企业级”的未来
数据库·分布式·go·milvus·增强现实
冷冷的菜哥1 天前
go(golang)调用ffmpeg对视频进行截图、截取、增加水印
后端·golang·ffmpeg·go·音视频·水印截取截图
Grassto1 天前
深入 `modload`:Go 是如何加载并解析 module 的
golang·go·go module
帅猛的Shic2 天前
Kubernetes Service深度解析:为什么Pod需要稳定接入点
kubernetes·go
molaifeng2 天前
Token:AI 时代的数字货币——从原理到计费全解
人工智能·ai·大模型·llm·go·token
天天进步20153 天前
KrillinAI 源码级深度拆解四: 告别违和感:深度剖析 KrillinAI 中的 Lip-sync 唇形对齐技术实现
go
用户1296157358554 天前
Go语言云原生与微服务架构终极实践指南
go
踏浪无痕4 天前
Go 的协程是线程吗?别被"轻量级线程"骗了
后端·面试·go