GO的接口调用

204 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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方法的不同实现)。