『No8: Go 接口』

释放双眼,带上耳机,听听看~!

『No8: Go 接口』

golang-learning-eight.png

『No8: Go 接口』

8.png

大家好,我是谢伟,是一名程序员。

下面的学习是一个系列,力求从初学者的角度学会go 语言,达到中级程序员水平。

这一系列是我的输出总结,同时我还推出了视频版。正在制作过程。

为写出这些文章,我阅读了网上诸多热门的教程和纸质书籍。内容的实质都是那些,要区分出差异的话,只能表现在具体实例层面。所以,实例我会选取自己在工作中的项目实例抽取出来。希望对大家有所帮助。

我们已经研究了:

本节的主题是:接口

接口是 golang 中最值得强调的特性。它让面向对象,内容组织实现非常的方便。


  1. 接口的定义

  2. 接口的使用:赋值、嵌入接口

  3. 内置常用接口

  4. 任意(Any)类型、类型判断

1. 接口的定义

接口在 go 语言中是一系列方法的集合,原则上方法可以有很多个,但建议4个左右。

type HttpClient interface {
    Get(string) ([]byte, error)
    Post(string, map[string]interface{}) ([]byte, error)
    Put(string, map[string]interface{}) ([]byte, error)
    Delete(string) (int, error)
}
  • type 和 interface 关键字组成 接口定义

  • 接口是一系列方法的合集

  • 方法参数和返回值只需指定类型,无需参数名称

  • 只定义要干什么,而不关注实现

上文中定义了一个 httpClient 的接口,指定了这个接口可以干这些活:Get、Post、Put、Delete

2. 接口的使用:赋值、嵌入接口

上文中指定了 httpClient 接口,指定了这个接口需要干的活是:Get、Post、Put、Delete, 具体的实现需要靠其他结构体来实现。

type HttpImpl struct {
}

func (this *HttpImpl) Get(url string) ([]byte, error) {

    var (
        resq *http.Response
        err  error
    )    if resq, err = http.Get(url); err != nil {        return nil, ErrorHttpNil
    }

    defer resq.Body.Close()

    var (
        resp []byte
    )    if resp, err = ioutil.ReadAll(resq.Body); err != nil {        return nil, ErrorByteNil
    }    return resp, nil}

func (this *HttpImpl) Post(url string, body map[string]interface{}) ([]byte, error) {    return nil, nil}
func (this *HttpImpl) Put(url string, body map[string]interface{}) ([]byte, error) {    return nil, nil}
func (this *HttpImpl) Delete(url string) (int, error) {    return 0, nil}
  • 上文定义一个结构体 httpImpl, 这个结构体存在Get、Post、Put、Delete 四个方法,参数和返回值的类型和步骤一定义的接口的方法的参数和返回值一致。

  • 上文定义的结构体 httpImpl 还可以有其他的方法,但不重要,重要的是存在接口中定义的那四个方法

  • 那么我们就说这个结构体 httpImpl 实现了接口 HttpClient

一个结构体实现了接口要求的所有的方法(方法的参数和返回值一致),那么就说这个结构体实现了这个接口

接口可以屏蔽内部细节,和具体的实现方法。只关注我需要做什么,而不关注怎么做。

func main() {
    var httpImpl = &HttpImpl{} // 结构体

    var httpClient HttpClient // 接口

    httpClient = httpImpl // 接口赋值

    responseOne, _ := httpClient.Get("https://www.jianshu.com/u/58f0817209aa")
    fmt.Println(string([]byte(responseOne)))

}

上文中的使用:httpClient 屏蔽了 httpImpl 的内部细节,而依然可以使用 Get 方法,去完成任务。

当然接口可以被诸多结构体实现,只需存在接口定义的几种方法即可。

接口和结构体的定义很相似,也可以完成嵌入接口的功能,嵌入的匿名的接口,可以自动的具备被嵌入的接口的方法。

type InterHttpClient interface {
    HttpClient
}func main() {
    var httpImpl = &HttpImpl{}
    var httpClient HttpClient
    httpClient = httpImpl
    responseOne, _ := httpClient.Get("https://www.jianshu.com/u/58f0817209aa")
    fmt.Println(string([]byte(responseOne)))    // interHttpClient

    var interHttpClient InterHttpClient
    interHttpClient = httpImpl
    responseTwo, _ := interHttpClient.Get("http://www.gitbub.com")
    fmt.Println(string([]byte(responseTwo)))

}
  • InterHttpClient 嵌入接口 HttpClient , 而自动具备方法:Get、Post、Put、Delete

  • 同时 HttpImpl 结构体也实现了接口InterHttpClient

3. 内置常用接口

type Stringer interface {
    String() string
}

结构体实现 String 方法即可实现结构化输出结构体。

type error interface {
    Error() string
}

实现Error 方法即可自定义错误类型。

type Reader interface {
    Read(p []byte) (n int, err error)
}
type Writer interface {
    Write(p []byte) (n int, err error)
}
type ReadWriter interface {
    Reader
    Writer
}

这几个读写接口在好些库中实现了,后续我们再讨论。

4. 任意(Any)类型、类型断言

Any 类型

interface{}

空接口在 go 里,可以当成任意类型,意味着,比如你的函数或者方法不知道传入的参数的类型,可以直接定义为 interface{}

func Say(name interface{}) {
    fmt.Println(name)
}

类型断言

类型断言的使用场景是:接口类型的变量可以包含任何类型的值。如何判断变量的真实类型?

比如解析一个不知道字段类型的 json, 常常需要使用到类型断言。

可以使用:

  1. ok…idiom 模式进行类型判断

  2. switch… case 推断匹配

ok…idiom

varInterface.(T), varInterface 必须是接口、T 则是具体的实现接口的结构体

func main() {
    var httpImpl = &HttpImpl{}
    var httpClient HttpClient
    httpClient = httpImpl
    responseOne, _ := httpClient.Get("https://www.jianshu.com/u/58f0817209aa")
    fmt.Println(string([]byte(responseOne)))    // interHttpClient

    var interHttpClient InterHttpClient
    interHttpClient = httpImpl
    responseTwo, _ := interHttpClient.Get("http://www.gitbub.com")
    fmt.Println(string([]byte(responseTwo)))    // ok..idiom

    if n, ok := httpClient.(*HttpImpl); ok {
        fmt.Println(n)

    }    if m, ok := interHttpClient.(*HttpImpl); ok {
        fmt.Println(m)
    }

}

switch ..case…

.(type) 只在 switch 语句里才能使用。

func Show(param interface{}) {    switch param.(type) {    case *HttpImpl:
        fmt.Println("1")    default:
        fmt.Println("0")
    }
}

func main() {    var httpImpl = &HttpImpl{}    var httpClient HttpClient
    httpClient = httpImpl
    responseOne, _ := httpClient.Get("https://www.jianshu.com/u/58f0817209aa")
    fmt.Println(string([]byte(responseOne)))    // interHttpClient

    var interHttpClient InterHttpClient
    interHttpClient = httpImpl
    responseTwo, _ := interHttpClient.Get("http://www.gitbub.com")
    fmt.Println(string([]byte(responseTwo)))

    Show(httpClient) // 1
    Show(interHttpClient) // 1

以上就是接口的全部内容,接口是go 中最特别的特性。借助 接口, go 实现面向对象中的继承和多态。

接口是方法的集合,只定义具体要干什么,而怎么干,则由其他的结构体的方法实现。这样不同的结构体的方法的具体处理不同,实现的接口的功能就不一样。

尽管如此,接口并不意味着可以随意滥用。我们最好是根据面向对象的客观实体,抽象出接口和方法。

本节完,再会。

作者:谢小路
链接:https://www.jianshu.com/p/d1153a222cf7

【转自慕课】https://www.imooc.com

Go

想转行Go语言?机会终于来了!

2022-3-3 5:39:10

Go

go语言初体验(流程控制、range遍历、函数、结构体、面向对象)

2022-3-3 5:39:45

搜索