天天看點

golang-庫源碼檔案庫源碼檔案

庫源碼檔案

庫源碼檔案是不能被直接運作的源碼檔案,它僅用于存放程式實體,這些程式實體可以被其他代碼使用(隻要遵從 Go 語言規範的話)。

其他代碼:可以與被使用的程式實體在同一個源碼檔案内,也可以在其他源碼檔案,甚至其他代碼包中

那麼什麼是程式實體?

在 Go 語言中,程式實體是變量、常量、函數、結構體和接口的統稱。我們總是會先聲明(或者說定義)程式實體,然後再去使用。我們先定義了變量

name

,然後在

main

函數中調用

fmt.Printf

函數的時候用到了它。再多說一點,程式實體的名字被統稱為辨別符。辨別符可以是任何

Unicode

編碼可以表示的字母字元、數字以及下劃線“_”,但是其首字母不能是數字。從規則上說,我們可以用中文作為變量的名字。但是,我覺得這種命名方式非常不好,自己也會在開發團隊中明令禁止這種做法。作為一名合格的程式員,我們應該向着編寫國際水準的程式無限逼近。

拆分指令源碼

怎樣把指令源碼檔案中的代碼拆分到其他庫源碼檔案?

某個目錄下有一個指令源碼檔案 demo4.go,如下:

package main

import (
  "flag"
)

var name string

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

func main() {
  flag.Parse()
  hello(name)
}
           

調用了一個叫作

hello

的函數,該函數放在lib.go:

package main

import "fmt"

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

可以看到,同樣聲明了

package main

,main.go 中才沒有報錯且可以執行。

執行指令看看:

MacBook-Pro demo % go run src/main.go src/lib.go
Hello, everyone!
           

可以看到:執行指令:

o run src/main.go src/lib.go

,才能執行完整,不能單獨隻

run main.go

!

為什麼?

  • 在同一個目錄下的源碼檔案都需要被聲明為屬于同一個代碼包
  • 如果該目錄下有一個指令源碼檔案,那麼為了讓同在一個目錄下的檔案都通過編譯,其他源碼檔案應該也聲明屬于main包。

或者,像下面這樣先建構目前的代碼包再運作:

$ go build puzzlers/article3/q1
$ ./q1            
Hello, everyone!
           

在這裡,我把 main.go 和 lib.go 都放在了一個相對路徑為puzzlers/article3/q1的目錄中。

  • 源碼檔案聲明的包名可以與其所在目錄的名稱不同,隻要這些檔案聲明的包名一緻就可以。

問題解析

  • 第一條規則,同目錄下的源碼檔案的代碼包聲明語句要一緻。也就是說,它們要同屬于一個代碼包。這對于所有源碼檔案都是适用的。
    • 如果目錄中有指令源碼檔案,那麼其他種類的源碼檔案也應該聲明屬于

      main

      包。這也是我們能夠成功建構和運作它們的前提。
  • 第二條規則,源碼檔案聲明的代碼包的名稱可以與其所在的目錄的名稱不同。在針對代碼包進行建構時,生成的結果檔案的主名稱與其父目錄的名稱一緻。
    • 對于指令源碼檔案而言,建構生成的可執行檔案的主名稱會與其父目錄的名稱相同

在編寫真正的程式時,我們僅僅把代碼拆分到幾個源碼檔案中是不夠的。我們往往會用子產品化程式設計的方式,根據代碼的功能和用途把它們放置到不同的代碼包中。不過,這又會牽扯進一些 Go 語言的代碼組織規則

怎樣把指令源碼檔案中的代碼拆分到其他代碼包?

其實拆分很簡單,類似子產品化程式設計,隻不過golang需要拆分出不同的package, 不需要與目錄同名

  • 拆分到其他庫的代碼,函數要從小寫開頭改成大寫開頭, 表示可以被外部引用——沒錯,這是go的風格,獨樹一幟吧?

導包到底是導目錄名?還是package name?

我們在源碼檔案中聲明所屬的代碼包與其所在目錄的名稱不同。請記住,源碼檔案所在的目錄相對于 src 目錄的相對路徑就是它的代碼包導入路徑,而實際使用其程式實體時給定的限定符要與它聲明所屬的代碼包名稱對應——也就是說,導的是目錄名——package name主要是給整個屬于package的代碼使用友善的——個人了解

規範來了:

為了不讓該代碼包的使用者産生困惑,我們總是應該讓聲明的包名與其父目錄的名稱一緻

什麼樣的程式實體才可以被目前包外的代碼引用?

超級簡單,名稱的首字母為大寫的程式實體才可以被目前包外的代碼引用,否則它就隻能被目前包内的其他代碼引用。

通過名稱,Go 語言自然地把程式實體的通路權限劃分為了包級私有的和公開的。對于包級私有的程式實體,即使你導入了它所在的代碼包也無法引用到它。

對于程式實體,還有其他的通路權限規則嗎?

答案是肯定的。在 Go 1.5 及後續版本中,我們可以通過建立internal代碼包讓一些程式實體僅僅能被目前子產品中的其他代碼引用。這被稱為 Go 程式實體的第三種通路權限:子產品級私有。

具體規則是,internal代碼包中聲明的公開程式實體僅能被該代碼包的直接父包及其子包中的代碼引用。當然,引用前需要先導入這個internal包。對于其他代碼包,導入該internal包都是非法的,無法通過編譯。

看下目錄:

golang-庫源碼檔案庫源碼檔案

總結

讨論了把代碼從指令源碼檔案中拆分出來的方法,這包括拆分到其他庫源碼檔案,以及拆分到其他代碼包。

基本編碼規則

  • 代碼包聲明規則
  • 代碼包導入規則
  • 程式實體的通路權限規則

如果你需要導入兩個代碼包,而這兩個代碼包的導入路徑的最後一級是相同的,比如:dep/lib/flag和flag,那麼會産生沖突嗎?

!答案: 取别名!

繼續閱讀