在 Golang 中,代码包是代码编译和安装的基本单元,也是非常直观的代码组织形式。
在 Go 语言中,代码包中的源码文件可以任意命名。另外,这些任意名称的源码文件都必须以包声明语句作为文件中代码的第一行。比如:
package "base"
其中 package 是 Go 中用于包声明语句的关键字。Go 规定包声明中的包名是代码包路径的最后一个元素。
比如,gopcp.v2/helper/log/base
包的源码文件包声明中的包名是 base。但是,不论命令源码文件存放在哪个代码包中,它都必须声明属于 main 包。
代码包 gopcp.v2/helper/log
中的 logger.go
需要依赖 base
子包和 logrus
子包,因此需要在源码文件中使用代码包导入语句,如下所示:
import "gopcp.v2/helper/log/base"
import "gopcp.v2/helper/log/logrus"
这需要用到代码包导入路径,即代码包在工作区的 src 目录下的相对路径。当导入多个代码包时,可以用圆括号括起它们,且每个代码包名独占一行。
在使用被导入代码包中公开的程序实体时,需要使用包路径的最后一个元素加的方式指定代码所在的包。因此,上述语句可以写成:
import (
"github.com/Sirupsen/logrus"
mylogrus "gopcp.v2/helper/log/logrus"
)
同一个源码文件中导入的多个代码包的最后一个元素不能重复,否则一旦使用其中的程序实体,就会引起编译错误。但是,如果你只导入不使用,同样会引起编译错误。一个解决方法是为其中一个起个别名,比如:
import (
"github.com/Sirupsen/logrus"
mylogrus "gopcp.v2/helper/log/logrus"
)
如果我们想不加前缀而直接使用某个依赖包中的程序实体,就可以用来代替别名,如下所示:
import (
. "gopcp.v2/helper/log/logrus"
)
看到那个“.”了吗?之后,在当前源码文件中,我们就可以这样做了:
var logger = NewLogger("gopcp") // NewLogger 是 gopcp.v2/helper/log/logrus 包中的函数
Go 中的 变量、常量、函数 和类型声明可统称为程序实体,而它们的名称统称为 标识符。
标识符可以是 Unicode 字符集中任意能表示自然语言文字的字符、数字以及下划线 (_)。标识符不能以数字或下划线开头。
实际上,标识符的首字符的大小写控制着对应程序实体的访问权限。如果标识符的首字符是大写形式,那么它所对应的程序实体就可以被本代码包之外的代码访问到,也称为可导出的或公开的;否则,对应的程序实体就只能被本包内的代码访问,也称为不可导岀的或包级私有的。要想成为可导出的程序实体,还需要额外满足以下两个条件。
程序实体必须是非局部的。局部的程序实体是指:它被定义在了函数或结构体的内部。
代码包所属目录必须包含在 GOPATH 中定义的工作区目录中。代码包导入还有另外一种情况:如果只想初始化某个代码包,而不需要在当前源码文件中使用那个代码包中的任何程序实体,就可以用“_”来代替别名:
import (
_ "github.com/Simpsen/logrus"
)
这种情况下,我们只是触发了这个代码包中的初始化操作(如果有的话)。符号 “_” 就像一个垃圾桶,它在代码中使用很广泛,在后续的学习中还可以看到它的身影。
在 Go 语言中,可以有专门的函数负责代码包初始化,称为代码包初始化函数。这个函数需要无任何参数声明和结果声明,且名称必须为 init,如下所示:
func init() {
fmt.Println("Initialize...")
}
Go 会在程序真正执行前对整个程序的依赖进行分析,并初始化相关的代码包。也就是说,所有的代码包初始化函数都会在 main 函数(命令源码文件中的入口函数)执行前执行完毕,而且只会执行一次。
另外,对于每一个代码包来说,其中的所有全局变量的初始化,都会在代码包的初始化函数执行前完成。这避免了在代码包初始化函数对某个变量进行赋值之后,又被该变量声明中赋予的值覆盖掉的问题。
在 Golang 中,代码包是代码编译和安装的基本单元,也是非常直观的代码组织形式。