每日一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 语言"组合优于继承"的设计哲学。

相关推荐
运维开发笔记7 小时前
5.2 Go 数组进阶学习笔记(多维、排序、搜索)
go
newerp14 小时前
Redis 操作与缓存策略
后端·程序员·go
newerp14 小时前
GORM ORM 基础
后端·程序员·go
newerp14 小时前
CRUD 操作与预处理语句
后端·程序员·go
程序员爱钓鱼17 小时前
Go 编程实战:闭包 Closure——函数如何记住外部变量
后端·google·go
橘bird1 天前
Go 语言学习教程 万字精讲(新手完整版)
go
用户330144867632 天前
08. mcentral:中心缓存的 span 管理
go
用户330144867632 天前
07. mcache.refill:缓存补充与 span 归还
go
程序员爱钓鱼2 天前
Go 编程实战:匿名函数 Anonymous Function——没有名字的函数与灵活回调
后端·面试·go
用户330144867633 天前
03. Size Class 分级机制
go