golang从设计之初本不是一个面向对象的语言,但是为了更高效的开发,又提供了一些面向对象的能力,golang的面象对象主要就是通过interface来实现的,今天我们就来聊一聊golang的这个interface。
首先呢先聊点形式化的东西,golang的interface的设计哲学呢是靠近ducking type的,什么是ducking type呢?简单的来说,就是由使用者来定义我要用的这个东西到底是什么,这么说可能有点抽象,我们来举个例子说明一下:
非ducking type模式下,我们可能实现了一个类,然后告诉别人,我的这个类完成了什么功能,你可以调用哪些接口来实现这个功能。
而在ducking type下呢,我设计的是一个基本类,比如这个类是士兵,然后给你一个接口(类似回调之类的),让你来定义这个士兵,你给他装上机枪他就是一个机枪兵,你给他装上大炮他就是炮兵。
然而golang的interface又不是完全的ducking type,因为golang没有实现动态绑定的功能。说了这么多形式化的东西,我们来看看interface到底是个什么:
我们开始说到golang的interface是用于面向对象的,面向对象的3个基本要素继承,封装,多态。继承,封装由golang提供的struct搞定了,而interface提供的就是多态的能力,多态的能力简单来说就是一对多,多对一的能力,一对多的能力由golang的type提供了(比如 type Mytype1 int...type Mytype2 int,那么int对应了Mytype1和Mytype2),那么多对一呢?靠的就是interface。下面我们从代码来进行分析:
interface在我看来有一点点泛型+回调的味道(这是本人从应用上的一点理解,在原理上他们是没有很大的关联的),为什么这么说呢?我们来看看下面的代码:
package main
import (
"fmt"
)
type Book struct {
Content string
}
type Sentense struct {
Content string
}
func (b Book) PrintContent() {
fmt.Println(b.Content)
}
func (s Sentense) PrintContent() {
fmt.Println(s.Content)
}
type TestInterface interface {
PrintContent()
}
func testPrintBook(ts TestInterface) {
ts.PrintContent()
}
func testPrintSentense(ts TestInterface) {
ts.PrintContent()
}
func main() {
book := Book{"this is book"}
sentense := Sentense{"this is sentense"}
testPrintContent(book)
testPrintContent(sentense)
}
可以看到的是,interface实现了多对一的功能,Book和Sentense都可以通过接口来进行调用,go的官方标准的说法就是实现了interface中的函数,就相当与实现了这个interface,这就是我为什么说它有一点泛型的味道,这看起来是不是和c++的template
那么为什么又说他有那么一点点回调的味道呢?我们来扩展一下上面的代码的功能,然后拆分成几个模块,来一点点工程化的感觉,我们来简单描述下我们期望实现的功能:
我们现在希望做一个print函数,它允许使用者自己定义如何print自己相关结构的内容,比如遇到Book就print它内容的一个字,遇到Sentense就把content全print出来。
那么怎么设计呢?我们的print功能应该是一个独立的模块,它应该对外开放一个接口让使用者自己实现它需要print的东西对吧(回忆下上面讲的ducking type,是不是面向使用者的哲学就体现出来了),来看下具体代码:
首先我们在testInterface.go文件中来实现这个简单的print的功能的基本框架:
package testInterface
type PrintMethod interface {
OwnPrintMenthod()
}
func MyPrintMethod(pm PrintMethod) {
pm.OwnPrintMenthod()
}
OK,我们现在就可以使用MyPrintMethod来定制我们的特殊需求了,来看看我们的main.go
package main
import (
"fmt"
"testInterface"
)
type Book struct {
Content string
}
func (b Book) OwnPrintMenthod() {
fmt.Println(b.Content[:1])
}
type Sentense struct {
Content string
}
func (s Sentense) OwnPrintMenthod() {
fmt.Println(s.Content)
}
func main() {
book := Book{"this is book"}
sentense := Sentense{"this is sentense"}
testInterface.MyPrintMethod(book)
testInterface.MyPrintMethod(sentense)
}
这里充分体现了使用者定义的哲学,Book和Sentense分别定义了自己的Print模式,我们设计的Print模块负责基本功能的实现,看到这里如果学c/c++的同学应该会联想到回调吧,因为c/c++实现这种功能最简单的就是回调函数了,在stl中也广泛的体现了这种设计哲学。
本人对于golang的学习也还不够深入,如果有什么错误的理解欢迎指正