现在随便一个小程序的实现都可能包含超过10000个函数。然而作者一般只需要考虑其中很小的一部分和做很少的设计,因为绝大部分代码都是由他人编写的,它们通过类似包或模块的方式被重用。
Go语言有超过100个的标准包(译注:可以用go list std | wc -l
命令查看标准包的具体数目),标准库为大多数的程序提供了必要的基础构件。在Go的社区,有很多成熟的包被设计、共享、重用和改进,目前互联网上已经发布了非常多的Go语言开源包,它们可以通过 http://godoc.org 检索。在本章,我们将演示如果使用已有的包和创建新的包。
Go还自带了工具箱,里面有很多用来简化工作区和包管理的小工具。我们已经见识过如何使用工具箱自带的工具来下载、构建和运行我们的演示程序了。在本章,我们将看看这些工具的基本设计理论和尝试更多的功能,例如打印工作区中包的文档和查询相关的元数据等。
包简介
任何包系统设计的目的都是为了简化大型程序的设计和维护工作,通过将一组相关的特性放进一个独立的单元以便于理解和更新,在每个单元更新的同时保持和程序中其它单元的相对独立性。这种模块化的特性允许每个包可以被其它的不同项目共享和重用,在项目范围内、甚至全球范围统一的分发和复用。
每个包一般都定义了一个不同的名字空间用于它内部的每个标识符的访问。每个名字空间关联到一个特定的包,让我们给类型、函数等选择简短明了的名字,这样可以在使用它们的时候减少和其它部分名字的冲突。
每个包还通过控制包内名字的可见性和是否导出来实现封装特性。通过限制包成员的可见性并隐藏包API的具体实现,将允许包的维护者在不影响外部包用户的前提下调整包的内部实现。通过限制包内变量的可见性,还可以强制用户通过某些特定函数来访问和更新内部变量,这样可以保证内部变量的一致性和并发时的互斥约束。
当我们修改了一个源文件,我们必须重新编译该源文件对应的包和所有依赖该包的其他包。即使是从头构建,Go语言编译器的编译速度也明显快于其它编译语言。Go语言的闪电般的编译速度主要得益于三个语言特性。第一点,所有导入的包必须在每个文件的开头显式声明,这样的话编译器就没有必要读取和分析整个源文件来判断包的依赖关系。第二点,禁止包的环状依赖,因为没有循环依赖,包的依赖关系形成一个有向无环图,每个包可以被独立编译,而且很可能是被并发编译。第三点,编译后包的目标文件不仅仅记录包本身的导出信息,目标文件同时还记录了包的依赖关系。因此,在编译一个包的时候,编译器只需要读取每个直接导入包的目标文件,而不需要遍历所有依赖的的文件(译注:很多都是重复的间接依赖)。
Go Modules是什么?
Go语言通过包管理来封装模块和复用代码,这里我们只介绍Go Modules管理方法
Go Modules于Go语言1.11版本时引入,在1.12版本正式支持,是由Go语言官方提供的包管理解决方案
Modules是相关Go包的集合,是源代码交换和版本控制的单元。go命令直接支持使用Modules,包括记录和解析对其他模块的依赖性
Go Modules的使用方法
环境变量
首先需要设置环境变量,可以使用go env命令查看当前配置。
$ go env
GO111MODULE="auto"
GOPROXY="https://proxy.golang.org,direct"
GONOPROXY=""
GOSUMDB="sum.golang.org"
GONOSUMDB=""
GOPRIVATE=""
如果需要更改 GO111MODULE ,可以使用go env命令
go env -w GO111MODULE=on
GO111MODULE
- auto:只要项目包含了 go.mod 文件的话启用 Go modules,目前在 Go1.11 至 Go1.14 中仍然是默认值。
- on:启用 Go modules,推荐设置,将会是未来版本中的默认值。
- off:禁用 Go modules,不推荐设置。
GOPROXY
此环境变量主要用于设计Go Module的代理
GOSUMDB
此环境变量用于在拉取模块的时候保证模块版本数据的一致性。
初始化模块
Go Modules的使用方法比较灵活,在目录下包含go.mod文件即可
首先通过如下命令创建一个新的Module
go mod init [module name]
然后当前目录会生成go.mod文件,其内容为:
module ModuleName
go 1.15
Go Modules会自动管理包,如果需要引入依赖,只需要在go.mod下添加以下内容(以gorose为例子)
module ModuleName
require (
github.com/gohouse/gorose v1.0.5
)
go get
go get
命令用于拉取新的依赖,以下为go get命令具体用法
go get | 拉取依赖,会进行指定性拉取(更新),并不会更新所依赖的其它模块。 |
---|---|
go get -u | 更新现有的依赖,会强制更新它所依赖的其它全部模块,不包括自身。 |
go get -u -t ./… | 更新所有直接依赖和间接依赖的模块版本,包括单元测试中用到的。 |
其他参数
-d 只下载不安装
-f 只有在你包含了 -u 参数的时候才有效,不让 -u 去验证 import 中的每一个都已经获取了,这对于本地 fork 的包特别有用
-fix 在获取源码之后先运行 fix,然后再去做其他的事情
-t 同时也下载需要为运行测试所需要的包
-u 强制使用网络去更新包和它的依赖包
-v 显示执行的命令
常用命令
go mod init // 初始化go.mod
go mod tidy // 更新依赖文件
go mod download // 下载依赖文件
go mod vendor // 将依赖转移至本地的vendor文件
go mod edit // 手动修改依赖文件
go mod graph // 查看现有的依赖结构
go mod verify // 校验依赖
参考资料
http://shouce.jb51.net/gopl-zh/ch10/ch10.html
https://github.com/datawhalechina/go-talent/blob/master/8.%E5%8C%85%E7%AE%A1%E7%90%86.md