浅谈Go语言的接口设计一

浅谈Go语言的接口设计一

作为一个三年+的phper转到Go语言,手里拿着锤子看什么都是钉子。对象去哪儿了?抽象类,接口去哪儿了?

在Go语言中丢掉了很多语言都会有的 Class,那面向对象编程和多态从何谈起呢?

Go语言为我们提供了自定义数据结构类型的能力,而Go语言原生的数据结构中有一种叫做接口类型的变量,二者组合就可以帮助我们写出面向对象代码。在Go语言中自定义数据结构负责存储数据,就好比PHP类中的属性,而结构的自定义方法就像是PHP类中的函数,代码包中的首字母大小写决定了该属性(方法)能否被其他的包直接访问,就如同PHP类中的访问控制。如此一来,结构拥有了自定义存储,函数,权限控制,就可以支撑面向对象编程了。

Wait,对象继承呢?额,数据结构之间貌似不支持继承,也不能直接嵌套。不过没关心,我们还没有介绍什么是接口。

接口本来是Go语言里面的一个万金油类型,要知道Go是一门强类型的编译语言,声明了是什么类型就是什么类型,不像PHP那么随便 。但在处理不同类型数据的时候难免会不方便,无法实现多态,这个时候interface类型就可以排上用场了,因为任意的数据类型都可以赋值给一个interface类型的变量(或是interface的别名类型变量)。

注意是任意类型都可以赋值给interface,interface的神奇之处就在于此了。我们定义一个interface的别名类型,然后给这个interface的别名类型再声明一系列的方法。接下来只要是任何实现了这一系列方法的数据结构,都可以赋值给这个interface的别名类型变量,我们就说这个数据结构实现了该接口,此外接口和接口之间还可以嵌套组合出新的接口。例如,定义一个接口功能是做作业,定义一个接口功能是跑步,现在定义一个数据结构实现了这两个接口中的自定义方法,那么这个数据结构就实现了这两个接口。所以Go语言对象的继承和封装其实是通过自定义接口类型来完成的。简言之就是,自定义数据结构 + 自定义接口组合 = 面向对象

下面是一个例子,这个包中声明了一个叫bot类型的数据结构,我们叫它机器人好了。还有三个interface的别名数据结构,DoHomeWork是做作业功能,Run是跑步功能,HomeWorkAndRun是组合了前面的两个interface实现的。而bot类型是一个包级的私有结构体,其他包无法直接获得bot类型,必须通过GetRun, GetHomeWork, GetHomeWrokAndRun 来获得,这三个方法除了返回值类型不同外并没有什么区别,它们都是定义了一个bot结构体然后转为对应的接口类型返回(Go编译器自动转换的)。

这样做有什么好处呢?
1、封装性强,其他的包只能调用暴露的接口,例如通过GetRun获得实例,那么只能调用Run方法。
2、代码即注释,接口声明中可以直接看到需要暴露的方法,从而推断用途。
3、ducktype,接口可以被任意类型的结构体实现,对于调用者来说,在这样的封装下不需要关心是哪个结构体负责实现的,这使得Go语言更多态。

type DoHomeWork interface {
	Write(string)string
}

type Run interface {
	Run()string
}

type HomeWorkAndRun interface {
	DoHomeWork
	Run
}

type bot struct {
	name string
	age int
}

func (p *bot) Write(s string) string {
	return "I'm " + p.name + ". This is my " + s + " home work"
}

func (p *bot) Run() string {
	return "I'm " + p.name + ". I can run!"
}

func GetDoHomeWork() DoHomeWork {
	return &bot{
		name: "kim",
		age:  10,
	}
}

func GetRun() Run {
	return &bot{
		name: "kim",
		age:  20,
	}
}

func GetHomeWorkAndRun() HomeWorkAndRun {
	return &bot{
		name: "kim",
		age:  30,
	}
}


发表评论

邮箱地址不会被公开。 必填项已用*标注