1. 程式人生 > >Go語言學習2----程式碼組織之包

Go語言學習2----程式碼組織之包

上一篇檔案中已經學習到go語言的工程結構,都是放在GOPATH下的src目錄下,大部分高階程式語言都有包的概念像java、python等,即使沒有包的概念原始碼也會以不同的目錄來組織以方便大型軟體原始碼的管理,像c/c++等。go語言也是有包的概念的,個人感覺與java的包比較類似。所謂的包就是原始碼所在的相對路徑

原始碼檔案

包只是組織原始碼檔案的形式,所以首先要熟悉原始碼檔案。Go語言的原始碼檔案分為三類:命令原始碼檔案、庫原始碼檔案和測試原始碼檔案。

注意:Go原始碼檔案需要以UTF-8編碼儲存。

命令原始碼檔案

  • 被宣告為屬於main程式碼包且程式碼中包含無引數宣告和結果宣告的main函式,相當於程式的入口。
  • 同一個程式碼包中可以有多個命令原始碼檔案,可以通過go run命令後面加上相應命令原始碼檔名來分別執行
  • 同一個程式碼包中如果有多個命令原始碼檔案,則go build和go install命令無法編譯和安裝該程式碼包。
  • 如果命令原始碼檔案和庫原始碼檔案(定義後面給出)處於同一個程式碼包中,則go build和go install命令無法編譯和安裝該程式碼包。

根據以上要求,一般情況下將命令原始碼檔案放在單獨的包中,並且如果有多個命令原始碼檔案的話也都單獨存放在不同的包中。

庫原始碼檔案

  • 宣告的包名會與它實際所屬的程式碼包(目錄)名一致,且程式碼中不包含無引數宣告和結果宣告的main函式
  • go install命令安裝庫原始碼檔案時,是以整個包一起編譯並歸檔,歸檔檔名以包的最後一級目錄名命名,字尾為“.a”,歸檔目錄為工作區下的pkg的平臺相關目錄下

測試原始碼檔案

  • 是一種特殊的庫原始碼檔案,可以通過go test命令運行當前程式碼包下的所有測試原始碼檔案
  • 測試原始碼檔案必須以"XXX_test.go"形式命令,即必須以“_test.go”結尾。
  • 檔案中需要至少包含一個名稱以“Test”開頭或者“Benchmark”開頭、擁有一個型別為“testing.T”或者“testing.B”的引數的函式。

程式碼包

Go語言中的程式碼包是對程式碼進行構建和打包的基本單元。

包宣告

  • 與java等語言一樣,go的包宣告關鍵字為package
  • 與java不一樣的地方是,go語言的包名為原始碼檔案所在路徑的最後一級目錄的目錄名(即原始碼檔案所在目錄的目錄名,而不是多個目錄名以“.”連線)
  • 命令原始碼檔案不管在哪個包中,其宣告的包名都必須是main包

包匯入

  • 與java等語言一樣,go的包匯入關鍵字為import
  • java語言的import只能匯入檔案(匯入路徑以檔名結束,或者以“*”號結束),而go語言只能匯入包名
  • 程式碼包的匯入使用程式碼包匯入路徑,匯入路徑就是工作區下的src目錄下的相對路徑。例如go原始碼檔案所在路徑為$GOPATH/src/hello/log/logging.go,則此原始碼檔案的匯入路徑為hello/log
  • 當匯入多個程式碼包時,即可以對每個程式碼包都使用import關鍵字,也可以所有需要匯入的程式碼包共用一個import關鍵字,後一種需要用圓括號把包名括起來,並且每個包名也是獨佔一行
  • 呼叫匯入包中的變數或者函式方法:<匯入包名中最後一個目錄名稱>.<變數名/函式名>
import "a/b"
import "a/c"

與下面是等價的
import (
    "a/b"
    "a/c"
)
例如需要呼叫a/b包中的函式test(),在程式碼中就可以這樣寫:b.test()

注意:
  • 匯入包時禁止迴圈依賴,例如a依賴於b,而b又依賴於a,這是不允許的。
  • 如果要匯入多個程式碼包,那麼程式碼包路徑的最後一個目錄名稱不可以重複,否則編譯時就會報重複定義的錯誤,這個是由於go語言包宣告規則導致的。

但是這種情況go語言有個解決方法:就是在匯入時給相應衝突包起個別名,如下

import (
    La  "logging"
    Lb  "test/logging"
)

這樣在使用時直接使用別名(La和Lb)來代替相應匯入包名就可以區分具體使用的是哪個包中的變數或者函數了。

  • 如果需要直接呼叫某個依賴包的程式,可以用"."來代替別名
import (
    .  "a/b"
    "c/d"
)

例如需要呼叫a/b包中的函式test(),在程式碼中直接呼叫test()函式即可,而不需要再加上"b."了

包初始化

在Go語言中無引數宣告和結果宣告並且名為init的函式【func init() {XXX}】專門負責程式碼包的初始化工作。

  • 包初始化函式會在main函式之前執行,並且只執行一次
  • 對於一個程式碼包來說,其中的所有全域性變數的初始化都會在包初始化函式之前執行,避免在init函式中對某個變數賦值不成功的情況(即被相應變數的宣告時賦的值覆蓋)
  • 在同一個程式碼包中,可以存在多個程式碼包初始化函式 
  • 在同一個原始碼檔案中也可以存在多個程式碼包初始化函式

注意:

  • go語言編譯器不能保證同一個程式碼包中的多個程式碼包初始化函式的執行順序,所以要麼一個程式碼包中只寫一個程式碼包初始化函式,要麼使用併發模型來保證執行順序
  • 被匯入的程式碼包初始化函式總是會在匯入它的那個程式碼包的初始化函式之前執行

遺留問題:

  • 原始碼檔案中宣告包名為main,但是又不包含main入口函式的原始碼檔案,根據定義好像本章說的三種都不滿足?
  • 在我現在看過的資料中都沒有說明原始碼檔案是否可以直接放在工作區的src目錄下,而放在工作區的src目錄下之後,包應該怎麼宣告?宣告成main包?

後續研究好後再補充,各位大牛如果看到也可解惑一二,在此不勝感激。