代碼管理總結
-
同一目錄下面源碼應該聲明屬於同一個代碼包,而且同一個包下面的源文件之間的函數調用是可以屏蔽大小寫(私有和共有接口)。
-
源碼文件聲明的包的名字可以和目錄不一致,但編譯生成的文件的名字和父目錄是一致的。
-
如果聲明的包的名字和目錄的名字不一致,那麼怎麼引用這個包裏面的接口??第一章裏面寫過了,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. 採取不同的包名聲明