看到接口这两个字,咱们肯定会联想到面向接口编程。说白了就是接口指定执行对象的具体行为,也就是接口示意让执行对象具体应该做什么,所以,普遍意义上讲,接口是形象的,而理论执行行为,则是具象的。
接口(interface)的定义
在Go lang中,接口是一组办法签名,当类型为接口中的所有办法提供定义时,它被称为实现接口。和面向接口的思维十分相似,接口指定了类型应该具备的办法,类型决定了到底该怎么实现这些办法:
/* 定义接口 */ type interface_name interface { method_name1 [return_type] method_name2 [return_type] method_name3 [return_type] ... method_namen [return_type] } /* 定义构造体 */ type struct_name struct { /* variables */ } /* 实现接口办法 */ func (struct_name_variable struct_name) method_name1() [return_type] { /* 办法实现 */ } ... func (struct_name_variable struct_name) method_namen() [return_type] { /* 办法实现*/ }
具体实现形式:
package main import ( "fmt" ) type Phone interface { call() } type Android struct { } func (android Android) call() { fmt.Println("I am Android") } type Ios struct { } func (ios Ios) call() { fmt.Println("I am Ios") } func main() { var phone Phone phone = new(Android) phone.call() phone = new(Ios) phone.call() }
程序返回:
I am Android I am Ios
是的,当初咱们能够构造体、函数、以及接口三箭齐发了,这里首先定义好手机接口,并且指定call()办法,意思是我在形象层面领有一个手机,手机应该具备打电话的性能。
随后别离定义构造体和函数(也是办法),别离具现化的实现接口的指定行为,精力上大家是一样的,但肉体上,一个是安卓,另一个则是苹果。
Go lang中,接口能够被任意的对象实现,同样地,一个对象也能够实现任意多个接口,任意的类型都实现了空接口(interface{}),也就是蕴含0个method的interface。
诚然,如果独自应用构造体,咱们也能够,实现相似多态的构造:
package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string loan float32 } type Employee struct { Human //匿名字段 company string money float32 } //Human实现Sayhi办法 func (h Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } //Human实现Sing办法 func (h Human) Sing(lyrics string) { fmt.Println("。。。。。。。。", lyrics) } //Employee重写Human的SayHi办法 func (e Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) //Yes you can split into 2 lines here. }
能够独自为构造体定义方法,但如果接口参加逻辑:
type Men interface { SayHi() Sing(lyrics string) } func main() { mike := Student{Human{"Mike", 10, "1"}, "MIT", 0.00} paul := Student{Human{"Paul", 20, "2"}, "Harvard", 100} sam := Employee{Human{"Sam", 30, "3"}, "Golang Inc.", 1000} Tom := Employee{Human{"Tom", 40, "4"}, "Things Ltd.", 5000} //定义Men类型的变量i var i Men //i能存储Student i = mike fmt.Println("This is Mike, a Student:") i.SayHi() i.Sing("song") //i也能存储Employee i = Tom fmt.Println("This is Tom, an Employee:") i.SayHi() i.Sing("song") //定义了slice Men fmt.Println("Let's use a slice of Men and see what happens") x := make([]Men, 3) //T这三个都是不同类型的元素,然而他们实现了同一个接口 x[0], x[1], x[2] = paul, sam, mike for _, value := range x { value.SayHi() } }
程序返回:
This is Mike, a Student: Hi, I am Mike you can call me on 1 。。。。。。。。 song This is Tom, an Employee: Hi, I am Tom, I work at Things Ltd.. Call me on 4 。。。。。。。。 song Let's use a slice of Men and see what happens Hi, I am Paul you can call me on 2 Hi, I am Sam, I work at Golang Inc.. Call me on 3 Hi, I am Mike you can call me on 1
由此可见,接口的呈现,把原本不相干的构造体类型以形象的模式联合了起来,不同的类型实现内容不同的共性办法。
也就是说,Men接口类型的变量i,那么i外面能够存Human、Student或者Employee值,所以i是形象的,而Human、Student或者Employee就是i的具象化操作。
接口指定函数参数
接口不仅仅能够指定无参办法,也能够指定具体的参数,让函数承受各种类型的参数:
package main import "fmt" type Human interface { Len() } type Student interface { Human } type Test struct { } func (h *Test) Len() { fmt.Println("10个") } func main() { var s Student s = new(Test) s.Len() }
程序返回:
10个
这里应用接口嵌套的模式,Human接口定义了Len办法,构造体Test实现了所有的Len接口办法,当构造体s中调用Test构造体的时候,s就相当于Python中的继承,s继承了Test,因而,s能够不必重写所有的Human接口中的办法,因为父结构器曾经实现了接口。
鸭子类型(ducktyping)
什么是鸭子类型?当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。
所谓远看山有色,近听水无声,春去花还在,人来鸟不惊,意象上来讲,一个事物到底是不是某一种类型,取决于它具不具备这个类型的个性,这就是鸭子类型的实质。
所以鸭子类型次要形容事物的内部行为而非外部结构,在面向对象的编程语言中,比方Python中,一个对象无效的语义,不是由继承自特定的类或实现特定的接口,而是由"以后办法和属性的汇合"决定。
编写test.py文件:
class PsyDuck(): def gaga(self): print("这是可达鸭") # 应用的对象和办法 class DoningdDuck(): def gaga(self): print("这是唐老鸭") # 被调用的函数 def duckSay(func): return func.gaga() # 限度调用形式 if __name__ != '__main__': print("must __main__") if __name__ == "__main__": # 实例化对象 duck = PsyDuck() person = DoningdDuck() # 调用函数 duckSay(duck) duckSay(person)
程序返回:
这是可达鸭 这是唐老鸭
所以到底是什么鸭子不重要,重要的是调用了谁的实例。
再来看看go lang的手笔:
package main import "fmt" //定义一个鸭子接口 //Go 接口是一组办法的汇合,能够了解为形象的类型。它提供了一种非侵入式的接口。任何类型,只有实现了该接口中办法集,那么就属于这个类型。 type Duck interface { Gaga() } //假如当初有一个可达鸭类型 type PsyDuck struct{} //可达鸭申明办法-满足鸭子会嘎嘎叫的个性 func (pd PsyDuck) Gaga() { fmt.Println("this is PsyDuck") } //假如当初有一个唐老鸭类型 type DonaldDuck struct{} //唐老鸭申明办法-满足鸭子会嘎嘎叫的个性 func (dd DonaldDuck) Gaga() { fmt.Println("this is DoningdDuck") } //要调用的函数 - 负责执行鸭子能做的事件,留神这里的参数,有类型限度为Duck接口 func DuckSay(d Duck) { d.Gaga() } func main() { //提醒开始打印 fmt.Println("duck typing") //实例化对象 var pd PsyDuck //可达鸭类型 var dd DonaldDuck //唐老鸭类型 //调用办法 DuckSay(pd) //因为可达鸭实现了所有鸭子的函数,所以能够这么用 DuckSay(dd) //因为唐老鸭实现了所有鸭子的函数,所以能够这么用 }
程序返回:
duck typing this is PsyDuck this is DoningdDuck
这里首先定义形象的鸭子接口,指定gaga办法,不同的构造体:可达鸭、唐老鸭别离绑定并且实现了鸭子接口的办法,而后申明一个调用函数,在执行的时候,将构造体变量传递给调用函数,动静地实现了不同类型的办法。
结语
所谓接口(interface)的抽象性,就是从外表看到实质,从全面看到整体,而后抽出那些稳固的、共有的个性。平时咱们会思考代码的重用性,组件的复用性,同一个性能对不同场景的复用性,有了复用的能力,就可能用更少的开发去满足更多场景的同类需要问题。从而可能从一个具体的需要,看到一类的需要,看到衍生的相干的需要,甚至再对需要进行分类,看到更高层面的需要。进而才可能系统性解决同类的需要而不是就事论事点对点解决问题。
所以,总的来说,接口的极致就是形象,而形象的极致,则是格局,接口,能够更好的帮咱们扩充程序视线的格局。