GO语言学习笔记-包结构篇 Study for Go ! Chapter eight - Package Structure
持续更新 Go 语言学习进度中 ......
- GO语言学习笔记-类型篇 Study for Go! Chapter one - Type - slowlydance2me - 博客园 (cnblogs.com)
- GO语言学习笔记-表达式篇 Study for Go ! Chapter two - Expression - slowlydance2me - 博客园 (cnblogs.com)
- GO语言学习笔记-函数篇 Study for Go ! Chapter three - Function - slowlydance2me - 博客园 (cnblogs.com)
Study for Go ! Chapter eight - Package Structure
1. Workspace
工作空间主要由 src、bin、pkg 三个目录组成。通常需要将空间路径添加到 GOPATH 环境变量列表中,一遍相关工具能正常工作
在工作空间里,包括子包在内的所有源码文件都保存在 src 目录下。至于bin、pkg两个目录,其主要影响 go install/get 命令,它们会将编译结果 (可执行文件或静态库)安装到这两个目录下,以实现增量编译
环境变量
编译器等相关工具按 GOPATH 设置的路径搜索目标。也就是说在导入目标库时,排在列表前面的路径比当前工作空间优先级更高
另外,go get 默认将下载的第三方包保存到列表中第一个工作空间内
环境变量 GOROOT 用于指示工具链和标准库的存放位置。在生成工具链时,相关路径就已经嵌入到可执行文件内,故无须格外设置,但如果出现类似下面这样的错误提示,请检查路径是否一致
除通过设置 GOROOT 环境变量覆盖内部路径外,还可以移动目录 (改名、符号连接等),或重新编译工具链来解决
至于 GOBIN ,则是强制代替工作空间的 bin 目录,作为 go install 目标保存路径,这可避免将所有工作空间的 bin 路径 添加到 PATH 环境变量中
在使用 Git 等版本控制工具时,建议忽略 pkg、bin 目录。直接在 src, 或具体的子包下创建代码仓库 (repository)
2. 导入包
使用标准库或第三方包前,需使用 import 导入,参数是工作空间中以 src 为七十的绝对路径。编译器从标准库开始搜索,然后依次搜索 GOPATH 列表中的各个工作空间
除了使用默认包名外还可以使用别名,以解决同名冲突问题
Attention;
import 导入参数是路径,而非包名,尽管习惯将包和目录名保持一致,但这不是强制规定,在代码中引用包成员时,使用包名而非目录名
有四种导入方式
默认方式
别名方式
简便方式 (常用于单元测试代码中,不推荐在正式项目于代码中使用。)
初始化方式 (无法引用,仅用来初始化目标包,让目标包的初始化函数得以执行)
不能直接或者间接导入自己,不支持任何形式的循环导入
未使用的导入(不包含初始化方式)会被编译器视为错误
相对路径
除了工作空间和绝对路径外,部分工具还支持相对路径。可在非工作空间目录下,直接运行,编译一些测试代码
相对路径是指:当前目录,或以“ ./ ” 和 “ ../ ” 开头的路径
不管是否在 test 目录下,只要命令行路径正确,就可以用go build/run/test 进行编译,运行或测试。但因缺少工作空间相关目录,go install 会无法工作
在设置了 GOPATH 的工作空间中,相对路径会导致编译失败
go run 不受此影响,可正常执行
自定义路径
即便将代码托管在 GitHub ,但我们依然希望使用自有域名定义下载和导入路径。方法很简单,在 Web 服务器对应路径返回中包含 “ go-import ” 跳转信息即可
使用唯一的导入路径,方便日后迁移存储端,但此方法对 vendor 机制无效
3. 组织结构
包由一个或多个保存在同一目录下(不含子目录)的源码文件组成。包的用途类似名字空间(namespace),是成员作用域和访问权限的边界
包名和目录名并无关系,不要求保持一致
包名常用单数形式
源码文件必须使用 UTF-8 格式,否则会导致编译出错
同一目录下所有源码文件必须使用相同包名称,因导入是使用绝对路径,所以在搜索路径下,包必须有唯一路径,但无须是唯一名字
有几个被保留、有特殊含义的包名称;
main:可执行入口(入口函数 mian.mian)
all:标准库以及 GOPATH 中能找到的所有包
std,cmd:标准库及工具链
documentation:储存文档信息,无法导入(和目录名无关)
(相关工具忽略以” . “ 或 ” _ “ 开头的目录或文件,但是又允许导入保存在这些目录中的包! )
权限
所有成员在包内均可访问,无论是否在同一源码文件中。但只有名称首字母大写的为可导出成员,在包外可视 (该规则适用于全局变量、全局常量、类型、结构字段、函数、方法等)
可通过指针转换等方式绕开该限制
初始化
包内每个源码文件都可定义一到多个初始化函数,但编译器不保证执行次序。
实际上,所有这些初始化函数(包括标准库和导入的第三方包)都由编译器自动生成的一个包装函数进行调用,因此可保证在单一线程上执行,且只执行一次
编译器首先确保完成所有全局变量初始化,然后才开始执行初始化函数,知道这些全部结束后,运行时才正式进入 main.main 入口函数
可以在初始化函数中创建 goroutine,或等到它执行结束
如果在多个初始化函数中引用全局变量,那么最好在变量定义出直接赋值,因无法保证执行次序,所以任何初始化函数中的赋值都有可能“ 延迟无效 ”
延迟包
在进行代码重构时,我们会将一些内部模块陆续分离出来,以独立包形式维护。此时,基于首字母大小写的访问权限控制就显得过于粗犷。因为我们希望这些包导出成员仅在特定范围内访问,而不是向所有用户公开
内部包机制相当于增加了新的访问权限控制:所有保存在 internal 目录下的包 ( 包括自身 )仅能被其父目录下的包 ( 含所有层次的子目录 )访问
导入内部包必须使用完整路径
4. 依赖管理
如何管理和保存第三方包,一致存在争议。将项目所有的第三方依赖都放到一个独立工作空间中,可能会导致版本冲突。放到项目工作空间,又会把工作目录搞的面目全非。为此,引入了名为 vendor 的机制,专门存放第三方包,实现将源码和依赖完整打包分发
如果说 internal 针对内部,那么 vender 就是 针对外部 (external)
导入 vendor 中的第三方包,参数是以 vendor/ 为起点的绝对路径。这就避免了 vendor 目录位置带来的麻烦,让导入无论使用 vender,还是 GOPATH 都能保持一致
ATTENTION
vendor 比标准库优先级更高
question:当多个 vendor 目录嵌套时,如何正确查找目标 ?要知道引入的第三方包也可能存在有自己的 vendor 依赖目录
answer:从当前源文件所在目录开始,逐级向上构造 vendor 全路径,知道发现路径匹配的目标为止。匹配失败,则依旧搜索 GOPATH
要使用 vendor机制,须开启 ” GO15VENDOREXPERIMENT=1 “ 环境变量开关 ( GO 1.6 以上默认开启 )且必须是设置了 GOPATH 的工作空间
使用 go get 下载第三方包时,依旧使用 GOPATH 第一个工作空间,而非 vendor 目录。当前工具链中并没有真正意义上的包管理依赖,好在有不少的第三方工具可以选择
- 上一篇 »Go语言基础语法笔记
- 下一篇 »go语言方法-学习笔记 - 长方形