代码管理总结
-
同一目录下面源码应该声明属于同一个代码包,而且同一个包下面的源文件之间的函数调用是可以屏蔽大小写(私有和共有接口)。
-
源码文件声明的包的名字可以和目录不一致,但编译生成的文件的名字和父目录是一致的。
-
如果声明的包的名字和目录的名字不一致,那么怎么引用这个包里面的接口??第一章里面写过了,go是按照文件地址寻址的。所以规则如下:
import 文件夹的路径
包名.接口名字()
我们导入的时候肯定是文件的路径,但调用的时候要用在源码文件里面声明的package的名字。
- 程序实体的访问规则,通过大小写来决定接口的访问规则,首字母大写代表的是公开的,小写代表的是只有同一个包下面可以引用。
代码示例
下面是程序的主体,但是其中调用了square()函数,这个函数并没有声明:
// Go36/article03/example01/demo.go
package main
import (
"flag"
"fmt"
)
var x int
func init() {
flag.IntVar(&x, "x", 0, "计算平方")
}
func main() {
flag.Parse()
res := square(x)
fmt.Println(x, "的平方:", res)
}
上面用到的square()函数被声明在了另一个文件中:
// Go36/article03/example01/calc.go
package main
func square(x int) int {
return x * x
}
这里只声明了一个函数,可以在这个写更多关于计算的函数,这样在同个包里都可以方便的调用这些函数使用。
上面的2个文件都要在同一个目录下,并且需要被声明为属于同一个包。
执行代码
因为示例中都声明为main包,并且包里也有一个main函数。所以存在一个命令源码文件,这样就可以直接运行起来:
PS H:\Go\src\Go36\article03\example01> go run demo.go calc.go -x 3
3 的平方: 9
PS H:\Go\src\Go36\article03\example01>
上面注意要把所有的文件都写在命令里。
还可以先构建代码包,在执行:
PS H:\Go\src> go build Go36/article03/example01
PS H:\Go\src> .\example01.exe -x 4
4 的平方: 16
PS H:\Go\src>
把代码拆分到多个包
先修改calc.go的路径,并且做一些修改:
// Go36/article03/example02/lib/calc.go
package lib2
func Square(x int) int {
return x * x
}
这里创建了一个子目录,把文件放到了这个子目录中,这样使得它同命令源码文件不在同一个目录下了。
并行代码也做了一些修改:
包名变成了lib2,这里故意和目录不是同一个名字
函数名的首字母变成了大写
包名和目录名不同
现在要使用上面的包。导入包的路径应该是目录的路径名称:
import (
"Go36/article03/example02/lib"
)
如果要构建或者安装这个代码包,使用的命令应该是下面这样,还是用目录名称:
go install Go36/article03/example02/lib
并且命令成功后,pkg子目录产生的归档文件也是目录名称:
pkg\windows_amd64\Go36\article03\example02\lib.a
但是最后调用的时候需要使用包名称,命令源码文件的代码如下:
// Go36/article03/example02/demo.go
package main
import (
"flag"
"fmt"
"Go36/article03/example02/lib"
)
var x int
func init() {
flag.IntVar(&x, "x", 0, "计算平方")
}
func main() {
flag.Parse()
res := lib2.Square(x)
fmt.Println(x, "的平方:", res)
}
上面调用程序时使用的lib2.称为限定符。
结论:导入路径使用的是文件所在目录的路径。而调用程序时使用的限定符要与它声明的包的名称一致。
为了不在使用代码包是产生困惑,应该让声明的包的名称与其父目录的目录名称一致。
访问权限
在这里把函数名称的首字母改为大写的原因是,名称的首字母为大写的程序实体才可以被当前包外的代码引用,否则它就只能被当前包内的其他代码引用。
这涉及了Go语言中对于程序实体访问权限的规则。通过名称的首字母的大小写,就把访问权限分为了包级私有和公开这两种。对于包级私有,只有在包内部可以访问。由于我们需要在main包里调用lib包的函数,只能访问到公开的部分,所以需要把函数的首字母大写。
模块级私有 上面的访问权限都以包的级别进行划分的。在Go 1.5及后续版本中,可以通过创建internal代码包让一些程序实体仅仅能被当前模块中的其他代码引用。这是第三种访问权限:模块级私有。
具体规则是,internal代码包中声明的公开程序实体仅能被该代码包的直接父包及其子包中的代码引用。当然,引用前需要先导入这个internal包。对于其他代码包,导入该internal包都是非法的,无法通过编译。
这里的名称必须是internal,示例如下:
// 父级目录 Go36/article03/example03/demo.go
package main
import (
"flag"
"Go36/article03/example03/lib"
//"Go36/article03/example03/lib/internal" // 此行无法通过编译。
)
var x int
func init() {
flag.IntVar(&x, "x", 0, "计算平方")
}
func main() {
flag.Parse()
lib.Cale(x)
//res := internal.Square(x)
//lib.Cale(res)
}
// 子级目录 Go36/article03/example03/lib/demo_lib.go
package lib
import (
"fmt"
"Go36/article03/example03/lib/internal"
)
func Cale(x int) {
res := internal.Square(x)
fmt.Println(x, "的平方:", res)
}
// 孙子目录 Go36/article03/example03/lib/internal/internal.go
package internal
func Square(x int) int {
return x * x
}
模块级私有的internal包,仅能被直接父包及其子包中的代码引用。上面如果要在父级里调用孙级目录的internal包,就是非法的:
PS H:\Go\src\Go36\article03\example03> go run demo.go -x 7
demo.go:6:2: use of internal package not allowed
PS H:\Go\src\Go36\article03\example03>
internal包
这个东西还是第一次见,按照老师说的:
internal 仅能被直接父级和子包引用。这个意思是什么那??
假设我们工程的目录结构如下:
q4
- lib
- internal
-test
-test2
-demo9.go
-demo8.go
internal.go
- demo7.go
- demo6.go
q4目录下面有lib子目录和demo6.go,lib下面有Internal目录和demo7.go,internal目录下面有internal,go和test目录,test目录下面有test2目录,test2下面有demo9.go,这样除了demo6不能引用internal.go,其它的都属于inter的父目录和子目录下面的,所以可以引用。
思考题
1. 如果你需要导入两个代码包,而这两个代码包的导入路径的最后一级是相同的,比如:dep/lib/flag和flag,那么会产生冲突吗?
分两种情况:
1.如果目录名字相同 && 源文件里面声明的package名字一样,那么肯定是冲突的。
2.如果目录名字相同但源文件里面声明的package名字不同,那么就不存在冲突
2. 如果会产生冲突,那么怎么解决这种冲突,有几种方式?
a. 给包设置别名,调用的时候来区分开不同的package,比如:import(b "bbbb")
b. 导入的点操作,import(. "bbbb")。这样就可以直接调用bbbb下面的函数而不用再bbbb.funcname的方式调用。
c. 如果只是想引入某包并没有在代码中实际调用则可以这么处理来避免冲突:import(_ "bbbb")
d. 采取不同的包名声明