學習筆記-go庫源碼文件

庫源碼文件

庫源碼文件是不能被直接運行的源碼文件,它僅用於存放程序實體,這些程序實體可以被其他代碼使用(只要遵從 Go 語言規範的話)。他代碼”可以與被使用的程序實體在同一個源碼文件內,也可以在其他源碼文件,甚至其他代碼包中。

1 怎樣把命令源碼文件中的代碼拆分到其他庫源碼文件?

cat demo4.go
package main  //這裏也申明瞭屬於main包
import (
    "flag"
)
var name string
func init() {
    flag.StringVar(&name, "name", "everyone", "The greeting object.")
}

func main() {
    flag.Parse()
    hello(name) //調用了代碼包一個叫作hello的函數
}
cat demo4_lib.go
package main  //這裏也申明瞭屬於main包
import "fmt"
func hello(name string) {
    fmt.Printf("Hello, %s!\n", name)
}
執行:go run demo4.go demo4_lib.go 
Hello, everyone!
demo4.go 和 demo4_lib.go 都放在了一個相對路徑爲q1的目錄中
$ ll
-rw-r--r--  1 daixuan  staff  178 11  6 22:13 demo4.go
-rw-r--r--  1 daixuan  staff   90 11  6 22:13 demo4_lib.go
$ go build .
ls
demo4.go        demo4_lib.go    q1//build一個q1文件
$ ./q1 
Hello, everyone!
$ ./q1 -name huaihe
Hello, huaihe!

代碼包聲明的基本規則。這裏再總結一下:
第一條規則,同目錄下的源碼文件的代碼包聲明語句要一致。也就是說,它們要同屬於一個代碼包。這對於所有源碼文件都是適用的。
如果目錄中有命令源碼文件,那麼其他種類的源碼文件也應該聲明屬於main包。這也是我們能夠成功構建和運行它們的前提。

第二條規則,源碼文件聲明的代碼包的名稱可以與其所在的目錄的名稱不同。在針對代碼包進行構建時,生成的結果文件的主名稱與其父目錄的名稱一致。
對於命令源碼文件而言,構建生成的可執行文件的主名稱會與其父目錄的名稱相同

模塊化編程的方式,根據代碼的功能和用途把它們放置到不同的代碼包中

2 怎樣把命令源碼文件中的代碼拆分到其他代碼包?

先搞定環境變量:

vim ~/.bash_profile 
export GOPATH=/Users/daixuan/pinduoduo/go:/Users/daixuan/pinduoduo/go/src/github.com/Golang_Puzzlers
source ~/.bash_profile 
$ echo $GOPATH
/Users/daixuan/pinduoduo/go:/Users/daixuan/pinduoduo/go/src/github.com/Golang_Puzzlers

查看代碼包目錄:

/Users/daixuan/pinduoduo/go/src/github.com/Golang_Puzzlers/src/puzzlers/article3/q2
daixuandeMacBook-Pro:q2 daixuan$ tree .
.
├── demo5.go
└── lib
    └── demo5_lib.go

1 directory, 2 files

查看代碼文件:

 cat demo5.go 
package main

import (
        "flag"

        "github.com/Golang_Puzzlers/src/puzzlers/article3/q2/lib"
)

var name string

func init() {
        flag.StringVar(&name, "name", "everyone", "The greeting object.")
}

func main() {
        flag.Parse()
        lib.Hello(name)
}

 cat lib/demo5_lib.go 
package lib5 //注意,這裏lib5應該與父目錄lib對應,否則報錯

import "fmt"

func Hello(name string) {
        fmt.Printf("Hello, %s!\n", name)
}

執行:

 go run demo5.go 
# command-line-arguments
./demo5.go:6:2: imported and not used: "github.com/Golang_Puzzlers/src/puzzlers/article3/q2/lib" as lib5
./demo5.go:17:2: undefined: lib

第一個錯誤提示的意思是,我們導入了puzzlers/article3/q2/lib包,但沒有實際使用其中的任何程序實體。這在 Go 語言中是不被允許的,在編譯時就會導致失敗。

注意,這裏還有另外一個線索,那就是“as lib5”。這說明雖然導入了代碼包puzzlers/article3/q2/lib,但是使用其中的程序實體的時候應該以lib5.爲限定符。這也就是第二個錯誤提示的原因了。Go 命令找不到lib.這個限定符對應的代碼包。
爲什麼會是這樣?根本原因就是,我們在源碼文件中聲明所屬的代碼包與其所在目錄的名稱不同。請記住,源碼文件所在的目錄相對於 src 目錄的相對路徑就是它的代碼包導入路徑,而實際使用其程序實體時給定的限定符要與它聲明所屬的代碼包名稱對應。
有兩個方式可以使上述構建成功完成。我在這裏選擇把 demo5_lib.go 文件中的代碼包聲明語句改爲package lib。理由是,爲了不讓該代碼包的使用者產生困惑,我們總是應該讓聲明的包名與其父目錄的名稱一致。

解決辦法:

vim lib/demo5_lib.go 
package lib5 //注意,這裏lib5修改爲lib,與父目錄lib對應

import "fmt"

func Hello(name string) {
        fmt.Printf("Hello, %s!\n", name)
}

執行不報錯:

$ go run demo5.go 
Hello, everyone!
$ go run demo5.go  -name huaihe
Hello, huaihe!

3. 什麼樣的程序實體纔可以被當前包外的代碼引用?

4.你可能會有疑問,我爲什麼要把 demo5_lib.go 文件中的那個函數名稱hello的首字母大寫?實際上這涉及了 Go 語言中對於程序實體訪問權限的規則。超級簡單,名稱的首字母爲大寫的程序實體纔可以被當前包外的代碼引用,否則它就只能被當前包內的其他代碼引用。通過名稱,Go 語言自然地把程序實體的訪問權限劃分爲了包級私有的和公開的。對於包級私有的程序實體,即使你導入了它所在的代碼包也無法引用到它。

4. 對於程序實體,還有其他的訪問權限規則嗎?

答案是肯定的。在 Go 1.5 及後續版本中,我們可以通過創建internal代碼包讓一些程序實體僅僅能被當前模塊中的其他代碼引用。這被稱爲 Go 程序實體的第三種訪問權限:模塊級私有。
具體規則是,internal代碼包中聲明的公開程序實體僅能被該代碼包的直接父包及其子包中的代碼引用。當然,引用前需要先導入這個internal包。對於其他代碼包,導入該internal包都是非法的,無法通過編譯。
“Golang_Puzzlers”項目的puzzlers/article3/q4包中有一個簡單的示例,可供你查看。你可以改動其中的代碼並體會internal包的作用。

總結

本篇文章中詳細討論了把代碼從命令源碼文件中拆分出來的方法,這包括拆分到其他庫源碼文件,以及拆分到其他代碼包。這裏涉及了幾條重要的 Go 語言基本編碼規則,
即:代碼包聲明規則、代碼包導入規則以及程序實體的訪問權限規則。在進行模塊化編程時,你必須記住這些規則,否則你的代碼很可能無法通過編譯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章