开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
为什么要使用接口
实现类(在GO中是结构体)和接口的分离,减少耦合!例如你有好几个抽象出来的汽车结构体(如Tesla、BMW、Aodi),每个结构体都有Drive方法,但是这些Drive方法的内部实现是不同的。按照直觉,我们可以定义Tesla_Drive、BMW_Drive、Aodi_Drive来实现不同结构体的Drive方法。不太好!汽车组装者需要根据汽车类型判断是调用Tesla_Drive还是BMW_Drive,假如又要新生产一款车(Benz),那么又要加判断,导入Benz_Drive,这种方法的耦合度无疑非常高。但如果我们定义一个标准(结构),定义好需要传入参数和输出参数,所有的零件制造商就按照这个标准,不管内部是怎么实现的,能接上就行。组装者只需要调用Drive一个方法就行。
文件组织
-car
-cardirve.go
-aodi.go
-bmw.go
-mycar.go
-main.go
cardirve.go
package car
//定义一个接口
type CarDrive interface {
Drive()
}
aodi.go
package car
import "fmt"
type Aodi struct {
Name string
}
//实现接口的方法
func (aodi *Aodi) Drive() {
fmt.Printf("Aodi %v is Driving\n", aodi.Name)
}
bmw.go
package car
import "fmt"
type Bmw struct {
Name string
}
func (bmw *Bmw) Drive() {
fmt.Printf("BMW %v is Driving\n", bmw.Name)
}
main.go
package main
import "go3/car"
func main() {
aodi := car.Aodi{"A4"}
aodi.Drive() //Aodi结构体实现了CarDrive接口,可以直接用接口的方法Drive
mycar := car.MyCar{&aodi} //将Aodi A4作为mycar,这种方法不用显示初始化接口,因为Mycar结构体中直接包含接口
mycar.Drive()
mycar = car.MyCar{&car.Bmw{"AMG"}} //将Benz AMG 作为mycar
mycar.Drive()
var i car.CarDrive
i = &car.Aodi{"A5"} //初始化接口,也就是接口和结构体绑定
i.Drive()
}
一般情况下接口定义用一个文件,每个类型实现接口也要单独一个文件。在main.go中需要将接口初始化,也就是接口和类型实例进行绑定。例如mycar := car.MyCar{&aodi},解析后就是mycar := car.MyCar{&car.Aodi{"A4"}},这里的&car.Aodi{"A4"}是实例地址,然后mycar.Drive()调用接口方法。也可以直接i := &car.Aodi{"A5"},然后再i.Drive()调用接口方法。使用Mycar结构体的好处是,它可以放一系列的接口。
运行结果
Aodi A4 is Driving
Aodi A4 is Driving
BMW AMG is Driving
Aodi A5 is Driving
后记
go虽然严格意义上不是面向对象编程语言,但是他有面向对象的特性(封装、继承、多态)。通过结构体来进行封装(抽象一些特征放在字段上,如Aodi结构体中的name string)和继承(通过结构体嵌套)、通过接口进行多态(如mycar可以是Aodi结构体的实例,也可以是BMW结构体的实例,每个结构体都有Drive方法的不同实现)。