- 原文地址:Part 26: Structs Instead of Classes - OOP in Go
- 原文做者:Naveen R
- 译者:咔叽咔叽 转载请注明出处。
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
的建立变量是不可用的。它没有有效的FirstName
,LastName
,也没有有效的休假详情。
在像 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()
方法去替代构造函数来有效地实现了相似的行为。