首页 > 其他分享 >go教程7

go教程7

时间:2024-01-23 10:49:01浏览次数:26  
标签:教程 Employee employee New go Go main

Mutex vs 信道

通过使用 Mutex 和信道,我们已经解决了竞态条件的问题。那么我们该选择使用哪一个?答案取决于你想要解决的问题。如果你想要解决的问题更适用于 Mutex,那么就用 Mutex。如果需要使用 Mutex,无须犹豫。而如果该问题更适用于信道,那就使用信道。:)

由于信道是 Go 语言很酷的特性,大多数 Go 新手处理每个并发问题时,使用的都是信道。这是不对的。Go 给了你选择 Mutex 和信道的余地,选择其中之一都可以是正确的。

总体说来, Go 协程需要与其他协程通信时,可以使用信道。而当只允许一个协程访问临界区时,可以使用 Mutex

就我们上面解决的问题而言,我更倾向于使用 Mutex,因为该问题并不需要协程间的通信。所以 Mutex 是很自然的选择。

我的建议是去选择针对问题的工具,而别让问题去将就工具。:)

本教程到此结束。祝你愉快。

 

1.     结构体取代类

Go 支持面向对象吗?

Go 并不是完全面向对象的编程语言。Go 官网的 FAQ 回答了 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 里的内容替换为如下所示的代码。

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))
}

在上述程序里,第 1 行指定了该文件属于 employee 包。而第 7 行声明了一个 Employee 结构体。在第 14 行,结构体 Employee 添加了一个名为 LeavesRemaining 的方法。该方法会计算和显示员工的剩余休假数。于是现在我们有了一个结构体,并绑定了结构体的方法,这与类很相似。

接着在 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() 方法。

由于有自定义包,这个程序不能在 go playground 上运行。你可以在你的本地运行,在 workspacepath/bin/oop 下输入命令 go install opp,程序会打印输出:

Sam Adolf has 10 leaves remaining

使用 New() 函数,而非构造器

我们上面写的程序看起来没什么问题,但还是有一些细节问题需要注意。我们看看当定义一个零值的 employee 结构体变量时,会发生什么。将 main.go 的内容修改为如下代码:

package main
 
import "oop/employee"
 
func main() {  
    var e employee.Employee
    e.LeavesRemaining()
}

我们的修改只是创建一个零值的 Employee 结构体变量(第 6 行)。该程序会输出:

has 0 leaves remaining

你可以看到,使用 Employee 创建的零值变量没有什么用。它没有合法的姓名,也没有合理的休假细节。

在像 Java 这样的 OOP 语言中,是使用构造器来解决这种问题的。一个合法的对象必须使用参数化的构造器来创建

Go 并不支持构造器。如果某类型的零值不可用,需要程序员来隐藏该类型,避免从其他包直接访问。程序员应该提供一种名为 NewT(parameters) 的 函数,按照要求来初始化 T 类型的变量。按照 Go 的惯例,应该把创建 T 类型变量的函数命名为 NewT(parameters)。这就类似于构造器了。如果一个包只含有一种类型,按照 Go 的惯例,应该把函数命名为 New(parameters), 而不是 NewT(parameters)

让我修改一下原先的代码,使得每当创建 employee 的时候,它都是可用的。

首先应该让 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,也就是将 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 里的内容。

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) 签名的方法可以替代构造器。

关于 Go 中的类和构造器到此结束。祝你愉快。

标签:教程,Employee,employee,New,go,Go,main
From: https://www.cnblogs.com/gongxianjin/p/17981801

相关文章

  • go教程6
    关闭信道和使用forrange遍历信道数据发送方可以关闭信道,通知接收方这个信道不再有数据发送过来。当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭。v,ok:=<-ch上面的语句里,如果成功接收信道所发送的数据,那么 ok 等于true。而如果 ok 等于fals......
  • go教程8
    1.    合取代继承      Go不支持继承,但它支持组合(Composition)。组合一般定义为“合并在一起”。汽车就是一个关于组合的例子:一辆汽车由车轮、引擎和其他各种部件组合在一起。通过嵌套结构体进行组合在Go中,通过在结构体内嵌套结构体,可以实现组合。组合的典型例......
  • go教程10
    1.    自定义错误在上一教程里,我们学习了Go中的错误是如何表示的,并学习了如何处理标准库里的错误。我们还学习了从标准库的错误中提取更多的信息。在本教程中,我们会学习如何创建我们自己的自定义错误,并在我们创建的函数和包中使用它。我们会使用与标准库中相同的技术,来提......
  • go教程9
    什么是defer?defer 语句的用途是:含有 defer 语句的函数,会在该函数将要返回之前,调用另一个函数。这个定义可能看起来很复杂,我们通过一个示例就很容易明白了。示例packagemain import(     "fmt") funcfinished(){     fmt.Println("Finishedfindi......
  • go教程11
    1.    Panic和recover什么是panic?在Go语言中,程序中一般是使用错误来处理异常情况。对于程序中出现的大部分异常情况,错误就已经够用了。但在有些情况,当程序发生异常时,无法继续运行。在这种情况下,我们会使用 panic 来终止程序。当函数发生panic时,它会终止运行,在执行......
  • go教程12
    1.    读取文件文件读取是所有编程语言中最常见的操作之一。本教程我们会学习如何使用Go读取文件。本教程分为如下小节。将整个文件读取到内存使用绝对文件路径使用命令行标记来传递文件路径将文件绑定在二进制文件中分块读取文件逐行读取文件将整个文件读取......
  • go教程
    这是我们 Golang系列教程的第一个教程。1.    GO介绍与安装Golang是什么     Go亦称为Golang(译注:按照RobPike说法,语言叫做Go,Golang只是官方网站的网址),是由谷歌开发的一个开源的编译型的静态语言。     Golang的主要关注点是使得高可用性和可扩展......
  • 使用Go进行HTTP客户端认证
    在Go语言中,HTTP客户端认证可以通过net/http包来实现。下面是一个简单的示例,展示如何使用Go进行HTTP客户端认证。首先,确保你已经安装了Go语言环境,并设置好了相关的环境变量。Go语言中的HTTP客户端认证主要涉及到设置请求头中的认证信息。以下是一个简单的示例代码,展示了如何使用Go发......
  • 在Go中处理HTTP重定向
    在Go语言中处理HTTP重定向是一个常见的任务,特别是在开发Web应用程序或与Web服务进行交互时。HTTP重定向是一种服务器端的行为,它告诉客户端请求的资源已移动到新的位置,并提供了新的位置信息。Go语言的net/http包提供了处理HTTP请求和响应的工具,包括重定向的处理。以下是一个示例,展示......
  • 使用Go编写HTTP中间件
    在Go语言中,HTTP中间件是一种处理HTTP请求和响应的函数,它可以拦截到请求并对其进行处理,然后再将请求传递给下一个中间件或目标处理程序。HTTP中间件在Web应用程序中非常常见,它提供了一种机制来执行各种任务,例如身份验证、授权、日志记录和错误处理等。下面是一个使用Go编写HTTP中间......