Go 语言和其他 Java 这类语言有一个非常不一样的地方,那就是接口这块。
Java 这些你需要在你的实现类上面声明下你实现了哪个接口,而 Go 不需要,只要你实现了接口里面的方法,就默认你实现了这个接口。
所以在某种程度上讲,Go 在面向接口编程这块他更加易用。
一、什么是接口?
我们需要先知道什么是面向接口编程,就要先搞懂啥是接口,这对于新手来说比较难理解,但是对于老鸟来说也未必能清晰表达出这个概念。
所有讲接口的文章总是喜欢拿 USB 说事,这其实很难理解的,反正我那时是一直没想明白,接口和 USB 有啥关系,虽然老师讲了一大堆,但是我依然没明白。
其实我们现实生活中到处都在用接口特点。
1、拿汽车举例子
就拿汽车举例,你会发现大部分同类汽车,他的很多设计都差不大多,比如都有油门,都有离合,都是圆形的方向盘等。
这么做的目的是为了啥呢?
为啥没有哪个汽车厂家,追求标新立异,把方向盘搞成方形的,或者把油门离合调换下位置?
这里面其实就用到了接口,那就是标准,这个标准有一点好处,那就是为了方便人们更容易使用汽车,不然你可能每买一辆汽车就要去考一辆车的驾照。
现在我们一步一步解析这里面的编程技术问题。
2、没接口的代码
直接上代码:
代码语言:javascript复制// WuLing 五菱
type WuLing struct{}
func (this *WuLing) Drive() {
fmt.Println("驾驶五菱汽车")
}
// ChangAn 长安
type ChangAn struct{}
func (this *ChangAn) Drive() {
fmt.Println("驾驶长安汽车")
}
这里有两辆车,一辆是五菱,一辆是长安。
他们都是汽车,所以都应该具有一个共同特点,那就是驾驶,也就是具有 Drive 方法给别人调用。
此时我们带入游戏代码场景中:假如一个玩家,他拥有很多辆车,我们不使用接口的情况下,是不是得这样去设计我们的代码:
代码语言:javascript复制// Person 一个人
type Person struct {
Car1 *ChangAn
Car2 *WuLing
}
func main() {
// 创建一个人
p := Person{
Car1: new(ChangAn),
Car2: new(WuLing),
}
p.Car1.Drive()
p.Car2.Drive()
}
后续这玩家每新增一辆车都要去修改结构体,是不是不太符合我们的需求。
所以这里我们最好的处理方式就是使用接口,把汽车的特定属性 Drive 抽理出来。
没错现实生活中的某某标准,在代码里面其实就是接口。
- 现实生活中达到某某标准,就说明他咋样咋样,能咋样咋样。
- 代码的世界里面也是这样,实现某某接口,才可能被后续的某某使用。
二、面向接口编程
现在我们来制定下汽车的标准,也就是抽离下公共属性:
代码语言:javascript复制// ICar 汽车接口
type ICar interface {
Drive()
}
这样就制定好了一个标准,只要具有驾驶属性的我们都认为他是汽车,当然你可以加更多的标准。
所以我们现在就能改造人类结构体了:
代码语言:javascript复制// Person 一个人
type Person struct {
//Car1 *ChangAn
//Car2 *WuLing
// 拥有的汽车
Cars []ICar
}
// DriveCar 驾驶车
func (this *Person) DriveCar(car ICar) {
car.Drive()
}
现在我们就可以使用 Cars 一个属性承载所有的汽车了。使用就可以变成这样:
代码语言:javascript复制func main() {
// 创建一个人
p := Person{}
p.Cars = append(p.Cars, &ChangAn{})
p.Cars = append(p.Cars, &WuLing{})
// 驾驶车
p.DriveCar(p.Cars[0])
}
后续扩展、删除汽车都非常容易了。我们后续可以新增很多很多汽车道具,比如:比亚迪、红旗等。
三、好处
面向接口编程,有一个最大的特点就是能解耦,易扩展。
实际开发中,还有一个词语叫 可插拔 ,其实这就是接口最好的体现,为啥能做到可插拔,那就是只要符合接口标准的就能插上使用。
比如日志库,他既能配置输出到控制台,又能配置输出到文件,或者同时输出。
再或者你的数据库模块,假如你公司有两套底层数据库 MySQL 和 Oracle ,但是又希望随时可是切换两套数据库,那最好的办法就是制定接口。
你学废了么?
你学废了么?