接口
golang中的接口设计非常出色,因为它解耦了接口和实现类之间的联系,使得进一步增加了我们编码的灵活度,解决了供需关系颠倒的问题。但是世上没有绝对的好坏,golang中的接口在方便了我们编码的同时也带来了一些问题,比如说由于没了接口和实现类的强绑定,其实也一定程度上增加了开发和维护的成本。
总体来说这是一个仁者见仁的改动,有些写惯了Java的同学可能会觉得没有必要,这是过度解绑,有些人之前深受其害的同学可能觉得这个进步非常关键。但不论你怎么看,这都不影响我们学习它,毕竟学习本身是不带立场的。
接口本身就是一种规范,能让大家在一个框架下开发,比如张三新进入部门,开发一个新的支付功能,在接口的限制下,开发就会会规范很多。就像USB接口一样,定义统一接口,无论外部实现的是音响还是硬盘,必须都按定义好的数据格式开发。
Go的接口语法
基本语法
在 Golang 中,interface 是一组 method 的集合,是 duck-type programming 的一种体现。不关心属性(数据),只关心行为(方法)。具体使用中你可以自定义自己的 struct,并提供特定的 interface 里面的 method 就可以把它当成 interface 来使用。
每个接口由数个方法组成,接口的定义格式如下:
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
其中:
- 接口名:使用
type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。 - 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
实现接口的条件
一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。
我们来定义一个Animal接口:
// Animal 接口
type Animal interface {
sleep()
}
定义Dog和Cat两个结构体:
type Dog struct {
name string
}
type Cat struct {
name string
}
因为Animal接口里只有一个sleep方法,所以我们只需要给Dog和Cat类分别实现sleep方法就可以实现Sayer接口了。
// Dog实现了Animal接口
func (d Dog) sleep() {
fmt.Printf("%s正在侧卧着睡觉\n", d.name)
}
// Cat实现了Animal接口
func (c Cat) sleep() {
fmt.Printf("%s正在卷成团睡觉\n", c.name)
}
接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。
接口类型变量
那实现了接口有什么用呢?
接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Animal类型的变量能够存储Dog和Cat类型的变量。
func foo(animal Animal) {
animal.sleep()
}
func main() {
var a Animal
var d = Dog{"川普"}
var c = Cat{"拜登"}
// 案例1
a = d
a.sleep()
a = c
a.sleep()
// 案例2
foo(d)
foo(c)
}