golang中接口怎么用?

2023-08-07  本文已影响0人  护念

概念与用法

接口是一个go语言中特有的概念,我们该如何理解呢?
我们从现实生活中的例子着手来看,现实生活中有USB接口,USB是一种通用的接口协议,我们可以在手机、台式电脑、笔记本电脑等各种场景下方便使用,它和具体硬件无关。

go中的接口和上面USB接口有异曲同工之妙,只不过它约定的是结构体需要实现哪些方法,只要满足了这个接口中需要实现的方法,那么就可以归类为这个接口

接口的基本语法如下:


type ReadWriter interface {
    Read(b Buffer) bool    // 这里约定接口需要实现的方法 参数 返回值
    Write(b Buffer) bool
}

另外接口也是一种类型,定义好后我们可以像正常变量一样做为参数类型

PS: 接口名称在命名上有一个隐形规则,用xxer来命名,当然如果不太能用er结尾,用其它也是可以的

场景一:多态

多态是面向对象语言中一种常见用法

package main

import "fmt"

// 定义一个动物接口
type Animal interface {
    Speak() string // 约定需要有speak方法
}

// 狗结构体
type Dog struct{}

// dog 实现Speak方法
func (d *Dog) Speak() string {
    return "汪汪"
}

// 猫结构体
type Cat struct{}

// cat 实现Speak方法
func (c *Cat) Speak() string {
    return "喵喵"
}

func main() {
    animals := []Animal{&Dog{}, &Cat{}}
    for i, a := range animals {
        fmt.Println(i, a.Speak())
    }
}

// 输出
// 0 汪汪
// 1 喵喵

场景二:依赖注入

依赖注入指的是,底层的实现和高层的使用分开,通常用于使用层,实现层的解耦,真实项目中用的非常多

比如:

  1. 数据库可以使用mysql、pg、sqlite,但是对于上层数据存储并不关心使用具体是哪一个,可以将底层数据库抽象出来形成一个接口,底层每个数据库的代码实现相关接口就可以
  2. 又比如,发短信有很多商家阿里/云片等,但是上层对于发短信而言,不用关心具体使用哪一种去发短信,可以将发短信抽象成一个接口,底层去实现具体每家的相关接口

说起来比较抽象,我们看代码

package main

import "fmt"

// 信息发送接口
type MessageSender interface {
    SendMessage(message string) // 都需要有一个发送信息方法
}

// 接口公用通知函数
func Notify(sender MessageSender, message string) {
    sender.SendMessage(message) //
}

// 邮箱
type EmailSender struct{}

func (s *EmailSender) SendMessage(message string) {
    fmt.Println("send email: ", message)
}

// 短信
type SmsSender struct{}

func (s *SmsSender) SendMessage(message string) {
    fmt.Println("send sms: ", message)
}

func main() {
    smsSender := &SmsSender{}
    emailSender := &EmailSender{}

  // 将邮箱和 短信底层实发送进行了抽离
    Notify(smsSender, "这是短信")
    Notify(emailSender, "这是邮件")
}

// 输出
// send sms:  这是短信
// send email:  这是邮件

场景三:空接口

空接口也是项目中经常会遇到的,它主要用于代表参数可以是任意类型,这有点动态语言的味道了。

看代码

package main

import "fmt"

// 这里代表v可以是任意类型
func ProcessValue(val interface{}) {

    switch val.(type) {
    case string:
        fmt.Println("string:", val)
    case float64:
        fmt.Println("float64:", val)
    case bool:
        fmt.Println("bool:", val)
    case *Person:
        fmt.Println("*person:", val)
    default: // 注意这里是default 而不是else
        fmt.Println("unknow:", val)
    }
}

type Person struct{}

func main() {
    // 这里切片中存了任意类型值
    values := []interface{}{
        "hello world",
        234.355,
        false,
        12,
        &Person{},
    }

    for _, val := range values {
        ProcessValue(val)
    }
}

// 输出
// string: hello world
// float64: 234.355
// bool: false
// unknow: 12
// *person: &{}

场景四:接口嵌套

一个接口中可以嵌套另外一个接口,通常可以用于接口的扩展

package main

import "fmt"

// 定义基本接口
type Reader interface {
    Read() string
}

// 在基本接口的基础上定义扩展接口
type ReaderWithInfo interface {
    Reader // 这里嵌套了另外一个接口
    Info() string
}

// 实现基本接口
type MyDevice struct{}

func (d MyDevice) Read() string {
    return "Reading data..."
}

// 实现扩展接口
func (d MyDevice) Info() string {
    return "MyDevice is ready."
}

func main() {
    device := MyDevice{}

    // device 由于同时实现了Read和Info 因此可以同时即属于Reader接口 也属于 ReaderWithInfo接口
    var reader Reader = device
    var readerWithInfo ReaderWithInfo = device

    fmt.Println(reader.Read())

    fmt.Println(readerWithInfo.Read())
    fmt.Println(readerWithInfo.Info())
}

// 输出
// Reading data...
// Reading data...
// MyDevice is ready.
上一篇 下一篇

猜你喜欢

热点阅读