golang教程之結構體代替類-Go面向物件
結構體代替類-Go面向物件
Go面向物件?
Go不是純粹的面向物件程式語言。摘自Go的常見問題解答,回答了Go是否面向物件的問題。
是的,不是。儘管Go具有型別和方法,並且允許面向物件的程式設計風格,但是沒有型別層次結構。 Go中“介面”的概念提供了一種我們認為易於使用且在某些方面更為通用的不同方法。還有一些方法可以在其他型別中嵌入型別,以提供類似但不完全相同的子類化。此外,Go中的方法比C++或Java更通用:它們可以為任何型別的資料定義,甚至是內建型別,例如普通的“未裝箱”整數。它們不限於結構(類)。
在即將到來的教程中,我們將討論如何使用Go實現面向物件的程式設計概念。與其他面向物件的語言(如Java)相比,它們中的一些在實現上有很大不同。
結構體代替類
Go不提供類,但它確實提供了結構。可以在結構上新增方法。這提供了將資料和方法捆綁在一起的行為,類似於類。
讓我們馬上開始一個例子,以便更好地理解。
我們將在此示例中建立一個自定義包,因為它有助於更好地理解結構如何成為類的有效替代。
在Go工作區內建立一個資料夾,並將其命名為oop
。在oop
中建立一個子資料夾employee
。在employee
資料夾中,建立一個名為employee.go
的檔案
資料夾結構看起來像,
workspacepath -> oop -> employee -> employee.go
請用以下內容替換employee.go
的內容,
package employee
import (
"fmt"
)
type Employee struct {
FirstName string
LastName string
TotalLeaves int
LeavesTaken int
}
func (e Employee) LeavesRemaining() {
fmt.Printf("%s %s has %d leaves remaining", e.FirstName, e.LastName, (e.TotalLeaves - e.LeavesTaken))
}
在上面的程式中,第一行指定此檔案屬於employee
Employee
結構。一個名為LeavesRemaining
的方法被新增到第14行的Employee
結構中。這將計算並顯示員工剩餘的離職數量。 現在我們有一個結構和一個方法,它執行在一個類似於類的結構上。
在oop
資料夾中建立名為main.go
的檔案。
現在資料夾結構看起來像,
workspacepath -> oop -> employee -> employee.go
workspacepath -> oop -> main.go
main.go
的內容如下。
package main
import "oop/employee"
func main() {
e := employee.Employee {
FirstName: "Sam",
LastName: "Adolf",
TotalLeaves: 30,
LeavesTaken: 20,
}
e.LeavesRemaining()
}
我們在第3行匯入employee
包。 在main()
中第12行呼叫Employee
結構的LeavesRemaining()
方法。
此程式無法在playground上執行,因為它有自定義程式包。 如果您通過發出命令go install oop
後跟workspacepath/bin/oop
在本地執行該程式,程式將列印輸出,
Sam Adolf has 10 leaves remaining
New()函式代替建構函式
我們上面寫的程式看起來不錯,但它有一個微妙的問題。 讓我們看看當我們定義零值的僱員結構時會發生什麼。 將main.go
的內容更改為以下程式碼,
package main
import "oop/employee"
func main() {
var e employee.Employee
e.LeavesRemaining()
}
我們所做的唯一改變是在第6行建立零值員工。 該程式將輸出,
has 0 leaves remaining
如您所見,使用Employee
的零值建立的變數不可用。它沒有有效的first name,last name,也沒有有效的leave details。
在像Java這樣的其他OOP語言中,這個問題可以通過使用建構函式來解決。可以使用引數化建構函式建立有效物件。
Go不支援建構函式。如果型別的零值不可用,則程式設計師的工作是取消匯出型別以防止從其他包訪問,並且還提供名為NewT(parameters)
的函式,該函式使用所需的值初始化型別T
. Go中的一個約定是命名一個函式,它為NewT(parameters)
建立一個T
型別的值。這將像一個建構函式。如果包只定義了一個型別,那麼Go的一個約定就是將此函式命名為New(parameters)
而不是 NewT(parameters)
。
讓我們對我們編寫的程式進行更改,以便每次建立員工時都可以使用它。
第一步是取消匯出Employee
結構並建立一個函式New()
,它將建立一個新的Employee
。將employee.go
中的程式碼替換為以下程式碼,
package employee
import (
"fmt"
)
type employee struct {
firstName string
lastName string
totalLeaves int
leavesTaken int
}
func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee {
e := employee {firstName, lastName, totalLeave, leavesTaken}
return e
}
func (e employee) LeavesRemaining() {
fmt.Printf("%s %s has %d leaves remaining", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken))
}
我們在這裡做了一些重要的改變。我們已將Employee struct的起始字母e設為小寫,即我們已將type Employee struct
更改為type employee struct
。通過這樣做,我們成功地取消了employee
結構,並阻止了其他包的訪問。除非特定需要匯出它們,否則將未匯出結構的所有欄位都取消匯出是一種很好的做法。由於我們不需要在包之外的任何地方使用employee
結構的欄位,因此我們也取消了所有欄位的輸出。
我們在LeavesRemaining()
方法中相應地更改了欄位名稱。
現在,由於employee
未被匯出,因此無法從其他包建立Employee
型別的值。因此我們在第一行提供了一個匯出的New
函式。將所需引數作為輸入並返回新建立的employee。
該程式仍然需要進行更改以使其工作,但是讓我們執行它來了解到目前為止更改的效果。如果執行此程式,它將失敗並出現以下編譯錯誤,
go/src/constructor/main.go:6: undefined: employee.Employee
這是因為我們有未匯出的Employee
,因此編譯器丟擲錯誤,這個型別沒有在main.go
中定義。正是我們想要的。 現在沒有其他包能夠建立零值employee
。 我們已成功阻止建立不可用的employee 結構值。 現在建立員工的唯一方法是使用New
功能。
用以下內容替換main.go
的內容,
package main
import "oop/employee"
func main() {
e := employee.New("Sam", "Adolf", 30, 20)
e.LeavesRemaining()
}
對此檔案的唯一更改是第6行,我們通過將所需引數傳遞給New
函式建立了一個新employee 。
以下是進行所需更改後的兩個檔案的內容,
employee.go
package employee
import (
"fmt"
)
type employee struct {
firstName string
lastName string
totalLeaves int
leavesTaken int
}
func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee {
e := employee {firstName, lastName, totalLeave, leavesTaken}
return e
}
func (e employee) LeavesRemaining() {
fmt.Printf("%s %s has %d leaves remaining", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken))
}
main.go
package main
import "oop/employee"
func main() {
e := employee.New("Sam", "Adolf", 30, 20)
e.LeavesRemaining()
}
執行此程式將輸出,
Sam Adolf has 10 leaves remaining
因此,您可以理解,儘管Go不支援類,但可以有效地使用結構代替類和簽名方法。可以在建構函式的位置使用New(parameters)
。