1. 程式人生 > 其它 >goalng包和命令工具

goalng包和命令工具

包簡介

任何包系統設計的目的都是為了簡化大型程式的設計和維護工作,通過將一組相關的特性放進一個獨立的單元以便於理解和更新,在每個單元更新的同時保持和程式中其它單元的相對獨立性。這種模組化的特性允許每個包可以被其它的不同專案共享和重用,在專案範圍內、甚至全球範圍統一的分發和複用。

每個包一般都定義了一個不同的名字空間用於它內部的每個識別符號的訪問。每個名字空間關聯到一個特定的包,讓我們給型別、函式等選擇簡短明瞭的名字,這樣可以在使用它們的時候減少和其它部分名字的衝突。

每個包還通過控制包內名字的可見性和是否匯出來實現封裝特性。通過限制包成員的可見性並隱藏包API的具體實現,將允許包的維護者在不影響外部包使用者的前提下調整包的內部實現。通過限制包內變數的可見性,還可以強制使用者通過某些特定函式來訪問和更新內部變數,這樣可以保證內部變數的一致性和併發時的互斥約束。

包的匯入

每個包是由一個全域性唯一的字串所標識的匯入路徑定位。出現在import語句中的匯入路徑也是字串。

import (
    "fmt"
    "math/rand"
    "encoding/json"
    "golang.org/x/net/html"
    "github.com/go-sql-driver/mysql"
)

包宣告

在每個Go語言原始檔的開頭都必須有包宣告語句。包宣告語句的主要目的是確定當前包被其它包匯入時預設的識別符號(也稱為包名)。

通常來說,預設的包名就是包匯入路徑名的最後一段,因此即使兩個包的匯入路徑不同,它們依然可能有一個相同的包名。例如,math/rand包和golang.org/x/exp/rand包的包名都是rand。

package main
import (
	"fmt"
	"math/rand"  
)
	// 匯入rand包 用rand包裡的方法函式等
func main() {
	fmt.Println(rand.Int63n(10000))  // 9410
}
package main
import (
	"fmt"
	"golang.org/x/exp/rand"
)
func main() {
	fmt.Println(rand.Int63n(10000)) // 9351
}

匯入宣告

如果我們想同時匯入兩個有著名字相同的包,例如math/rand包和golang.org/x/exp/rand包,那麼匯入宣告必須至少為一個同名包指定一個新的包名以避免衝突。這叫做匯入包的重新命名。

package main
import (
	"fmt"
	rand1 "math/rand"
	rand2 "golang.org/x/exp/rand"
)
func main() {
	fmt.Println(rand1.Int63n(10000)) // 9401
	fmt.Println(rand2.Int63n(10000)) // 9351
}

包的匿名匯入

如果只是匯入一個包而並不使用匯入的包將會導致一個編譯錯誤。但是有時候我們只是想利用匯入包而產生的副作用:它會計算包級變數的初始化表示式和執行匯入包的init初始化函式。這時候我們需要抑制“unused import”編譯錯誤,我們可以用下劃線_來重新命名匯入的包。比如序號gorm包是要帶入sql驅動

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)
func main() {
  db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
  defer db.Close()
}

包和命名

當建立一個包,一般要用短小的包名,但也不能太短導致難以理解。標準庫中最常用的包有bufio、bytes、flag、fmt、http、io、json、os、sort、sync和time等包。

包名一般採用單數的形式。標準庫的bytes、errors和strings使用了複數形式,這是為了避免和預定義的型別衝突,同樣還有go/types是為了避免和type關鍵字衝突。

工具

Go語言工具箱的具體功能,包括如何下載、格式化、構建、測試和安裝Go語言編寫的程式。

go命令

$ go
...
    build            compile packages and dependencies
    clean            remove object files
    doc              show documentation for package or symbol
    env              print Go environment information
    fmt              run gofmt on package sources
    get              download and install packages and dependencies
    install          compile and install packages and dependencies
    list             list packages
    run              compile and run Go program
    test             test packages
    version          print Go version
    vet              run go tool vet on packages
Use "go help [command]" for more information about a command.
...

下載包

使用Go語言工具箱的go命令,不僅可以根據包匯入路徑找到本地工作區的包,甚至可以從網際網路上找到和更新包。

使用命令go get可以下載一個單一的包或者用...下載整個子目錄裡面的每個包。Go語言工具箱的go命令同時計算並下載所依賴的每個包,這也是前一個例子中golang.org/x/net/html自動出現在本地工作區目錄的原因。

構建包

go build命令編譯命令列引數指定的每個包。如果包是一個庫,則忽略輸出結果;這可以用於檢測包是可以正確編譯的。如果包的名字是main,go build將呼叫連結器在當前目錄建立一個可執行程式;以匯入路徑的最後一段作為可執行程式的名字。

包文件

Go語言的編碼風格鼓勵為每個包提供良好的文件。包中每個匯出的成員和包宣告前都應該包含目的和用法說明的註釋。

Go語言中的文件註釋一般是完整的句子,第一行通常是摘要說明,以被註釋者的名字開頭。註釋中函式的引數或其它的識別符號並不需要額外的引號或其它標記註明。例如,下面是fmt.Fprintf的文件註釋。

// Fprintf formats according to a format specifier and writes to w.
// It returns the number of bytes written and any write error encountered.
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error)

首先是go doc命令,該命令列印其後所指定的實體的宣告與文件註釋,該實體可能是一個包:

$ go doc time
package time // import "time"
Package time provides functionality for measuring and displaying time.
const Nanosecond Duration = 1 ...
func After(d Duration) <-chan Time
func Sleep(d Duration)
func Since(t Time) Duration
func Now() Time
type Duration int64
type Time struct { ... }
...many more...

或者是某個具體的包成員:

$ go doc time.Since
func Since(t Time) Duration
    Since returns the time elapsed since t.
    It is shorthand for time.Now().Sub(t).

或者是一個方法:

$ go doc time.Duration.Seconds
func (d Duration) Seconds() float64
    Seconds returns the duration as a floating-point number of seconds.

內部包

在Go語言程式中,包是最重要的封裝機制。沒有匯出的識別符號只在同一個包內部可以訪問,而匯出的識別符號則是面向全宇宙都是可見的。

net/http
net/http/internal/chunked
net/http/httputil
net/url

查詢包

go list命令可以查詢可用包的資訊。其最簡單的形式,可以測試包是否在工作區並列印它的匯入路徑:

$ go list github.com/go-sql-driver/mysql
github.com/go-sql-driver/mysql

go list命令還可以獲取每個包完整的元資訊,而不僅僅只是匯入路徑,這些元資訊可以以不同格式提供給使用者。其中-json命令列引數表示用JSON格式列印每個包的元資訊。可以用"..."表示匹配相關包的包的匯入路徑。

$ go list ...mysql
github.com/astaxie/beego/session/mysql
github.com/go-sql-driver/mysql

參考文章:

  • 《go語言聖經》