個人讀者中應該大部分都是 Java
從業者,不知道寫 Java
這些年是否真的有找到對象?python
沒找到也不要緊,總不能在一棵樹上吊死,咱們也能夠來 Go
這邊看看,說不定會有新發現。編程
開個玩笑,本文會以一個 Javaer
的角度來聊聊 Go
語言中的面向對象。json
面向對象這一詞來源於Object Oriented Programming
,也就是你們常說的 OOP
。markdown
對於 Go
是否爲面向對象的編程語言,這點也是討論已久;不過咱們能夠先看看官方的說法:編程語言
其餘的咱們暫且不看,Yes and No.
這個回答就比較微妙了,爲了這篇文章還能寫下去咱們先認爲 Go
是面向對象的。oop
面向對象有着三個重要特徵:學習
Go
並無 Class
的概念,卻可使用 struct
來達到相似的效果,好比咱們能夠對汽車聲明以下:this
type Car struct {
Name string
Price float32
}
複製代碼
與 Java
不一樣的是,struct
中只存儲數據,不能定義行爲,也就是方法。url
固然也能爲 Car
定義方法,只是寫法略有不一樣:spa
func (car *Car) Info() {
fmt.Printf("%v price: [%v]", car.Name, car.Price)
}
func main() {
car := Car{
Name: "BMW",
Price: 100.0,
}
car.Info()
}
複製代碼
在方法名稱前加上 (car *Car)
便能將該方法指定給 Car
,其中的 car
參數能夠理解爲 Java
中的 this
以及 Python
中的 self
,就語義來講我以爲 go
更加簡單一些。
畢竟我見過很多剛學習 Java
的萌新很是不理解 this
的含義與用法。
既然談到結構體了那就不得不聊聊 Go
支持的匿名結構體(雖然和麪向對象沒有太大關係)
func upload(path string) {
body, err := ioutil.ReadAll(res.Body)
smsRes := struct {
Success bool `json:"success"`
Code string `json:"code"`
Message string `json:"message"`
Data struct {
URL string `json:"url"`
} `json:"data"`
RequestID string `json:"RequestId"`
}{}
err = json.Unmarshal(body, &smsRes)
fmt.Printf(smsRes.Message)
}
複製代碼
Go
容許咱們在方法內部建立一個匿名的結構體,後續還能直接使用該結構體來獲取數據。
這點在咱們調用外部接口解析響應數據時很是有用,建立一個臨時的結構體也不用額爲維護;同時還能用面向對象的方式獲取數據。
相比於將數據存放在 map
中用字段名獲取要優雅許多。
Go
語言中並無 Java
、C++
這樣的繼承概念,類之間的關係更加扁平簡潔。
各位 Javaer
應該都看過這類圖:
相信大部分新手看到這圖時就已經懵逼,更別說研究各個類之間的關係了。
不過這樣好處也明顯:若是咱們抽象合理,整個系統結構會很好維護和擴展;但前提是咱們能抽象合理。
在 Go
語言中更推薦使用組合的方式來複用數據:
type ElectricCar struct {
Car
Battery int32
}
func main() {
xp := ElectricCar{
Car{Name: "xp", Price: 200},
70,
}
fmt.Println(xp.Name)
}
複製代碼
這樣咱們即可以將公共部分的數據組合到新的 struct
中,並可以直接使用。
面向接口編程的好處這裏就不在贅述了,咱們來看看 Go 是如何實現的:
type ElectricCar struct {
Car
Battery int32
}
type PetrolCar struct {
Car
Gasoline int32
}
//定義一個接口
type RunService interface {
Run()
}
// 實現1
func (car *PetrolCar) Run() {
fmt.Printf("%s PetrolCar run \n", car.Name)
}
// 實現2
func (car *ElectricCar)Run() {
fmt.Printf("%s ElectricCar run \n", car.Name)
}
func Do(run RunService) {
run.Run()
}
func main() {
xp := ElectricCar{
Car{Name: "xp", Price: 200},
70,
}
petrolCar := PetrolCar{
Car{Name: "BMW", Price: 300},
50,
}
Do(&xp)
Do(&petrolCar)
}
複製代碼
首先定義了一個接口 RunService
;ElectricCar
與 PetrolCar
都實現了該接口。
能夠看到 Go
實現一個接口的方式並非 implement
,而是用結構體聲明一個相同簽名的方法。
這種實現模式被稱爲」鴨子類型「,Python
中的接口也是相似的鴨子類型
。
詳細介紹能夠參考這篇:Python 中的面向接口編程
接口固然也是能夠擴展的,相似於 struct
中的嵌套:
type DiService interface {
Di()
}
//定義一個接口
type RunService interface {
DiService
Run()
}
複製代碼
得益於 Go
的強類型,剛纔的 struct
也得實現 DiService
這個接口才能編譯經過。
到這裏應該是能理解官方所說的 Yes and No.
的含義了;Go
對面向對象的語法不像 Java
那麼嚴苛,甚至整個語言中都找不到 object(對象)
這個關鍵詞;可是利用 Go
裏的其餘特性也是能實現 OOP
的。
是否爲面向對象我以爲並不重要,主要目的是咱們能寫出易擴展好維護的代碼。
例如官方標準庫中就有許多利用接口編程的例子:
因爲公司技術棧如今主要由 Go
爲主,後續也會繼續更新 Go
相關的實戰經驗;若是你也對學習 Go
感興趣那不妨點個關注吧。