鍍金池/ 教程/ GO/
版權(quán)
簡介
Bibliography
接口
通訊
函數(shù)
進(jìn)階
索引
并發(fā)

包是函數(shù)和數(shù)據(jù)的集合。用 package 關(guān)鍵字定義一個(gè)包。文件名不需要與包名一致。包名的約定是使用小寫字符。Go 包可以由多個(gè)文件組成,但是使用相同的 package 這一行。讓我們在文件 even.go 中定義一個(gè)叫做 even 的包。

http://wiki.jikexueyuan.com/project/learn-go-language/images/97.png" alt="pic" />

名稱以大寫字母起始的是可導(dǎo)出的,可以在包的外部調(diào)用(稍候會對此進(jìn)行討論)。

現(xiàn)在只需要構(gòu)建這個(gè)包。在 $GOPATH 下建立一個(gè)目錄,復(fù)制 even.go 到這個(gè)目錄(參閱第 1 章的“編譯和運(yùn)行代碼”)。

% mkdir $GOPATH/src/even
% cp even.go $GOPATH/src/even
% go build
% go install

現(xiàn)在就可以在程序 myeven.go 中使用這個(gè)包:

http://wiki.jikexueyuan.com/project/learn-go-language/images/98.png" alt="pic" />

0 .導(dǎo)入下面的包;

  1. 本地包 even 在這里導(dǎo)入;
  2. 官方 fmt 包導(dǎo)入;
  3. 調(diào)用 even 包中的函數(shù)。訪問一個(gè)包中的函數(shù)的語法是 .Function()。
% go build myeven.go
% ./myeven
Is 5 even? false

在 Go 中,當(dāng)函數(shù)的首字母大寫的時(shí)候,函數(shù)會被從包中導(dǎo)出(在包外部可見,或者說公有的),因此函數(shù)名是 Even。如果修改 myeven.go 的第 10 行,使用未導(dǎo)出的函數(shù) even.odd:

fmt.Printf("Is %d even? %v\n", i, even.odd(i))

由于使用了私有的函數(shù),會得到一個(gè)編譯錯(cuò)誤:

myeven.go:10: cannot refer to unexported name even.odd

概括來說:

  • 公有函數(shù)的名字以大寫字母開頭;
  • 私有函數(shù)的名字以小寫字母開頭。

這個(gè)規(guī)則同樣適用于定義在包中的其他名字(新類型、全局變量)。注意,“大寫” 的含義并不僅限于 US ASCII,它被擴(kuò)展到了所有大小寫字母表(拉丁文、希臘文、斯拉夫文、亞美尼亞文和埃及古文)。

標(biāo)識符

像在其他語言中一樣,Go 的命名是很重要的。在某些情況下,它們甚至有語義上的作用:例如,在包外是否可見決定于首字母是不是大寫。因此有必要花點(diǎn)時(shí)間討論一下 Go 程序的命名規(guī)則。

使用的規(guī)則是讓眾所周知的縮寫保持原樣,而不是去嘗試到底哪里應(yīng)該大寫。Atoi,Getwd,Chmod。

駝峰式對那些有完整單詞的會很好:ReadFile,NewWriter,MakeSlice。

包名

當(dāng)包導(dǎo)入(通過 import)時(shí),包名成為了內(nèi)容的入口。在

import "bytes"

之后,導(dǎo)入包的可以調(diào)用函數(shù) bytes.Buffer。任何使用這個(gè)包的人,可以使用同樣的名字訪問到它的內(nèi)容,因此這樣的包名是好的:短的、簡潔的、好記的。根據(jù)規(guī)則,包名是小寫的一個(gè)單詞;不應(yīng)當(dāng)有下劃線或混合大小寫。保持簡潔(由于每個(gè)人都可能需要錄入這個(gè)名字),不要過早考慮命名沖突。

包名是導(dǎo)入的默認(rèn)名稱??梢酝ㄟ^在導(dǎo)入語句指定其他名稱來覆蓋默認(rèn)名稱:

import bar "bytes"

函數(shù) Buffer 現(xiàn)在可以通過 bar.Buffer 來訪問。這意味著,包名無需全局唯一;在少有的沖突中,可以給導(dǎo)入的包選擇另一個(gè)名字在局部使用。在任何時(shí)候,沖突都是很少見的,因?yàn)閷?dǎo)入的文件名會用來做判斷,到底是哪個(gè)包使用了。

另一個(gè)規(guī)則是包名就是代碼的根目錄名;在src/pkg/compress/gzip 的包,作為 compress/gzip 導(dǎo)入,但名字是 gzip,不是 compress_gzip 也不是 compressGzip。

導(dǎo)入包將使用其名字引用到內(nèi)容上,所以導(dǎo)入的包可以利用這個(gè)避免羅嗦。例如,緩沖類型 bufio 包的讀取方法,叫做 Reader,而不是 BufReader,因?yàn)橛脩艨吹降氖?bufio.Reader 這個(gè)清晰、簡潔的名字。更進(jìn)一步說,由于導(dǎo)入的實(shí)例總是它們包名指向的地址,bufio.Reader 不會與 io.Reader 沖突。類似的,ring.Ring(包 container/ring)創(chuàng)建新實(shí)例的函數(shù)——在 Go 中定義的構(gòu)造函數(shù)——通常叫做 NewRing,但是由于 Ring 是這個(gè)包唯一的一個(gè)導(dǎo)出的類型,同時(shí),這個(gè)包也叫做 ring,所以它可以只稱作 New。包的客戶看到的是 ring.New。用包的結(jié)構(gòu)幫助你選擇更好的名字。

另外一個(gè)簡短的例子是 once.Do(參看 sync);once.Do(setup) 讀起來很不錯(cuò),并且命名為 once.DoOrWaitUntilDone(setup) 不會有任何幫助。長的名字不會讓其變得容易閱讀。如果名字表達(dá)了一些復(fù)雜并且微妙的內(nèi)容,更好的辦法是編寫一些有幫助的注釋,而不是將所有信息都放入名字里。

最后,在 Go 中使用混合大小寫 MixedCaps 或者 mixedCaps,而不是下劃線區(qū)分含有多個(gè)單詞的名字。

包的文檔

每個(gè)包都應(yīng)該有包注釋,在 package 前的一個(gè)注釋塊。對于多文件包,包注釋只需要出現(xiàn)在一個(gè)文件前,任意一個(gè)文件都可以。包注釋應(yīng)當(dāng)對包進(jìn)行介紹,并提供相關(guān)于包的整體信息。這會出現(xiàn)在 go doc 生成的關(guān)于包的頁面上,并且相關(guān)的細(xì)節(jié)會一并顯示。來自官方 regexp 包的例子:

http://wiki.jikexueyuan.com/project/learn-go-language/images/99.png" alt="pic" />

每個(gè)定義(并且導(dǎo)出)的函數(shù)應(yīng)當(dāng)有一小段文字描述該函數(shù)的行為。來自于 fmt 包的例子:

// Printf formats according to a format specifier and writes to standard
// output. It returns the number of bytes written and any write error
// encountered.
func Printf(format string, a ...interface) (n int, err error)

測試包

在 Go 中為包編寫單元測試應(yīng)當(dāng)是一種習(xí)慣。編寫測試需要包含 testing 包和程序 gotest。兩者都有良好的文檔。

go test 程序調(diào)用了所有的測試函數(shù)。even 包沒有定義任何測試函數(shù),執(zhí)行 go test,這樣:

http://wiki.jikexueyuan.com/project/learn-go-language/images/100.png" alt="pic" />

在測試文件中定義一個(gè)測試來修復(fù)這個(gè)。測試文件也在包目錄中, 被命名為 *_test.go。這些測試文件同 Go 程序中的其他文件一樣, 但是 go test 只會執(zhí)行測試函數(shù)。每個(gè)測試函數(shù)都有相同的標(biāo)識,它的名字以 Test 開頭:

func TestXxx(t *testing.T)

編寫測試時(shí),需要告訴 go test 測試是失敗還是成功。測試成功則直接返回。當(dāng)測試失敗可以用下面的函數(shù)標(biāo)記 [11]。這是非常重要的(參閱 go doc testing 或go help testfunc 了解更多):

func (t *T) Fail()

Fail 標(biāo)記測試函數(shù)失敗,但仍然繼續(xù)執(zhí)行。

func (t *T) FailNow()

FailNow 標(biāo)記測試函數(shù)失敗,并且中斷其執(zhí)行。當(dāng)前文件中的其余的測試將被跳過,然后執(zhí)行下一個(gè)文件中的測試。

func (t *T) Log(args ... i n t e r f a c e { })

Log 用默認(rèn)格式對其參數(shù)進(jìn)行格式化,與 Print() 類似,并且記錄文本到錯(cuò)誤日志。

func (t *T) Fatal(args ... i n t e r f a c e { })

Fatal 等價(jià)于Log() 后跟隨 FailNow()。

將這些湊到一起,就可以編寫測試了。首先,選擇名字 even_test.go。然后添加下面的內(nèi)容:

http://wiki.jikexueyuan.com/project/learn-go-language/images/101.png" alt="pic" />

注意在第一行使用了 package even,測試使用與被測試的包使用相同的名字空間。這不僅僅是為了方便,也允許了測試未導(dǎo)出的函數(shù)和結(jié)構(gòu)。然后導(dǎo)入 testing 包,并且在第 5 行定義了這個(gè)文件中唯一的測試函數(shù)。展示的 Go 代碼應(yīng)當(dāng)沒有任何驚異的地方:檢查了 Even 函數(shù)是否工作正?!,F(xiàn)在等待了好久的時(shí)刻到了,執(zhí)行測試:

http://wiki.jikexueyuan.com/project/learn-go-language/images/102.png" alt="pic" />

測試執(zhí)行并且報(bào)告 ok。成功了!

如果重新定義測試函數(shù),就可以看到一個(gè)失敗的測試:

http://wiki.jikexueyuan.com/project/learn-go-language/images/103.png" alt="pic" />

然后得到:

http://wiki.jikexueyuan.com/project/learn-go-language/images/104.png" alt="pic" />

然后你可以以此行事(修復(fù)測試的實(shí)例)

在編寫包的時(shí)候應(yīng)當(dāng)一邊寫代碼,一邊寫(一些)文檔和測試函數(shù)。這可以讓你的程序更好,并且它展示了你的努力。

http://wiki.jikexueyuan.com/project/learn-go-language/images/105.png" alt="pic" />

常用的包

標(biāo)準(zhǔn)的Go 代碼庫中包含了大量的包,并且在安裝 Go 的時(shí)候多數(shù)會伴隨一起安裝。瀏覽 $GOROOT/src/pkg 目錄并且查看那些包會非常有啟發(fā)。無法對每個(gè)包就加以解說,不過下面的這些值得討論:

fmt

包 fmt 實(shí)現(xiàn)了格式化的 I/O 函數(shù),這與 C 的 printf 和 scanf 類似。格式化短語派生于 C 。一些短語(%-序列)這樣使用:

%v

默認(rèn)格式的值。當(dāng)打印結(jié)構(gòu)時(shí),加號(%+v)會增加字段名;

%#v

Go 樣式的值表達(dá);

%T

帶有類型的 Go 樣式的值表達(dá);

io

這個(gè)包提供了原始的 I/O 操作界面。它主要的任務(wù)是對 os 包這樣的原始的 I/O 進(jìn)行封裝,增加一些其他相關(guān),使其具有抽象功能用在公共的接口上。

bufio

這個(gè)包實(shí)現(xiàn)了緩沖的 I/O。它封裝于 io.Reader 和 io.Writer 對象,創(chuàng)建了另一個(gè)對象(Reader 和 Writer)在提供緩沖的同時(shí)實(shí)現(xiàn)了一些文本 I/O 的功能。

sort

sort 包提供了對數(shù)組和用戶定義集合的原始的排序功能。

strconv

strconv 包提供了將字符串轉(zhuǎn)換成基本數(shù)據(jù)類型,或者從基本數(shù)據(jù)類型轉(zhuǎn)換為字符串的功能。

os

os 包提供了與平臺無關(guān)的操作系統(tǒng)功能接口。其設(shè)計(jì)是 Unix 形式的。

sync

sync 包提供了基本的同步原語,例如互斥鎖。

flag

flag 包實(shí)現(xiàn)了命令行解析。參閱“命令行參數(shù)” 在第 92 頁。

encoding/json

encoding/json 包實(shí)現(xiàn)了編碼與解碼 RFC 4627 [2] 定義的 JSON 對象。

html/template

數(shù)據(jù)驅(qū)動(dòng)的模板,用于生成文本輸出,例如 HTML。將模板關(guān)聯(lián)到某個(gè)數(shù)據(jù)結(jié)構(gòu)上進(jìn)行解析。模板內(nèi)容指向數(shù)據(jù)結(jié)構(gòu)的元素(通常結(jié) 構(gòu)的字段或者 map 的鍵)控制解析并且決定某個(gè)值會被顯示。模板掃描結(jié)構(gòu)以便解析,而“游標(biāo)” @ 決定了當(dāng)前位置在結(jié)構(gòu)中的值。

net/http

net/http 實(shí)現(xiàn)了 HTTP 請求、響應(yīng)和 URL 的解析,并且提供了可擴(kuò)展的 HTTP 服務(wù)和基本的 HTTP 客戶端。

unsafe

unsafe 包包含了 Go 程序中數(shù)據(jù)類型上所有不安全的操作。通常無須使用這個(gè)。

reflect

reflect 包實(shí)現(xiàn)了運(yùn)行時(shí)反射,允許程序通過抽象類型操作對象。通常用于處理靜態(tài)類型 interface{} 的值,并且通過 Typeof 解析出其動(dòng)態(tài)類型信息,通常會返回一個(gè)有接口類型 Type 的對象。

參閱 5,第“自省和反射” 節(jié)。

os/exec

os/exec 包執(zhí)行外部命令。

練習(xí)

Q15. (0) stack 包

  1. 參考 Q8 練習(xí)。在這個(gè)練習(xí)中將從那個(gè)代碼中建立一個(gè)獨(dú)立的包。為 stack 的實(shí)現(xiàn)創(chuàng)建一個(gè)合適的包,Push、Pop 和 Stack 類型需要被導(dǎo)出。
  2. 為這個(gè)包編寫一個(gè)單元測試,至少測試 Push 后 Pop 的工作情況。

Q16. (2) 計(jì)算器

  1. 使用 stack 包創(chuàng)建逆波蘭計(jì)算器。

答案

A15. (0) stack 包

  1. 在創(chuàng)建 stack 包時(shí),僅有一些小細(xì)節(jié)需要修改。首先,導(dǎo)出的函數(shù)應(yīng)當(dāng)大寫首字母,因此應(yīng)該是 Stack。包所在的文件被命名為 stack-as-package.go,內(nèi)容是:

http://wiki.jikexueyuan.com/project/learn-go-language/images/106.png" alt="pic" />

2 .為了讓單元測試正常工作,需要做一些準(zhǔn)備。下面用一分鐘的時(shí)間來做這些。

首先是單元測試本身。創(chuàng)建文件 pushpop_test.go,有如下內(nèi)容:

http://wiki.jikexueyuan.com/project/learn-go-language/images/107.png" alt="pic" />

為了讓 go test 能夠工作,需要將包所在文件放到 $GOPATH/src:

% mkdir $GOPATH/src/stack
% cp pushpop_test.go $GOPATH/src/stack
% cp stack-as-package.go $GOPATH/src/stack

輸出:

http://wiki.jikexueyuan.com/project/learn-go-language/images/108.png" alt="pic" />

A16. (2) 計(jì)算器

  1. 這是第一個(gè)答案:

http://wiki.jikexueyuan.com/project/learn-go-language/images/109.png" alt="pic" />

http://wiki.jikexueyuan.com/project/learn-go-language/images/110.png" alt="pic" />

上一篇:簡介