首頁>技術>

前言

我的讀者中應該大部分都是 Java 從業者,不知道寫 Java 這些年是否真的有找到物件?

沒找到也沒關係,總不能在一棵樹上吊死,我們也可以來 Go 這邊看看,說不定會有新發現。

開個玩笑,本文會以一個 Javaer 的角度來聊聊 Go 語言中的面向物件。

其他的我們暫且不看,Yes and No. 這個回答就比較微妙了,為了這篇文章還能寫下去我們先認為 Go 是面向物件的。

面向物件有著三個重要特徵:

封裝繼承多型封裝

Go 並沒有 Class 的概念,卻可以使用 struct 來達到類似的效果,比如我們可以對汽車宣告如下:

type Car struct { Name string Price float32}

與 Java 不同的是,struct 中只儲存資料,不能定義行為,也就是方法。

當然也能為 Car 定義方法,只是寫法略有不同:

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()}// 實現1func (car *PetrolCar) Run() { fmt.Printf("%s PetrolCar run \n", car.Name)}// 實現2func (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 感興趣那不妨給我的號加個【關注】吧。

13
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 網路工程師必備的7款軟體