理解go语言编程-工程管理

Go语言明确宣告了拥护骆驼命名法而排斥下划线法。骆驼命名法在Java和C#中得到官方的支持和推荐,而下划线命名法则主要用在C语言的世界里,比如Linux内核和驱动开发上。

不带任何参数直接运行go fmt的话,可以直接格式化当前目录下的所有*.go文件,或者也可以指定一个GOPATH中可以找到的包名。

Go语言不仅允许我们导入本地包,还支持在语言级别调用远程的包。然后,在执行go build或者go install之前,只需要加这么一句: go get github.com/myteam/exp/crc32

1
2
3
4
import (
"fmt"
"github.com/myteam/exp/crc32"
)

当我们执行完go get之后,我们会在src目录中看到github.com目录,其中包含myteam/exp/crc32目录。在crc32中,就是该包的所有源代码。也就是说,go工具会自动帮你获取位于远程的包源码,在随后的编译中,也会在pkg目录中生成对应的.a文件。
对于Go语言本身来讲,远程包github.com/myteam/ exp/crc32只是一个与fmt无异的本地路径而已。

Gotool的大部分功能其实已经不再针对当前目录,而是针对包名,于是如何才能定位到对应的源代码就落到了GOPATH身上。

GOPATH目录结构:
bin //用来存放编译后的可执行文件
pkg //用于存放编译后生成的归档文件
src //用来存放go源码文件

假设现在本地硬盘上有3个Go代码工程,分别为~/work/go-proj1、~/work2/goproj2和~/work3/work4/go-proj3,那么GOPATH可以设置为如下内容:
export GOPATH=~/work/go-proj1:~/work2/goproj2:~/work3/work4/go-proj3
经过这样的设置后,你可以在任意位置对以上的3个工程进行构建,注意vscode每次只打开一个工程。
便于管理项目,每个项目都是不同的GoPath,这对于我们管理多个Golang项目而言,能够非常清晰的处理项目结构。如果我们把所有项目都放在同一个GoPath的src包下,那么项目的结构就会变得非常混乱,难以管理。

但是当我们需要依赖第三方的包的时候,不同的项目设置不同的GoPath的缺点也非常明显:

  • 第三方依赖的包和我们自己的Golang包混在一起,会给我们的项目文件管理带来一定的麻烦
  • 不同的GoPath都需要下载依赖,那么磁盘中重复的依赖就会非常多,会占用我们大量的磁盘空间

一个标准的Go语言工程包含以下几个目录:src、pkg和bin。目录src用于包含所有的源代码,是Gotool一个强制的规则,而pkg和bin则无需手动创建,如果必要Gotool在构建过程中会自动创建这些目录。
Gotool会认为src下包含了两个包:calc和simplemath,而且这两个包的路径都是一级的,即simplemath下的*.go文件将会被构建为一个名为simplemath.a的包。

为了解决GOPATH的问题,因此官方在1.11开始推出了Go Modules的功能。Go Modules解决方式很像是Java看到Maven的做法,将第三方库储存在本地的空间,并且给项目代码去引用。

采用Go Modules,下载下来的第三方依赖就位于GOPATH/pkg/mod目录下。除了go.mod之外,go命令还维护一个名为go.sum的文件,其中包含特定模块版本内容的预期加密哈希。

只要有开启go modules功能,go get就不会像以前一样在GOPATH/src下放置依赖,而是会放在GOPATH/pkg/mod里面,并且go.mod会写好引入。

go mod init加模块名,引入本地文件为import “模块名/目录路径”

go doc foo生成文档,godoc -http=:76 -path=”.”,然后再访问http://localhost:76/,单击顶部的foo链接,或者直接访问http://localhost:76/pkg/foo/ ,就可以看到注释提取的效果。// BUG(author): 的方式记录该代码片段中的遗留问题

可以通过设置GOOS和GOARCH两个环境变量来指定交叉编译的目标格式。
GOOS=windows GOARCH=386 go build -o hello.exe hello.go

Go的单元测试函数分为两类:功能测试函数和性能测试函数,分别为以Test和Benchmark为函数名前缀并以testing.T为单一参数的函数。下面是测试函数声明的例子:
func TestAdd1(t
testing.T)
func BenchmarkAdd1(t *testing.T)

1
2
3
4
5
6
func TestAdd1(t *testing.T) {
r := Add(1, 2)
if r != 2 { // 这里本该是3,故意改成2测试错误场景
t.Errorf("Add(1, 2) failed. Got %d, expected 3.", r)
}
}

执行功能单元测试非常简单,直接执行go test命令即可,如go test -v mplayer/library.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 最大的区别在于代码里的这个for循环,循环b.N次
func BenchmarkAdd1(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
// 也可以这样处理以明确排除这些准备工作所花费 时间对于性能测试的时间影响
func BenchmarkAdd1(b *testing.B) {
b.StopTimer() // 暂停计时器
DoPreparation() // 一个耗时较长的准备工作,比如读文件
b.StartTimer() // 开启计时器,之前的准备时间未计入总花费时间内
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}

性能单元测试的执行与功能测试一样简单,只不过调用时需要增加-test.bench参数而已.

nephen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!