
一、前言
OOP特性有三:封装、继承、多态。
封装:通过权限控制保证数据安全,隐藏细节(直接api调用就行),提供简单的接口。
继承:继承可以复用代码内容,例如多个类的同一个方法对外表现都是一样的,那么可以抽取到父类中。而接口定义了一套规范,所有实现接口的类必须遵守这个规范,具有相同的能力。能力表现可能不尽相同。
多态:多态分为静态多态和动态多态。静态多态:方法重载。动态多态:通过上述的继承实现。
这三大特性,保证了我们可以非常高效的利用代码,同时对使用代码的程序员也是非常的友好!
二、Go的接口和继承
写过Java的都知道,Java对封装、继承、多态支持的非常友好。但是Go语言中接口支持尚可,但是继承和抽象类能力非常拉胯。
Go是通过组合来实现继承的,你没有看错,就是UML的组合,即子类持有父类对象的实例当做继承(这算哪门子继承?),代码如下:
package main
import "fmt"
/**
声明人类这个接口,有吃饭“eat”的方法
*/
type computer struct {
logo string
// 不能在结构体中声明方法
// ShowLogo()
}
//如果一个struct嵌入另一个匿名结构体,就可以直接访问匿名结构体的字段或方法,从而实现继承
type dell struct {
// 匿名结构体
computer
}
//如果一个struct嵌套了另外一个有名结构体,是UML中的组合或者聚合(根据实际情况来定)
type apple struct{
pc computer
}
/**
声明了父类computer的showLogo方法,接收computer做对象
*/
func (ptr computer) ShowLogo(){
fmt.Println("当前电脑的logo是:" + ptr.logo)
}
func main() {
// 因为go只有伪继承, 其本质是一种语法糖, 无法实现继承中动态绑定等特性. 且构造函数, 连伪继承都达不到.所以一下代码编译会报错类型不匹配 Cannot use 'dell{computer{logo: "dell"}}' (type dell) as type computer
//var pc computer = dell{computer{logo: "dell"}}
//showLogo(pc)
//pc = apple{computer{logo: "apple"}}
//showLogo(pc)
// 伪继承可以直接调用,但是不能使用父类指针指向子类对象
var d dell = dell{computer{logo: "dell"}}
d.ShowLogo()
// 组合更不能了。
var a apple = apple{computer{logo: "apple"}}
a.pc.ShowLogo()
}
func showLogo(pc computer){
pc.ShowLogo()
}
继承拉胯的难受,但是还好Go语言有提供interface:
package main
import "fmt"
/**
声明人类这个接口,有吃饭“eat”的方法
*/
type animal interface {
// 接口内不能声明字段
//name string
// 说方法,这里string是返回值的意思,实现类的方法签名和接口一致。
Say() string
// 走路方法,没有返回值
Walk()
}
type human struct {
// 做事的内容
walkWay string
// 说话的内容
sayContent string
}
/**
下面的实现方法中,一个使用human一个使用 * human来接收对象。
只要使用了指针对象,在创建对象时必须使用&来获得对象的引用
*/
// 这里是 (human)类型,所以函数内部不可以对当前对象进行更改,只能读取内容
func (instance human) Say() string {
fmt.Println("人类说:" + instance.sayContent)
return instance.sayContent
}
// 这里是 (* human)类型,所以函数内部可以对当前对象进行更改
func (instance *human) Walk() {
fmt.Println("人类走路:" + instance.walkWay)
}
type dog struct {
// 做事的内容
walkWay string
// 说话的内容
sayContent string
}
// 这里是 (human)类型,所以函数内部不可以对当前对象进行更改,只能读取内容
func (instance dog) Say() string {
fmt.Println("狗说:" + instance.sayContent)
return instance.sayContent
}
// 这里是 (* human)类型,所以函数内部可以对当前对象进行更改
func (instance *dog) Walk() {
fmt.Println("狗走路:" + instance.walkWay)
}
func main() {
var a animal = &human{"人走路", "我是一个人"}
doWalk(a)
doSay(a)
a = &dog{"狗走路", "汪汪汪"}
doWalk(a)
doSay(a)
}
// 多态
// 模拟业务操作,然后调用到say方法
func doSay(a animal) {
// do sth
a.Say()
}
// 模拟业务操作,然后调用到walk方法
func doWalk(a animal) {
// do sth 模拟具体的业务操作,
a.Walk()
}
go的接口代码和java的区别不大,但是go的接口中无法声明字段。可以使用接口实现多态。实际使用应该和java差不多。
看到接口你一定会想到抽象类,百度了一下发现Go没有抽象类的概念,抽象类也只能自己伪装一个抽象类:
package main
import "fmt"
// 游戏接口
type IGame interface {
Name() string
}
// 抽象的游戏,实际并不抽象(假抽象)
type AbstractGame struct{}
func (g *AbstractGame) play(game IGame) {
fmt.Println(fmt.Sprintf("%s is awesome!", game.Name()))
}
// Dota游戏,嵌入匿名结构体的方式继承抽象类
type Dota struct {
AbstractGame
}
func (d *Dota) Name() string {
return "Dota"
}
// Lol游戏,嵌入匿名结构体的方式继承抽象类
type LOL struct {
AbstractGame
}
func (l *LOL) Name() string {
return "LOL"
}
func main() {
dota := &Dota{}
dota.play(dota)
lol := &LOL{}
lol.play(lol)
}
抽象类是通过接口 + 伪继承实现的,哈哈哈哈!
三、总结
Go语言的OOP支持做的比较差,或者说Go语言的继承做的比较差,或者说Go认为继承是不必要的。
比起Go语言,Java需要花更多更长的时间才能了解Java的内容,但是也正因为如此抽象能力也非常强,非常适合做大型的项目。而Go相对java在抽象能力表达能力等会差一点,但是上手非常快,所以相对来说可能更适合中小型项目。
当然这并不绝对,比如加拿大某知名网站(数十亿用户)用的是PHP,而go大型项目应用主要用bilibili和youtube等。
工具的好坏看用在谁的手上。