1. 程式人生 > 其它 >geant4裡如何引用類_Go 經典入門系列 26:結構體取代類

geant4裡如何引用類_Go 經典入門系列 26:結構體取代類

技術標籤:geant4裡如何引用類

點選上方藍色“ Go語言中文網 ”關注, 每天一起學 Go

歡迎來到 Golang 系列教程[1]的第 26 篇。

Go 支援面向物件嗎?

Go 並不是完全面向物件的程式語言。Go 官網的 FAQ[2] 回答了 Go 是否是面嚮物件語言,摘錄如下。

可以說是,也可以說不是。雖然 Go 有型別和方法,支援面向物件的程式設計風格,但卻沒有型別的層次結構。Go 中的“介面”概念提供了一種不同的方法,我們認為它易於使用,也更為普遍。Go 也可以將結構體巢狀使用,這與子類化(Subclassing)類似,但並不完全相同。此外,Go 提供的特性比 C++ 或 Java 更為通用:子類可以由任何型別的資料來定義,甚至是內建型別(如簡單的“未裝箱的”整型)。這在結構體(類)中沒有受到限制。

在接下來的教程裡,我們會討論如何使用 Go 來實現面向物件程式設計概念。與其它面嚮物件語言(如 Java)相比,Go 有很多完全不同的特性。

使用結構體,而非類

Go 不支援類,而是提供了結構體。結構體中可以新增方法。這樣可以將資料和操作資料的方法繫結在一起,實現與類相似的效果。

為了加深理解,我們來編寫一個示例吧。

在示例中,我們建立一個自定義包,它幫助我們更好地理解,結構體是如何有效地取代類的。

在你的 Go 工作區建立一個名為 oop 的資料夾。在 opp 中再建立子資料夾 employee。在 employee 內,建立一個名為 employee.go 的檔案。

資料夾結構會是這樣:

workspacepath->oop->employee->employee.go

請將 employee.go 裡的內容替換為如下所示的程式碼。

packageemployee

import(
"fmt"
)

typeEmployeestruct{
FirstNamestring
LastNamestring
TotalLeavesint
LeavesTakenint
}

func(eEmployee)LeavesRemaining(){
fmt.Printf("%s%shas%dleavesremaining",e.FirstName,e.LastName,(e.TotalLeaves-e.LeavesTaken))
}

在上述程式裡,第 1 行指定了該檔案屬於 employee 包。而第 7 行聲明瞭一個 Employee 結構體。在第 14 行,結構體 Employee 添加了一個名為 LeavesRemaining 的方法。該方法會計算和顯示員工的剩餘休假數。於是現在我們有了一個結構體,並綁定了結構體的方法,這與類很相似。

接著在 oop 資料夾裡建立一個檔案,命名為 main.go

現在目錄結構如下所示:

workspacepath->oop->employee->employee.go
workspacepath->oop->main.go

main.go 的內容如下所示:

packagemain

import"oop/employee"

funcmain(){
e:=employee.Employee{
FirstName:"Sam",
LastName:"Adolf",
TotalLeaves:30,
LeavesTaken:20,
}
e.LeavesRemaining()
}

我們在第 3 行引用了 employee 包。在 main()(第 12 行),我們呼叫了 EmployeeLeavesRemaining() 方法。

由於有自定義包,這個程式不能在 go playground 上執行。你可以在你的本地執行,在 workspacepath/bin/oop 下輸入命令 go install opp,程式會列印輸出:

SamAdolfhas10leavesremaining

使用 New() 函式,而非構造器

我們上面寫的程式看起來沒什麼問題,但還是有一些細節問題需要注意。我們看看當定義一個零值的 employee 結構體變數時,會發生什麼。將 main.go 的內容修改為如下程式碼:

packagemain

import"oop/employee"

funcmain(){
vareemployee.Employee
e.LeavesRemaining()
}

我們的修改只是建立一個零值的 Employee 結構體變數(第 6 行)。該程式會輸出:

has0leavesremaining

你可以看到,使用 Employee 建立的零值變數沒有什麼用。它沒有合法的姓名,也沒有合理的休假細節。

在像 Java 這樣的 OOP 語言中,是使用構造器來解決這種問題的。一個合法的物件必須使用引數化的構造器來建立。

Go 並不支援構造器。如果某型別的零值不可用,需要程式設計師來隱藏該型別,避免從其他包直接訪問。程式設計師應該提供一種名為 NewT(parameters) 的函式,按照要求來初始化 T 型別的變數。按照 Go 的慣例,應該把建立 T 型別變數的函式命名為 NewT(parameters)。這就類似於構造器了。如果一個包只含有一種型別,按照 Go 的慣例,應該把函式命名為 New(parameters), 而不是 NewT(parameters)

讓我修改一下原先的程式碼,使得每當建立 employee 的時候,它都是可用的。

首先應該讓 Employee 結構體不可引用,然後建立一個 New 函式,用於建立 Employee 結構體變數。在 employee.go 中輸入下面程式碼:

packageemployee

import(
"fmt"
)

typeemployeestruct{
firstNamestring
lastNamestring
totalLeavesint
leavesTakenint
}

funcNew(firstNamestring,lastNamestring,totalLeaveint,leavesTakenint)employee{
e:=employee{firstName,lastName,totalLeave,leavesTaken}
returne
}

func(eemployee)LeavesRemaining(){
fmt.Printf("%s%shas%dleavesremaining",e.firstName,e.lastName,(e.totalLeaves-e.leavesTaken))
}

我們進行了一些重要的修改。我們把 Employee 結構體的首字母改為小寫 e,也就是將 type Employee struct 改為了 type employee struct。通過這種方法,我們把 employee 結構體變為了不可引用的,防止其他包對它的訪問。除非有特殊需求,否則也要隱藏所有不可引用的結構體的所有欄位,這是 Go 的最佳實踐。由於我們不會在外部包需要 employee 的欄位,因此我們也讓這些欄位無法引用。

同樣,我們還修改了 LeavesRemaining() 的方法。

現在由於 employee 不可引用,因此不能在其他包內直接建立 Employee 型別的變數。於是我們在第 14 行提供了一個可引用的 New 函式,該函式接收必要的引數,返回一個新建立的 employee 結構體變數。

這個程式還需要一些必要的修改,但現在先執行這個程式,理解一下當前的修改。如果運行當前程式,編譯器會報錯,如下所示:

go/src/constructor/main.go:6:undefined:employee.Employee

這是因為我們將 Employee 設定為不可引用,因此編譯器會報錯,提示該型別沒有在 main.go 中定義。很完美,正如我們期望的一樣,其他包現在不能輕易建立零值的 employee 變量了。我們成功地避免了建立不可用的 employee 結構體變數。現在建立 employee 變數的唯一方法就是使用 New 函式。

如下所示,修改 main.go 裡的內容。

packagemain

import"oop/employee"

funcmain(){
e:=employee.New("Sam","Adolf",30,20)
e.LeavesRemaining()
}

該檔案唯一的修改就是第 6 行。通過向 New 函式傳入所需變數,我們建立了一個新的 employee 結構體變數。

下面是修改後的兩個檔案的內容。

employee.go

packageemployee

import(
"fmt"
)

typeemployeestruct{
firstNamestring
lastNamestring
totalLeavesint
leavesTakenint
}

funcNew(firstNamestring,lastNamestring,totalLeaveint,leavesTakenint)employee{
e:=employee{firstName,lastName,totalLeave,leavesTaken}
returne
}

func(eemployee)LeavesRemaining(){
fmt.Printf("%s%shas%dleavesremaining",e.firstName,e.lastName,(e.totalLeaves-e.leavesTaken))
}

main.go

packagemain

import"oop/employee"

funcmain(){
e:=employee.New("Sam","Adolf",30,20)
e.LeavesRemaining()
}

執行該程式,會輸出:

SamAdolfhas10leavesremaining

現在你能明白了,雖然 Go 不支援類,但結構體能夠很好地取代類,而以 New(parameters) 簽名的方法可以替代構造器。

關於 Go 中的類和構造器到此結束。祝你愉快。

上一教程 - Mutex

下一教程 - 組合取代繼承[3]



推薦閱讀

  • Go 經典入門系列 25:Mutex

福利 我為大家整理了一份 從入門到進階的Go學習資料禮包 ,包含學習建議:入門看什麼,進階看什麼。 關注公眾號 「polarisxu」,回覆 ebook 獲取;還可以回覆「進群」,和數萬 Gopher 交流學習。

79e5a6b45c69c09aaa66cc5ffb3b07da.png