0%

Go基础之面向接口

面向接口

golang是一门面向接口的语言,没有传统的继承和多态,golang的面向对象仅支持封装(通过定义结构实现,type struct_name struct),而继承和多态是通过接口来实现的。所以golang的接口要比其他语言灵活的多。

1
2
3
4
5
6
7
8
9
type Traversal interface {
Traverse()
}

func main(){
traversal := getTraversl()
traversal.Traverse()
}

duck typing

  • 描述事物的外部行为而非内部结构
  • 严格说go属于结构化类型系统,类似duck typing

Go中的duck typing

  • 同时能实现多个接口
  • 同时具有python, c++的duck typing的灵活性 — 只要实现了某个方法,就能被调用
  • 又具有Java的类型检查 — 不是动态语言的运行时检查,也不是静态语言的编译时才检查,而是在实现代码的过程中就知道必须要

接口的定义

  • 接口由使用者定义,而非传统的为某个被调用者实现某个方法,告诉其他调用者,我实现了该方法,你们可以调用我。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // main.go
    type Retriever interface {
    Get(url string) string // 注意:interface中定义函数不需要使用func关键字,会自动识别为函数,因为interface内部本身都是函数
    }

    func download(r Retriever) string {
    return r.Get("www.xxx.com")
    }
    // 以上为接口的定义

    func main() {
    var r Retriever // 此处的Retriever是上面的接口对象
    r = mock.Retriever{"this is a fack xxx.com"} // 此处可见,接口类型变量可以接受其实现类型的变量
    fmt.Println(download(r))
    }

    // mock包中定义实现接口的方法
    package mock

    // 实现接口
    type Retriever struct { // 注意,结构名要大写,因为是要在本包外被调用
    Contents string
    }

    func (r Retriever) Get(url string) string {
    return r.Contents
    }
    // 以上为接口的实现

小结

  • 接口的实现是隐式的

  • 只要实现接口里的方法,不需要指明说 我是实现了某个接口,只要实现了接口的(全部)方法即可

    上面的例子中,定义了含有Get方法的接口,并定义了一个使用该接口类型(Retriever类型)的函数,在该函数中调用了接口定义的Get方法

    实现接口的时候,只要实现了该接口中定义的方法之后,就能够被download函数使用,虽然该函数接受的是接口类型,但是是能够接受该接口类型的实现类型的。接口类型变量可以接受其实现类型的变量。

  • 和Python中的type typing类似的地方在于都是只要实现了某个方法,就能被调用(Python中通过魔法函数实现)。

  • 但是要注意,因为接口本身功能的要求,实现时,必须实现接口内定义的所有的类型

  • 可以有多个结构实现了同一个接口,一个结构也可以同时实现多个接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    type all struct {
    content string
    }

    func (a all) Post(s string) string{
    return "post"
    }

    func (a all) Get(s string) string {
    return "get"
    }

    func AllMethod(r Retriever, m MyTest) string {
    return r.Get("http://www.baidu.com") + " " + m.Post("success")
    }
    func main() {
    m := all{"aaa"}
    fmt.Println(AllMethod(m, m))
    }
    // 结果
    get post

    接口的值类型

    因为go语言中都是值类型,所以定义变量的时候,值实际是应该有值的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    func main() {
    var r Retriever // 此处的Retriever是上面的接口对象
    fmt.Printf("%T %v\n",r,r)
    r = mock.Retriever{"this is a fack xxx.com"}// 此处可见,接口类型变量可以接受其实现类型的变量
    fmt.Printf("%T %v\n",r,r)
    r = real.Retriever{
    UserAgent: "Mozilla/5.0",
    Timeout: time.Minute, // 一旦换行,每个k/v值后需要添加逗号
    }
    fmt.Printf("%T %v\n",r,r)
    fmt.Println(reflect.TypeOf(r))
    }

    //结果
    <nil> <nil>
    mock.Retriever {this is a fack xxx.com}
    real.Retriever {Mozilla/5.0 1m0s}
    real.Retriever
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    func (r *Retriever) Get(url string) string {

    }

    r = &real.Retriever{
    UserAgent: "Mozilla/5.0",
    Timeout: time.Minute, // 一旦换行,每个k/v值后需要添加逗号
    }
    fmt.Printf("%T %v\n",r,r)

    // 结果
    *real.Retriever &{Mozilla/5.0 1m0s}