[譯] part 26: golang 的面向對象 -- 結構取代類

Go 是面向對象的語言嗎

Go 不是純粹的面向對象的編程語言。摘自 Go 的常見問題解答,回答了 Go 是否面向對象的問題。程序員

是也不是。儘管 Go 具備類型和方法,而且容許面向對象的編程風格,可是沒有類型層次結構。 Go 中 「interface」 的概念提供了一種咱們認爲易於使用且在某些方面更爲通用的方法。還有一些方法能夠將類型嵌入到其餘類型中,以提供相似但不徹底相同的子類化。此外,Go 中的方法比 C ++或 Java 更通用:能夠爲任何類型的數據定義它們,甚至是內置類型,例如普通的整數。它們不限於結構(類)。golang

在即將到來的教程中,咱們將討論如何使用 Go 實現面向對象的編程概念。與其餘面向對象的語言(如 Java)相比,它們中的一些在實現上有很大不一樣。編程

結構取代類

Go 沒有提供類,但它提供告終構,能夠在結構上添加方法。這提供了將數據和方法捆綁在一塊兒的行爲,相似於類。編程語言

讓咱們立刻開始一個例子,以便更好地理解。函數

咱們將在此示例中建立一個自定義包,由於它有助於更​​好地理解結構如何有效地替代類。oop

在 Go 工做區內建立一個文件夾,並將其命名爲 oop。在 oop 中建立一個子文件夾 employee。在 employee 文件夾中,建立一個名爲 employee.go 的文件spa

結構看起來像這樣,code

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 包。第 7 行聲明瞭Employee結構,一個名爲LeavesRemaining的方法被添加到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() 函數中調用了 Employee結構的LeavesRemaining()方法。

此程序沒法在 playground 上運行,由於它具備自定義包。若是你在本地運行這個程序,經過令go install oop後跟workspacepath/bin/oop,該程序將打印輸出,

Sam Adolf has 10 leaves remaining  
複製代碼

New() 函數取代構造函數

咱們上面寫的程序看起來不錯,但它有一個小問題。讓咱們看看當咱們定義零值的Employee結構時會發生什麼。將 main.go 的內容更改成如下代碼,

package main

import "oop/employee"

func main() {  
    var e employee.Employee
    e.LeavesRemaining()
}
複製代碼

咱們作的惟一修改是在第 6 行建立了零值Employee。該程序將輸出,

has 0 leaves remaining
複製代碼

如你所見,使用零值Employee的建立變量是不可用的。它沒有有效的FirstNameLastName,也沒有有效的休假詳情。

在像 Java 這樣的 OOP 語言中,這個問題能夠經過構造函數來解決。可使用參數化構造函數建立有效對象。

Go 不支持構造函數。若是類型的零值不可用,則須要程序員讓類型不能導出使之不能從其餘包訪問,而且還要提供名爲NewT()的函數,該函數使用所需的值初始化類型T。Go 中的一個約定是命名一個函數,它爲NewT()建立一個 T 類型的值。這就像一個構造函數。若是包只定義了一種類型,那麼 Go 中的一個約定就是將此函數命名爲New()而不是NewT()

讓咱們修改一下程序,以便每次建立員工時均可以使用。

第一步是讓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 結構的起始字母e設爲小寫,即咱們已將類型Employee 結構更改成類型employee結構。經過這樣作,咱們能讓employee結構不能被導出,從而阻止了其餘包的訪問。除非須要指定導出它們,不然將不能導出的結構的全部字段都禁止導出是一種很好的作法。因爲咱們不須要在包外的任何地方使用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 的內容,

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()方法去替代構造函數來有效地實現了相似的行爲。

相關文章
相關標籤/搜索