Skip to content

Latest commit

 

History

History
150 lines (105 loc) · 4.24 KB

面向接口.md

File metadata and controls

150 lines (105 loc) · 4.24 KB

面向接口

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

type Traversal interface {
  Traverse()
}

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

duck typing

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

Go中的duck typing

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

接口的定义

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

    // 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中通过魔法函数实现)。

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

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

    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语言中都是值类型,所以定义变量的时候,值实际是应该有值的。

    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
    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}