包的管理和常用规范
包管理历程
大部分语言都有版本管理工具,比如nodejs的npm,python中的pip,java里的maven,但是go语言的版本管理经历了漫长的演进历程:
Go 1.5以前,golang使用GOPATH方式管理代码。
- 代码开发必须在 GOPATH 的 src 目录下。
- 依赖包没有版本可言,都是指 master 最新代码。这个阶段只能手动管理依赖。
开始 Golang 这么设计是有原因的,因为 Google 是一个实践 Mono Repo(把所有的相关项目都放在一个仓库中)的公司。但更多的公司和组织更多的是用 Multi Repo(按模块分为多个仓库),GOPATH 至少解决了第三方源码依赖的问题,虽然它还不够完美。
Go1.5后,有人提出了external packages 的概念,于是就有了Go Vendor。
- 解决了包依赖,用一个配置文件来管理。
- 依赖包全都下载到项目vendor下,每个项目都把有一份,不能夸项目共享公共依赖。
- 依赖包查找路径变成了 src/vendor ===> GOPATH。
- 在这种模式下,会将第三方依赖的源码下载到本地,不同项目下可以有自己不同的vendor,依然没有解决版本化问题。
- 默认是忽略vendor的,如果想在项目目录下有vendor可以执行
go vendor
命令,当然,如果构建程序的时候,希望使用vendor中的依赖go build -mod vendor
。
Go1.12 版本后, 使用go modules进行包管理。
- 不再以 GOPATH 为项目空间,使用 Modules 启用一个新的文件 go.mod 记录 module 的元信息,其中包含了依赖包的版本信息。
- 项目内会生成一个
go.mod
文件,列出包依赖。所有来的第三方包会准确的指定版本号对于已经转移的包,可以用 replace 申明替换,不需要改代码。 - 通过GO111MODULE控制,GO111MODULE 有三个值 off/on/auto(默认值),定义如下:
- off:go命令行将不会支持 module 功能,寻找依赖包的方式将会沿用旧版本那种通过 vendor 目录或者 GOPATH 模式来查找。
- on:go命令行会使用modules,而一点也不会去GOPATH目录下查找。
- auto:默认值,当项目在 $GOPATH/src 外,且项目根目录有 go.mod 文件时,开启模块支持。
- 当modules功能启用时,依赖包的存放位置变更为$GOPATH/pkg,允许同一个package多个版本并存,且多个项目可以共享缓存的module。
Vendor 介绍
为了解决 GOPATH
方式 三方依赖与项目都放在 GOPATH/src
下的问题,Go1.5之后,官方推出了 Go Vendor
的方式来管理项目。
go vendor
就是将依赖的包,特指外部包,复制到当前项目下的 vendor
目录下,这样 go build
的时候,go会优先从 vendor
目录寻找依赖包。
Go Vender 的出现解决了 GOPATH
不能多版本控制的问题。
但 Go Vender
放弃了依赖重用,使冗余度上升。同一个依赖包如果不同工程想复用,都必须各自复制一份在自己的 vendor
目录下。
Vendor 未解决的问题:无法精确的引用 外部包进行版本控制,不能指定引用某个特定版本的外部包,只是在开发时将其拷贝过来,但是一旦外部包升级,vendor 下面的包会跟着升级,而且 vendor 下面没有完整的引用包的版本信息, 对包升级带来了无法评估的风险。
Go Modules 介绍
大多数语言都有“依赖”、“包”等概念,Go语言的依赖处理经历了几次变革。
最早的时候,Go所依赖的所有的第三方库都放在 GOPATH
这个目录下面。从 go1.5
开始引入 vendor
模式,如果项目目录下有 vendor
目录,那么go工具链会优先使用 vendor
内的包进行编译、测试等。
从 go 1.11
开始,引入了 Go Modules
作为依赖解决方案,到 go 1.14
宣布 Go Modules
已经可以用于生产环境,到 go 1.16
版本开始 Go Module
默认开启。
Go modules
是 Go 语言的依赖解决方案,发布于 go1.11
,成长于 go1.12
,丰富于 go1.13
,正式于 go1.14
推荐在生产上使用。
Go Modules使得Go语言开发者能够更方便地管理代码包及其版本,并能够与现有的版本控制工具(如Git、SVN等)集成使用。
在传统的 GOPATH
模式中,所有Go代码都必须位于一个全局的 GOPATH
路径之下,这使得在不同项目中使用不同版本的依赖包变得非常困难。然而,在 Go Modules
模式下,每个项目都可以独立管理自己的依赖关系,具有更好的兼容性。当使用 Go Modules
模式后,项目中会自动创建 go.mod
文件,其中记录了项目所依赖的模块及其版本信息。go.mod
是Go语言项目中的模块文件,用于管理项目的依赖关系和版本信息。
Go Modules
也支持语义化版本控制,这意味着开发者可以指定依赖包的版本范围,而不是仅仅依赖最新的版本。这种灵活性有助于确保项目的稳定性和可维护性。
Go moudles
目前集成在 Go 的工具链中,只要安装了 Go,自然而然也就可以使用 Go moudles
了,而 Go modules
的出现也解决了在 go1.11
前的几个常见争议问题:
- Go 语言长久以来的依赖管理问题。
- “淘汰”现有的
GOPATH
的使用模式。 - 统一社区中的其它的依赖管理工具(提供迁移功能)。
优势:
- 首先,研发者能够在任何目录下工作,而不仅仅是在
GOPATH
指定的目录。 - 可以安装依赖包的指定版本,而不是只能从 main 分支安装最新的版本。
- 可以导入同一个依赖包的多个版本。当我们老项目使用老版本,新项目使用新版本时会非常有用。
- 要有一个能够罗列当前项目所依赖包的列表。这个的好处是当我们发布项目时不用同时发布所依赖的包。Go能够根据该文件自动下载对应的包。
命名规范
区分大小写
命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名。 Go语言从语法层面进行了以下限定:任何需要对外暴露的名字必须以大写字母开头,不需要对外暴露的则应该以小写字母开头。
go文件名
文件名一律使用小写,不同单词之间用下划线分割, 命名应尽可能地见名知意。
go文件名一般以.go结尾,如:main.go、main_test.go、db.go、db_test.go 等。
包名
包名使用小写单词,不要使用下划线或者混合大小写。例如,包:tabwriter
不应该命名为 tabWriter
、 TabWriter
或 tab_writer
。
示例:
import (
"flag"
"fmt"
"github.com/BurntSushi/toml"
"github.com/pingcap/errors"
)
变量
使用驼峰命名法(CamelCase)由有意义的英文单词拼接而成。比如说 UpperName
,lowName
区别就是前者是大驼峰是导出的变量,后者是小驼峰是不可导出变量。
示例:
var userName string // 不可导出
var UserName string // 可导出
常量
常量均需使用全部大写字母组成,并使用下划线分词。
示例:
const MaxLength = 100
const PI = 3.14159
如果是枚举类型的常量,需要先创建相应类型:
type Scheme string
const (
HTTP Scheme = "http"
HTTPS Scheme = "https"
)
结构体
采用驼峰命名法,首字母根据访问控制大写或者小写,例如:结构体 UserInfo
和 userInfo
,前者是所有包都可以访问,后者是当前包内可以访问。
Struct 申明和初始化格式采用多行,例如下面:
// 多行申明
type User struct{
Username string
Email string
}
// 多行初始化
u := User{
Username: "astaxie",
Email: "[email protected]",
}
接收者命名
接收者变量名必须满足:短(通常是一两个字母的长度),类型本身的缩写,始终如一地应用于该类型的每个接收者。
长名称 | 更好命名 |
---|---|
func (tray Tray) | func (t Tray) |
func (info *ResearchInfo) | func (ri *ResearchInfo) |
func (this *ReportWriter) | func (w *ReportWriter) |
func (self *Scanner) | func (s *Scanner) |
示例:
type Person struct {
Name string
Age int
}
// 这里的方法定义的指针变量p就是接收者
func (p *Person) ChangeName(newName string) {
p.Name = newName
}