对于 Go 这门语言来说,最常见的就是使用各种各样的包来进行代码功能复用。对于 Go 语言初学者而言,Go 的包管理可能会带来一些困扰,本博客就笔者 Go 入门编程时的一些经验与困扰作一些总结。
两个关于 Go 的包管理的常用命令
首先,使用 Go 不可避免地要与以下两个涉及 Go 的包管理的常用的命令打交道 —— go mod
、go get
。首先明晰一下二者的功能:
go mod
go mod
的作用是用来管理当前开发的 Go 的逻辑单元,也就是一个 module
。对于一个从零开始的 Go 项目,为了让 Go 工具链正常管理项目,需要先执行 go mod init
命令来初始化当前的逻辑 module。这个 module 名字也是后续其他人复用这个项目代码包的标识,所以命名上往往要兼顾唯一性来对其命名,比如 git.woa.com/modnarshen/uasvr-go/dbproxysvr
。
在开发过程中,随着使用的包的不断更新,一些包的依赖关系可能发生变化,此时可以使用 go mod tidy
命令来进行包整理。此命令可以在 go.mod
中移除掉那些不再使用的包,从而使项目的包变得更加清爽。协同开发过程中,由于 go.mod 和 go.sum 都是由 go 的工具链自动写入的,因此提交代码到远程前使用 go mod tidy
就是一个有必要的操作,这样可以避免一些改动引入的包变化未同步给其他人带来的衍生问题。
go get
go get
的作用看起来更像是一个包下载器,因为在实际应用过程中,此命令主要用来进行包的下载安装,以及包的更新(从某种角度而言,涉及网络的包资源管理,一般都要用到此命令)。举个例子,对于某个开源的 Go 项目,将其 clone 到本地以后,往往可能无法直接编译项目本身,因为这个项目依赖的一些第三方包还没有“安装”。此时,项目所需的包类型、包版本都已指定在 go.mod
文件中,在项目中相应目录下(go.mod 文件同级或子目录层级)执行 go get
命令即可自动检索并确定需要安装哪些包、是否需要下载不满足版本需求的包、执行下载所需包的任务,这个过程是 go get
来执行的,使用者只需简单执行一下这个命令就可以了。
同理,在开发过程中,如果依赖的包拥有了新的版本,还可以使用 go get
来进行包资源更新。直接添加 -u
参数即可。更新后的包版本,会同步写入 go.mod 文件并更新 go.sum 文件。其命令形式可参考 go help get
来具体查看,这里仅做一个简单的介绍及辨析。
FAQ
Go 的包都是以开源源码的方式提供的吗?能否像 C++ 一样提供类似库的二进制文件以达到隐藏实现细节的目的?
是的。这也是 Go 和诸多编程语言差异比较大的一点。对其他语言而言,提供“库文件”可以省去编译阶段的大量时间,而对于 Go 而言,缩减编译时间也是 Go 创建时着重优化的重点之一,因此这样带来的第三方包代码重编带来的损耗往往不值一提。这样带来的好处就是第三方的实现细节都可以通过源码确定,而对于源码提供者而言不那么美好的一点就是无法隐藏具体的实现逻辑了。
辨析 Go 的 module 和 package?
逻辑上,module 往往是一些 package 的集合。举个例子,创建了一个名为 dbproxysvr
的 module 之后,依然可以在其下创建一个名为 table
的子目录,此时无需在 table
目录下继续生成 go.mod 文件,而是可以直接使用 dbproxysvr/table
来进行代码功能复用。由此可见,对于 Go 而言,一个良好的设计习惯是将提供逻辑相关的功能合集定义为一个 package,物理上组织为一个单独的目录,这个目录尽可能与源码中的 package 同名,由此使得逻辑功能上、代码组织上,都十分清晰。
尽管我们的代码引入往往是用 package
来进行逻辑组织的,但实际通过上述 go 的包管理命令下载的、更新的,依然是相应的 module,也就是说,代码中复用的单元是 package,资源上管理更新的单元是 module。