mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-17 19:24:19 +08:00
make loop
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
|
||||
任何包繫統設計的目的都是爲了使大型程序的設計和維護, 通過將一組相關的特性放進一個獨立的單元以便於理解和更新, 同時保持和程序中其他單元的相對獨立性. 這種模塊化的特性允許每個包可以被其他的不同項目共享和重用, 在項目內甚至全球統一的分發.
|
||||
|
||||
每個包定義了一個不同的名稱空間用於它內部的每個標識符. 每個名稱關聯到一個特定的包, 我們最好給類型, 函數等選擇簡短清晰的名字, 這樣可以避免在我們使用它們的時候減少和其他部分名字的沖突.
|
||||
每個包定義了一個不同的名稱空間用於它內部的每個標識符. 每個名稱關聯到一個特定的包, 我們最好給類型, 函數等選擇簡短清晰的名字, 這樣可以避免在我們使用它們的時候減少和其他部分名字的衝突.
|
||||
|
||||
包還通過控製包內名字的可見性和是否導齣來實現封裝特性. 通過限製包成員的可見性並隱藏包API的具體實現, 將允許包的維護者在不影響外部包用戶的前提下調整包的內部實現. 通過限製包內變量的可見性, 還可以控製用戶通過某些特定函數來訪問和更新內部變量, 這樣可以保証內部變量的一緻性和並發時的互斥約束.
|
||||
包還通過控製包內名字的可見性和是否導齣來實現封裝特性. 通過限製包成員的可見性併隱藏包API的具體實現, 將允許包的維護者在不影響外部包用戶的前提下調整包的內部實現. 通過限製包內變量的可見性, 還可以控製用戶通過某些特定函數來訪問和更新內部變量, 這樣可以保證內部變量的一致性和併發時的互斥約束.
|
||||
|
||||
當我們脩改了一個文件, 我們必鬚重新編譯改文件對應的包和所以依賴該包的其他包.卽使是從頭構建, Go的編譯器也明顯快於其他編譯語言. Go的編譯速度主要得益於三個特性. 第一點, 所有導入的包必鬚在每個文件的開頭顯式聲明, 這樣的話編譯器就沒有必要讀取分析整個文件來判斷包的依賴關繫. 第二點, 包的依賴關繫形成一個有向無環圖, 因爲沒有循環依賴, 每個包可以被獨立編譯, 很可能是並發編譯. 第三點, 編譯後包的目標文件不僅僅記録包本身的導齣信息, 同時還記録了它的依賴關繫. 因此, 在編譯一個包的時候, 編譯器隻需要讀取每個直接導入包的目標文件, 而不是要遍曆所有依賴的的文件(譯註: 很多可能是間接依賴).
|
||||
當我們脩改了一個文件, 我們必鬚重新編譯改文件對應的包和所以依賴該包的其他包.卽使是從頭構建, Go的編譯器也明顯快於其他編譯語言. Go的編譯速度主要得益於三個特性. 第一點, 所有導入的包必鬚在每個文件的開頭顯式聲明, 這樣的話編譯器就沒有必要讀取分析整個文件來判斷包的依賴關繫. 第二點, 包的依賴關繫形成一個有向無環圖, 因爲沒有循環依賴, 每個包可以被獨立編譯, 很可能是併發編譯. 第三點, 編譯後包的目標文件不僅僅記録包本身的導齣信息, 同時還記録了它的依賴關繫. 因此, 在編譯一個包的時候, 編譯器隻需要讀取每個直接導入包的目標文件, 而不是要遍歷所有依賴的的文件(譯註: 很多可能是間接依賴).
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## 10.2. 導入路徑
|
||||
|
||||
每個包是由一個全侷唯一的字符串所標識的導入路徑定位.
|
||||
每個包是由一個全局唯一的字符串所標識的導入路徑定位.
|
||||
齣現在導入聲明中的導入路徑也是字符串.
|
||||
|
||||
```Go
|
||||
@@ -15,6 +15,6 @@ import (
|
||||
)
|
||||
```
|
||||
|
||||
就像我們在2.6.1節提到過的, Go語言的規範並沒有指明包導入路徑字符串的具體含義, 具體含義是由構建工具來解釋的. 在本章, 我們將深入討論Go工具箱的功能, 包括大傢經常使用的構建測試等功能. 當然, 也有第三方擴展的工具箱存在. 例如, Google公司內部的Go碼農, 就使用內部的多語言構建繫統, 用不同的規則來處理名字和定位包, 指定單元測試等待, 這樣可以緊密適配他們內部的繫統.
|
||||
就像我們在2.6.1節提到過的, Go語言的規范併沒有指明包導入路徑字符串的具體含義, 具體含義是由構建工具來解釋的. 在本章, 我們將深入討論Go工具箱的功能, 包括大家經常使用的構建測試等功能. 當然, 也有第三方擴展的工具箱存在. 例如, Google公司內部的Go碼農, 就使用內部的多語言構建繫統, 用不同的規則來處理名字和定位包, 指定單元測試等待, 這樣可以緊密適配他們內部的繫統.
|
||||
|
||||
如果你計劃分享或發佈包, 那麽導入路徑最好是全球唯一的. 爲了避免沖突, 所有非標準庫包的導入路徑建議以所在組織的互聯網域名爲前綴; 這樣也有利於包的檢索. 例如, 上面的包導入聲明導入了Go團隊維護的HTML解析器和一個流行的第三方維護的MySQL驅動.
|
||||
如果你計劃分享或發布包, 那麽導入路徑最好是全球唯一的. 爲了避免衝突, 所有非標準庫包的導入路徑建議以所在組織的互聯網域名爲前綴; 這樣也有利於包的檢索. 例如, 上面的包導入聲明導入了Go糰隊維護的HTML解析器和一個流行的第三方維護的MySQL驅動.
|
||||
|
||||
@@ -17,11 +17,11 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
通常來說, 默認的包名就是包導入路徑名的最後一段, 因此卽使兩個包的導入路徑不同, 它們依然可能有一個相同的包名. 例如, math/rand 和 crypto/rand 包的名字都是 rand. 稍後我們將看到如何同時導入兩個包名字相同的包.
|
||||
通常來説, 默認的包名就是包導入路徑名的最後一段, 因此卽使兩個包的導入路徑不同, 它們依然可能有一個相同的包名. 例如, math/rand 和 crypto/rand 包的名字都是 rand. 稍後我們將看到如何同時導入兩個包名字相同的包.
|
||||
|
||||
關於默認包名一般採用導入路徑名的最後一段的約定有三種例外情況. 第一個例外是包對應一個可執行程序, 也就是 main 包, 這時候main包本身的導入路徑是無關緊要的. 這是給 go build (§10.7.3) 構建命令一個信息, 必鬚調用連接器生成一個可執行程序.
|
||||
關於默認包名一般采用導入路徑名的最後一段的約定有三種例外情況. 第一個例外是包對應一個可執行程序, 也就是 main 包, 這時候main包本身的導入路徑是無關緊要的. 這是給 go build (§10.7.3) 構建命令一個信息, 必鬚調用連接器生成一個可執行程序.
|
||||
|
||||
第二個例外是包所在的目録中可能有一些文件名是以_test.go爲後綴的Go源文件(譯註: 前面必鬚有其他的字符, 因爲 _ 前綴的源文件可能是被忽略的.), 並且這些源文件聲明的包名也是以_test爲後綴名的. 這種目録可以定義兩個包: 一個普通包, 加一個外部測試包. 以 _test 爲後綴包名的外部測試包由 go test 命令獨立編譯, 兩個包是相互獨立的. 外部測試包一般用來避免測試代碼中的導入包的循環導入依賴, 具體細節我們將在 11.2.4 中介紹.
|
||||
第二個例外是包所在的目録中可能有一些文件名是以_test.go爲後綴的Go源文件(譯註: 前面必鬚有其他的字符, 因爲 _ 前綴的源文件可能是被忽略的.), 併且這些源文件聲明的包名也是以_test爲後綴名的. 這種目録可以定義兩個包: 一個普通包, 加一個外部測試包. 以 _test 爲後綴包名的外部測試包由 go test 命令獨立編譯, 兩個包是相互獨立的. 外部測試包一般用來避免測試代碼中的導入包的循環導入依賴, 具體細節我們將在 11.2.4 中介紹.
|
||||
|
||||
第三個例外是一些依賴版本號的管理工具會在導入路徑後追加版本號信息, 例如 "gopkg.in/yaml.v2". 這種情況下包的名字並不包含版本號後綴, 隻是yaml.
|
||||
第三個例外是一些依賴版本號的管理工具會在導入路徑後追加版本號信息, 例如 "gopkg.in/yaml.v2". 這種情況下包的名字併不包含版本號後綴, 隻是yaml.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
```
|
||||
|
||||
如果我們想同時導入兩個名字相同的包, 例如 math/rand 和 crypto/rand, 導入聲明必鬚至少爲一個同名包指定一個新的包名, 以避免沖突. 這叫做導入包重命名.
|
||||
如果我們想同時導入兩個名字相同的包, 例如 math/rand 和 crypto/rand, 導入聲明必鬚至少爲一個同名包指定一個新的包名, 以避免衝突. 這叫做導入包重命名.
|
||||
|
||||
```Go
|
||||
import (
|
||||
@@ -36,6 +36,6 @@ import (
|
||||
|
||||
導入包重命名隻影響當前的Go源文件. 其他的Go源文件如果導入了相同的包, 可以用導入包原本的名字或重命名爲另一個完全不同的名字.
|
||||
|
||||
導入包重命名是一個有用的特性, 不僅僅是爲了解決名字沖突. 如果導入的一個包名很笨重, 特別是在一些自動生成的代碼中, 這時候用一個簡短名稱會更方便. 選擇用簡短名稱重命名導入包時候最好統一, 比避免包名混亂. 選擇另一個包名稱還可以幫助避免和本地普通變量名產生沖突. 例如, 如果文件中已經有了一個名爲 path 的變量, 我們可以將"path"標準包重命名爲pathpkg.
|
||||
導入包重命名是一個有用的特性, 不僅僅是爲了解決名字衝突. 如果導入的一個包名很笨重, 特别是在一些自動生成的代碼中, 這時候用一個簡短名稱會更方便. 選擇用簡短名稱重命名導入包時候最好統一, 比避免包名混亂. 選擇另一個包名稱還可以幫助避免和本地普通變量名産生衝突. 例如, 如果文件中已經有了一個名爲 path 的變量, 我們可以將"path"標準包重命名爲pathpkg.
|
||||
|
||||
每個導入聲明明確指定了當前包和導入包之間的依賴關繫. 如果遇到包循環導入的情況, Go的構建工具將報告錯誤.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## 10.5. 匿名導入
|
||||
|
||||
如果隻是導入一個包而並不使用導入的包是一個編譯錯誤. 但是有時候我們隻是想利用導入包產生的副作用: 它會計算包級變量的初始化表達式和執行導入包的 init 初始化函數 (§2.6.2). 這時候我們需要抑製“未使用的導入”錯誤是合理的, 我們可以用下劃綫 `_` 來重命名導入的包. 像往常一樣, 下劃綫 `_` 爲空白標識符, 並不能被訪問.
|
||||
如果隻是導入一個包而併不使用導入的包是一個編譯錯誤. 但是有時候我們隻是想利用導入包産生的副作用: 它會計算包級變量的初始化表達式和執行導入包的 init 初始化函數 (§2.6.2). 這時候我們需要抑製“未使用的導入”錯誤是合理的, 我們可以用下劃線 `_` 來重命名導入的包. 像往常一樣, 下劃線 `_` 爲空白標識符, 併不能被訪問.
|
||||
|
||||
```Go
|
||||
import _ "image/png" // register PNG decoder
|
||||
@@ -8,7 +8,7 @@ import _ "image/png" // register PNG decoder
|
||||
|
||||
這個被稱爲匿名導入. 它通常是用來實現一個編譯時機製, 然後通過在main主程序入口選擇性地導入附加的包. 首先, 讓我們看看如何使用它, 然後再看看它是如何工作的:
|
||||
|
||||
標準庫的 image 圖像包導入了一個 `Decode` 函數, 用於從 `io.Reader` 接口讀取數據並解碼圖像, 它調用底層註冊的圖像解碼器工作, 然後返迴 image.Image 類型的圖像. 使用 `image.Decode` 很容易編寫一個圖像格式的轉換工具, 讀取一種格式的圖像, 然後編碼爲另一種圖像格式:
|
||||
標準庫的 image 圖像包導入了一個 `Decode` 函數, 用於從 `io.Reader` 接口讀取數據併解碼圖像, 它調用底層註冊的圖像解碼器工作, 然後返迴 image.Image 類型的圖像. 使用 `image.Decode` 很容易編寫一個圖像格式的轉換工具, 讀取一種格式的圖像, 然後編碼爲另一種圖像格式:
|
||||
|
||||
```Go
|
||||
gopl.io/ch10/jpeg
|
||||
@@ -51,7 +51,7 @@ $ ./mandelbrot | ./jpeg >mandelbrot.jpg
|
||||
Input format = png
|
||||
```
|
||||
|
||||
要註意 image/png 包的匿名導入語句. 如果沒有這一行語句, 依然可以編譯和運行, 但是它將不能識別 PNG 格式的圖像:
|
||||
要註意 image/png 包的匿名導入語句. 如果沒有這一行語句, 依然可以編譯和運行, 但是它將不能識别 PNG 格式的圖像:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch10/jpeg
|
||||
@@ -59,7 +59,7 @@ $ ./mandelbrot | ./jpeg >mandelbrot.jpg
|
||||
jpeg: image: unknown format
|
||||
```
|
||||
|
||||
下面的代碼演示了它的工作機製. 標準庫提供了GIF, PNG, 和 JPEG 格式圖像的解碼器, 用戶也可以提供自己的解碼器, 但是爲了保存程序體積較小, 很多解碼器並沒有被包含儘量, 除非是明確需要支持的格式. image.Decode 函數會査詢支持的格式列表. 列表的每個入口指定了四件事情: 格式的名稱; 一個用於描述這種圖像數據開頭部分模式的字符串, 用於解碼器檢測識別; 一個 Decode 函數 用於解碼圖像; 一個 DecodeConfig 函數用於解碼圖像的大小和顏色空間的信息. 每個入口是通過調用 image.RegisterFormat 函數註冊, 一般是在每個格式包的初始化函數中調用, 例如 image/png 包是這樣的:
|
||||
下面的代碼演示了它的工作機製. 標準庫提供了GIF, PNG, 和 JPEG 格式圖像的解碼器, 用戶也可以提供自己的解碼器, 但是爲了保存程序體積較小, 很多解碼器併沒有被包含盡量, 除非是明確需要支持的格式. image.Decode 函數會査詢支持的格式列表. 列表的每個入口指定了四件事情: 格式的名稱; 一個用於描述這種圖像數據開頭部分模式的字符串, 用於解碼器檢測識别; 一個 Decode 函數 用於解碼圖像; 一個 DecodeConfig 函數用於解碼圖像的大小和顔色空間的信息. 每個入口是通過調用 image.RegisterFormat 函數註冊, 一般是在每個格式包的初始化函數中調用, 例如 image/png 包是這樣的:
|
||||
|
||||
```Go
|
||||
package png // image/png
|
||||
@@ -75,7 +75,7 @@ func init() {
|
||||
|
||||
最終的效果是, 主程序值需要匿名導入需要 image.Decode 支持的格式對應解碼包就可以解碼圖像了.
|
||||
|
||||
數據庫包 database/sql 也是採用了類似的技術, 讓用戶可以根據自己需要選擇導入必要的數據庫驅動. 例如:
|
||||
數據庫包 database/sql 也是采用了類似的技術, 讓用戶可以根據自己需要選擇導入必要的數據庫驅動. 例如:
|
||||
|
||||
```Go
|
||||
import (
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
|
||||
在本節中, 我們將提供一些關於如何遵循Go語言獨特的包和成員的命名約定.
|
||||
|
||||
當創建一個包, 一般要用短小的包名, 但也不能太短導緻難以理解.
|
||||
當創建一個包, 一般要用短小的包名, 但也不能太短導致難以理解.
|
||||
標準庫中最常用的包有 bufio, bytes, flag, fmt, http, io, json, os, sort, sync, 和 time 等包.
|
||||
|
||||
它們的名字都簡潔明了. 例如, 不要將一個類似 imageutil 或 ioutilis 的通用包命名爲 util,
|
||||
雖然它看起來很短小. 要儘量避免包名使用經常被用於侷部變量的名字, 這樣可能導緻用戶重命名導入包, 例如前面看到的 path 包.
|
||||
雖然它看起來很短小. 要盡量避免包名使用經常被用於局部變量的名字, 這樣可能導致用戶重命名導入包, 例如前面看到的 path 包.
|
||||
|
||||
包名同時採用單數的形式. 標準庫的 bytes, errors, 和 strings 使用了復數是爲了避免和預定義的類型沖突, 同樣還有 go/types 是爲了避免和關鍵字沖突.
|
||||
包名同時采用單數的形式. 標準庫的 bytes, errors, 和 strings 使用了複數是爲了避免和預定義的類型衝突, 同樣還有 go/types 是爲了避免和關鍵字衝突.
|
||||
|
||||
要避免包名有其他的含義. 例如, 2.5節中我們的溫度轉換包最初使用了 temp 包名, 雖然並沒有持續多久. 這是一個糟糕的做法, 因爲 `temp` 幾乎是臨時變量的同義詞. 然後我們有一段時間使用了 temperature 作爲包名, 雖然名字並沒有表達包的眞是用途. 最後我們改成了 tempconv 包名, 和 strconv 類似也很簡潔明了.
|
||||
要避免包名有其他的含義. 例如, 2.5節中我們的溫度轉換包最初使用了 temp 包名, 雖然併沒有持續多久. 這是一個糟糕的做法, 因爲 `temp` 幾乎是臨時變量的同義詞. 然後我們有一段時間使用了 temperature 作爲包名, 雖然名字併沒有表達包的眞是用途. 最後我們改成了 tempconv 包名, 和 strconv 類似也很簡潔明了.
|
||||
|
||||
現在讓我們看看如何命名包的襯衣. 由於是通過包的導入名字引入包裡面的成員, 例如 fmt.Println, 同時包含了包和成名的描述信息(翻譯障礙). 我們並不需要關註Println的具體內容, 因爲 fmt 已經包含了這個信息. 當設計一個包的時候, 需要考慮包名和成員名兩個部分如何配合. 下面有一些例子:
|
||||
現在讓我們看看如何命名包的襯衣. 由於是通過包的導入名字引入包里面的成員, 例如 fmt.Println, 同時包含了包和成名的描述信息(翻譯障礙). 我們併不需要關註Println的具體內容, 因爲 fmt 已經包含了這個信息. 當設計一個包的時候, 需要考慮包名和成員名兩個部分如何配合. 下面有一些例子:
|
||||
|
||||
```
|
||||
bytes.Equal flag.Int http.Get json.Marshal
|
||||
@@ -33,7 +33,7 @@ type Reader struct{ /* ... */ }
|
||||
func NewReader(s string) *Reader
|
||||
```
|
||||
|
||||
string 本身並沒有齣現在每個成員名字中. 因爲用戶會這樣引用這些成員 strings.Index, strings.Replacer 等.
|
||||
string 本身併沒有齣現在每個成員名字中. 因爲用戶會這樣引用這些成員 strings.Index, strings.Replacer 等.
|
||||
|
||||
其他一些包, 可能隻描述了單一的數據類型, 例如 html/template 和 math/rand 等, 隻暴露一個主要的數據結構和與它相關的方法, 還有一個 New 名字的函數用於創建實例.
|
||||
|
||||
@@ -44,11 +44,11 @@ type Rand struct{ /* ... */ }
|
||||
func New(source Source) *Rand
|
||||
```
|
||||
|
||||
這可能導緻一些名字重復, 例如 template.Template 或 rand.Rand, 這就是爲什麽這些種類的包的名稱往往特別短.
|
||||
這可能導致一些名字重複, 例如 template.Template 或 rand.Rand, 這就是爲什麽這些種類的包的名稱往往特别短.
|
||||
|
||||
另一個極端, 還有像 net/http 包那樣含有非常多的名字和不多的數據類型, 因爲它們是要執行一個復雜的復合任務. 儘管有將近二十種類型和更多的函數, 包中最重要的成員名字卻是簡單明了的: Get, Post, Handle, Error, Client, Server.
|
||||
另一個極端, 還有像 net/http 包那樣含有非常多的名字和不多的數據類型, 因爲它們是要執行一個複雜的複合任務. 盡管有將近二十種類型和更多的函數, 包中最重要的成員名字卻是簡單明了的: Get, Post, Handle, Error, Client, Server.
|
||||
|
||||
有包net/http這樣有很多名字沒有很多結構,因爲他們執行一個復雜任務。儘管二十類型和更多的功能,包最重要的成員最簡單的名字:Get、Post、處理、錯誤,客戶端,服務器。
|
||||
有包net/http這樣有很多名字沒有很多結構,因爲他們執行一個複雜任務。盡管二十類型和更多的功能,包最重要的成員最簡單的名字:Get、Post、處理、錯誤,客戶端,服務器。
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
使用Go工具, 不僅可以根據包導入路徑找到本地工作區的包, 甚至可以從互聯網上找到和更新包.
|
||||
|
||||
使用命令 `go get` 可以下載一個單一的包或者用 `...` 下載整個子目録裡面的每個包. Go工具同時計算並下載所依賴的每個包, 這也是前一個例子中 golang.org/x/net/html 自動齣現在本地工作區目録的原因.
|
||||
使用命令 `go get` 可以下載一個單一的包或者用 `...` 下載整個子目録里面的每個包. Go工具同時計算併下載所依賴的每個包, 這也是前一個例子中 golang.org/x/net/html 自動齣現在本地工作區目録的原因.
|
||||
|
||||
一旦 `go get` 命令下載了包, 然後就是安裝包或包對應的命令. 我們將在下一節再關註它的細節, 現在隻是展示下整個過程是如何的簡單. 第一個命令是獲取 golint 工具, 用於檢測Go源代碼的編程風格是否有問題. 第二個命令是用 golint 對 2.6.2節的 gopl.io/ch2/popcount 包代碼進行編碼風格檢査. 它友好地報告了忘記了包的文檔:
|
||||
一旦 `go get` 命令下載了包, 然後就是安裝包或包對應的命令. 我們將在下一節再關註它的細節, 現在隻是展示下整個過程是如何的簡單. 第一個命令是穫取 golint 工具, 用於檢測Go源代碼的編程風格是否有問題. 第二個命令是用 golint 對 2.6.2節的 gopl.io/ch2/popcount 包代碼進行編碼風格檢査. 它友好地報告了忘記了包的文檔:
|
||||
|
||||
```
|
||||
$ go get github.com/golang/lint/golint
|
||||
@@ -13,9 +13,9 @@ src/gopl.io/ch2/popcount/main.go:1:1:
|
||||
package comment should be of the form "Package popcount ..."
|
||||
```
|
||||
|
||||
`go get` 命令支持當前流行的託管網站 GitHub, Bitbucket, 和 Launchpad, 可以直接從它們的版本控製繫統請求代碼. 對於其他的網站, 你可能需要指定版本控製繫統的具體路徑和協議, 例如 Git 或 Mercurial. 運行 `go help importpath` 獲取更新的信息.
|
||||
`go get` 命令支持當前流行的託管網站 GitHub, Bitbucket, 和 Launchpad, 可以直接從它們的版本控製繫統請求代碼. 對於其他的網站, 你可能需要指定版本控製繫統的具體路徑和協議, 例如 Git 或 Mercurial. 運行 `go help importpath` 穫取更新的信息.
|
||||
|
||||
`go get` 獲取的代碼是眞實的本地存儲倉庫, 不僅僅隻是復製文件, 因此你依然可以使用版本管理工具比較本地代碼的變更, 或者切換到其他的版本. 例如 golang.org/x/net 目録對應一個 Git 倉庫:
|
||||
`go get` 穫取的代碼是眞實的本地存儲倉庫, 不僅僅隻是複製文件, 因此你依然可以使用版本管理工具比較本地代碼的變更, 或者切換到其他的版本. 例如 golang.org/x/net 目録對應一個 Git 倉庫:
|
||||
|
||||
```
|
||||
$ cd $GOPATH/src/golang.org/x/net
|
||||
@@ -24,7 +24,7 @@ origin https://go.googlesource.com/net (fetch)
|
||||
origin https://go.googlesource.com/net (push)
|
||||
```
|
||||
|
||||
需要註意的是導入路徑含有的網站域名和本地Git倉庫遠程的Git服務地址並不相同, 眞實的Git地址是 go.googlesource.com. 這其實是Go工具箱的一個特性, 可以讓包用一個自定義的導入路徑, 但是眞實的代碼卻是由更通用的服務提供, 例如 googlesource.com 或 github.com. 頁面 https://golang.org/x/net/html 包含了如下的元數據, 告訴 Go 工具Git倉庫的眞實託管地址:
|
||||
需要註意的是導入路徑含有的網站域名和本地Git倉庫遠程的Git服務地址併不相同, 眞實的Git地址是 go.googlesource.com. 這其實是Go工具箱的一個特性, 可以讓包用一個自定義的導入路徑, 但是眞實的代碼卻是由更通用的服務提供, 例如 googlesource.com 或 github.com. 頁面 https://golang.org/x/net/html 包含了如下的元數據, 告訴 Go 工具Git倉庫的眞實託管地址:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch1/fetch
|
||||
@@ -35,7 +35,7 @@ $ ./fetch https://golang.org/x/net/html | grep go-import
|
||||
|
||||
如果指定 `-u` 命令行標誌參數, `go get` 將確保所有的包和依賴的包的版本都是最新的, 然後編譯和安裝它們. 如果不包含該標誌參數, 如果包已經在本地存在, 那麽將不會被更新.
|
||||
|
||||
`go get -u` 命令隻是簡單地保証每個包是最新版本, 如果你是第一次下載則比較很方便的; 但是如果是發佈程序則可能是不合適的, 因爲本地程序可能需要對依賴的包做精確的版本依賴管理. 通常的解決方案是使用 vendor 目録存儲固定版本的代碼, 對本地依賴的包的版本更新也是謹慎和持續可控的. 在 Go 1.5 之前, 一般需要脩改包的導入路徑, 所以復製後 golang.org/x/net/html 導入路徑可能會變爲 gopl.io/vendor/golang.org/x/net/html. 最新的Go工具已經支持 vendor 特性, 但限於篇幅這裡並不討論細節. 不過可以通過 `go help gopath` 目録査看 Vendor 目録的幫助.
|
||||
`go get -u` 命令隻是簡單地保證每個包是最新版本, 如果你是第一次下載則比較很方便的; 但是如果是發布程序則可能是不合適的, 因爲本地程序可能需要對依賴的包做精確的版本依賴管理. 通常的解決方案是使用 vendor 目録存儲固定版本的代碼, 對本地依賴的包的版本更新也是謹慎和持續可控的. 在 Go 1.5 之前, 一般需要脩改包的導入路徑, 所以複製後 golang.org/x/net/html 導入路徑可能會變爲 gopl.io/vendor/golang.org/x/net/html. 最新的Go工具已經支持 vendor 特性, 但限於篇幅這里併不討論細節. 不過可以通過 `go help gopath` 目録査看 Vendor 目録的幫助.
|
||||
|
||||
**練習 10.3:** 從 http://gopl.io/ch1/helloworld?go-get=1 獲取內容, 査看本書的代碼的眞實託管的網址(`go get`請求HTML頁面時包含了 `go-get` 參數, 以區別普通的瀏覽器請求.)
|
||||
**練習 10.3:** 從 http://gopl.io/ch1/helloworld?go-get=1 穫取內容, 査看本書的代碼的眞實託管的網址(`go get`請求HTML頁面時包含了 `go-get` 參數, 以區别普通的瀏覽器請求.)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ $ ./quoteargs one "two three" four\ five
|
||||
["one" "two three" "four five"]
|
||||
```
|
||||
|
||||
特別是對於這類一次性的程序, 我們繫統儘快的構建並運行它. `go run` 命令結合了構建和運行的兩個步驟:
|
||||
特别是對於這類一次性的程序, 我們繫統盡快的構建併運行它. `go run` 命令結合了構建和運行的兩個步驟:
|
||||
|
||||
```
|
||||
$ go run quoteargs.go one "two three" four\ five
|
||||
@@ -66,9 +66,9 @@ $ go run quoteargs.go one "two three" four\ five
|
||||
|
||||
`go install` 命令和 `go build` 命令很相似, 但是它保存每個包的編譯成果, 而不是將它們都丟棄. 被編譯的包被保存到 $GOPATH/pkg 目録下和 src 目録對應, 可執行程序被保存到 $GOPATH/bin 目録. (很多用戶將 $GOPATH/bin 添加到可執行程序的蒐索列表中.) 還有, `go install` 命令和 `go build` 命令都不會重新編譯沒有發生變化的包, 這可以使後續構建更快捷. 爲了方便, `go build -i` 將安裝每個目標所依賴的包.
|
||||
|
||||
因爲編譯對應不同的操作繫統平檯和CPU架構, `go install` 會將編譯結果安裝到 GOOS 和 GOARCH 對應的目録. 例如, 在 Mac 繫統 golang.org/x/net/html 包將被安裝到 $GOPATH/pkg/darwin_amd64 目録下的 golang.org/x/net/html.a 文件.
|
||||
因爲編譯對應不同的操作繫統平颱和CPU架構, `go install` 會將編譯結果安裝到 GOOS 和 GOARCH 對應的目録. 例如, 在 Mac 繫統 golang.org/x/net/html 包將被安裝到 $GOPATH/pkg/darwin_amd64 目録下的 golang.org/x/net/html.a 文件.
|
||||
|
||||
鍼對不同操作繫統或CPU的交叉構建也是很簡單的. 隻需要設置好目標對應的GOOS 和 GOARCH, 然後運行構建目録卽可. 下面交叉編譯的程序將輸齣它在編譯時操作繫統和CPU類型:
|
||||
針對不同操作繫統或CPU的交叉構建也是很簡單的. 隻需要設置好目標對應的GOOS 和 GOARCH, 然後運行構建目録卽可. 下面交叉編譯的程序將輸齣它在編譯時操作繫統和CPU類型:
|
||||
|
||||
```Go
|
||||
gopl.io/ch10/cross
|
||||
@@ -78,7 +78,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
下面以64位和32位環境分別執行程序:
|
||||
下面以64位和32位環境分别執行程序:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch10/cross
|
||||
@@ -89,13 +89,13 @@ $ ./cross
|
||||
darwin 386
|
||||
```
|
||||
|
||||
有些包可能需要鍼對不同平檯和處理器類型輸齣不同版本的代碼, 以便於處理底層的可移植性問題或提供爲一些特點代碼提供優化. 如果一個文件名包含了一個操作繫統或處理器類型名字, 例如 net_linux.go 或 asm_amd64.s, Go工具將隻在對應的平檯編譯這些文件. 還有一個特別的構建註釋註釋可以提供更多的構建控製. 例如, 文件中如果包含下面的註釋:
|
||||
有些包可能需要針對不同平颱和處理器類型輸齣不同版本的代碼, 以便於處理底層的可移植性問題或提供爲一些特點代碼提供優化. 如果一個文件名包含了一個操作繫統或處理器類型名字, 例如 net_linux.go 或 asm_amd64.s, Go工具將隻在對應的平颱編譯這些文件. 還有一個特别的構建註釋註釋可以提供更多的構建控製. 例如, 文件中如果包含下面的註釋:
|
||||
|
||||
```Go
|
||||
// +build linux darwin
|
||||
```
|
||||
|
||||
在包聲明的前面(含包的註釋), 告訴 `go build` 隻在鍼對 Linux 或 Mac OS X 是纔編譯這個文件. 下面的構建註釋表示不編譯這個文件:
|
||||
在包聲明的前面(含包的註釋), 告訴 `go build` 隻在針對 Linux 或 Mac OS X 是纔編譯這個文件. 下面的構建註釋表示不編譯這個文件:
|
||||
|
||||
```Go
|
||||
// +build ignore
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
### 10.7.4. 包文檔
|
||||
|
||||
Go的編碼風格鼓勵爲每個包提供良好的文檔. 包中每個導齣的成員和包聲明前都應該包含添加目的和用法說明的註釋.
|
||||
Go的編碼風格鼓勵爲每個包提供良好的文檔. 包中每個導齣的成員和包聲明前都應該包含添加目的和用法説明的註釋.
|
||||
|
||||
Go中包文檔註釋一般是完整的句子, 第一行是包的摘要說明, 註釋後僅跟着包聲明語句. 函數的參數或其他的標識符並不需要額外的引號或其他標記註明. 例如, 下面是 fmt.Fprintf 的文檔註釋.
|
||||
Go中包文檔註釋一般是完整的句子, 第一行是包的摘要説明, 註釋後僅跟着包聲明語句. 函數的參數或其他的標識符併不需要額外的引號或其他標記註明. 例如, 下面是 fmt.Fprintf 的文檔註釋.
|
||||
|
||||
```Go
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
@@ -10,9 +10,9 @@ Go中包文檔註釋一般是完整的句子, 第一行是包的摘要說明,
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error)
|
||||
```
|
||||
|
||||
Fprintf 函數格式化的細節在 fmt 包文檔中描述. 如果註釋後僅跟着包聲明語句, 那註釋對應整個包的文檔. 包文檔對應的註釋隻能有一個(譯註: 其實可以多個, 它們會組合成一個包文檔註釋.), 可以齣現在任何一個源文件中. 如果包的註釋內容比較長, 可以當到一個獨立的文件中; fmt 包註釋就有 300 行之多. 這個專門用於保証包文檔的文件通常叫 doc.go.
|
||||
Fprintf 函數格式化的細節在 fmt 包文檔中描述. 如果註釋後僅跟着包聲明語句, 那註釋對應整個包的文檔. 包文檔對應的註釋隻能有一個(譯註: 其實可以多個, 它們會組合成一個包文檔註釋.), 可以齣現在任何一個源文件中. 如果包的註釋內容比較長, 可以當到一個獨立的文件中; fmt 包註釋就有 300 行之多. 這個專門用於保證包文檔的文件通常叫 doc.go.
|
||||
|
||||
好的文檔並不需要面面俱到, 文檔本身應該是簡潔但可不忽略的. 事實上, Go的風格喜歡簡潔的文檔, 並且文檔也是需要想代碼一樣維護的. 對於一組聲明語句, 可以同一個精煉的句子描述, 如果是顯而易見的功能則並不需要註釋.
|
||||
好的文檔併不需要面面俱到, 文檔本身應該是簡潔但可不忽略的. 事實上, Go的風格喜歡簡潔的文檔, 併且文檔也是需要想代碼一樣維護的. 對於一組聲明語句, 可以同一個精鍊的句子描述, 如果是顯而易見的功能則併不需要註釋.
|
||||
|
||||
在本書中, 隻要空間允許, 我們之前很多包聲明都包含了註釋文檔, 但你可以從標準庫中發現很多更好的例子. 有兩個工具可以幫到你.
|
||||
|
||||
@@ -53,7 +53,7 @@ func (d Duration) Seconds() float64
|
||||
Seconds returns the duration as a floating-point number of seconds.
|
||||
```
|
||||
|
||||
該工具並不需要輸入完整的包導入路徑或正確的大小寫. 下面的命令打印 encoding/json 包的 (*json.Decoder).Decode 方法的文檔:
|
||||
該工具併不需要輸入完整的包導入路徑或正確的大小寫. 下面的命令打印 encoding/json 包的 (*json.Decoder).Decode 方法的文檔:
|
||||
|
||||
```
|
||||
$ go doc json.decode
|
||||
@@ -63,7 +63,7 @@ func (dec *Decoder) Decode(v interface{}) error
|
||||
it in the value pointed to by v.
|
||||
```
|
||||
|
||||
第二個工具, 令人睏惑的也是名叫 godoc, 提供可以相互交叉引用的 HTML 頁面, 但是包含和 `go doc` 相同以及更多的信息. 10.1 節演示了 time 包的文檔, 11.6 節將看到godoc演示可以交互的示例程序. godoc 的在綫服務 https://godoc.org, 包含了成韆上萬的開源包的檢索工具.
|
||||
第二個工具, 令人睏惑的也是名叫 godoc, 提供可以相互交叉引用的 HTML 頁面, 但是包含和 `go doc` 相同以及更多的信息. 10.1 節演示了 time 包的文檔, 11.6 節將看到godoc演示可以交互的示例程序. godoc 的在線服務 https://godoc.org, 包含了成韆上萬的開源包的檢索工具.
|
||||
|
||||
You can also run an instance of godoc in your workspace if you want to browse your own packages. Visit http://localhost:8000/pkg in your browser while running this command:
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
在Go程序中, 包的封裝機製是一個重要的特性. 爲導齣的標識符隻在同一個包內部可以訪問, 導齣的標識符則是面向全世界可見.
|
||||
|
||||
有時候, 一個中間的狀態可能也是有用的, 對於一小部分信任的包是可見的, 但並不是對所有調用者都可見. 例如, 當我們計劃將一個大的包拆分爲很多小的更容易管理的子包, 但是我們並不想將內部的子包結構也完全暴露齣去. 同時, 我們肯呢個還希望在內部子包之間共享一些通用的處理包. 或者我們隻是想實驗一個新包的還並不穩定的接口, 暫時隻暴露給一些受限製的客戶端.
|
||||
有時候, 一個中間的狀態可能也是有用的, 對於一小部分信任的包是可見的, 但併不是對所有調用者都可見. 例如, 當我們計劃將一個大的包拆分爲很多小的更容易管理的子包, 但是我們併不想將內部的子包結構也完全暴露齣去. 同時, 我們肯呢個還希望在內部子包之間共享一些通用的處理包. 或者我們隻是想實驗一個新包的還併不穩定的接口, 暫時隻暴露給一些受限製的客戶端.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
### 10.7.6. 査詢包
|
||||
|
||||
`go list` 工具可以報告可用包的信息. 其最簡單的形式, 可以測試包是否在工作區並打印他的導入路徑:
|
||||
`go list` 工具可以報告可用包的信息. 其最簡單的形式, 可以測試包是否在工作區併打印他的導入路徑:
|
||||
|
||||
```
|
||||
$ go list github.com/go-sql-driver/mysql
|
||||
@@ -41,7 +41,7 @@ encoding/xml
|
||||
gopl.io/ch7/xmlselect
|
||||
```
|
||||
|
||||
`go list` 可以獲取每個包完整的元信息, 而不僅僅隻是導入路徑, 這些信息可以以不同格式提供給用戶. 其中 `-json` 標誌參數表示用JSON格式打印每個包的元信息.
|
||||
`go list` 可以穫取每個包完整的元信息, 而不僅僅隻是導入路徑, 這些信息可以以不同格式提供給用戶. 其中 `-json` 標誌參數表示用JSON格式打印每個包的元信息.
|
||||
|
||||
```
|
||||
$ go list -json hash
|
||||
@@ -80,7 +80,7 @@ errors math runtime unicode/utf8 unsafe
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
譯註: 上面的命令在 Windows 的命令行運行會遇到 `template: main:1: unclosed action` 的錯誤. 產生錯誤的原因是因爲命令行對裡面的 `" "` 參數進行轉義了. 按照下面的方法解決轉義字符串的問題:
|
||||
譯註: 上面的命令在 Windows 的命令行運行會遇到 `template: main:1: unclosed action` 的錯誤. 産生錯誤的原因是因爲命令行對里面的 `" "` 參數進行轉義了. 按照下面的方法解決轉義字符串的問題:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
本章剩下的部分將討論Go工具箱的特性, 包括如何 下載, 格式化, 構建, 測試 和 安裝 Go 程序.
|
||||
|
||||
Go的工具箱集合了一繫列的功能到一個命令集. 它可以看作是一個包管理器(類似於Linux中的apt和rpm工具), 用於包的査詢, 計算的包依賴關繫, 從遠程版本控製繫統和下載它們等任務. 它也是一個構建繫統, 計算文件的依賴關繫, 然後調用編譯器, 滙編器 和 連接器 構建程序, 雖然它故意被設計成沒有標準的make命令那麽復雜. 它也是一個測試驅動程序, 我們在第11章討論測試話題.
|
||||
Go的工具箱集合了一繫列的功能到一個命令集. 它可以看作是一個包管理器(類似於Linux中的apt和rpm工具), 用於包的査詢, 計算的包依賴關繫, 從遠程版本控製繫統和下載它們等任務. 它也是一個構建繫統, 計算文件的依賴關繫, 然後調用編譯器, 滙編器 和 連接器 構建程序, 雖然它故意被設計成沒有標準的make命令那麽複雜. 它也是一個測試驅動程序, 我們在第11章討論測試話題.
|
||||
|
||||
Go工具箱的命令有着類似"瑞士軍刀"的風格, 帶着一打子的子命令, 有一些我們經常用到, 例如 get, run, build, 和 fmt 等. 你可以運行 `go help` 命令査看內置的溫度, 爲了査詢方便, 我們列齣了最常用的命令:
|
||||
|
||||
@@ -27,7 +27,7 @@ Use "go help [command]" for more information about a command.
|
||||
...
|
||||
```
|
||||
|
||||
爲了達到零配置的目標, Go的工具箱很多地方都依賴各種約定. 例如, 給定的源文件的名稱, Go工具可以找到對應的包, 因爲每個目録隻包含了單一的包, 並且到的導入路徑和工作區的目録結構是對應的. 給定一個包的導入路徑, Go工具可以找到對應的目録中保存對象的文件. 它還可以發現存儲代碼倉庫的遠程服務器的URL.
|
||||
爲了達到零配置的目標, Go的工具箱很多地方都依賴各種約定. 例如, 給定的源文件的名稱, Go工具可以找到對應的包, 因爲每個目録隻包含了單一的包, 併且到的導入路徑和工作區的目録結構是對應的. 給定一個包的導入路徑, Go工具可以找到對應的目録中保存對象的文件. 它還可以發現存儲代碼倉庫的遠程服務器的URL.
|
||||
|
||||
{% include "./ch10-07-1.md" %}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 第十章 包和工具
|
||||
|
||||
現在隨便一個小程序的實現都可能包含超過10000個函數. 然後作者一般隻需要考慮其中很小的一部分和做很少的設計, 因爲絕大部分代碼都是由他人編寫的, 它們通過類似包的方式被重用.
|
||||
現在隨便一個小程序的實現都可能包含超過10000個函數. 然後作者一般隻需要考慮其中很小的一部分和做很少的設計, 因爲絶大部分代碼都是由他人編寫的, 它們通過類似包的方式被重用.
|
||||
|
||||
Go語言有超過100個的標準包, 爲大多數的程序提供了基礎構件. 在Go的社區, 有很多成熟的包被設計,共享,重用和改進, 目前已經發佈了非常多的開源包, 它們可以通過 http://godoc.org 檢索. 在本章, 我們將演示如果使用已有的包和創建新的包.
|
||||
Go語言有超過100個的標準包, 爲大多數的程序提供了基礎構件. 在Go的社區, 有很多成熟的包被設計,共享,重用和改進, 目前已經發布了非常多的開源包, 它們可以通過 http://godoc.org 檢索. 在本章, 我們將演示如果使用已有的包和創建新的包.
|
||||
|
||||
Go還自帶了工具箱, 裡面有很多用來簡化工作區和包管理的小工具. 在本身開始的時候, 我們已經見識過如果使用工具箱自帶的工具來下載, 構件 和 運行我們的演示程序了. 在本章, 我們將看看這些工具的基本設計理論和嘗試更多的功能, 例如打印工作區中包的文檔和査詢相關的元數據等. 在下一章, 我們將探討探索包的單元測試用法.
|
||||
Go還自帶了工具箱, 里面有很多用來簡化工作區和包管理的小工具. 在本身開始的時候, 我們已經見識過如果使用工具箱自帶的工具來下載, 構件 和 運行我們的演示程序了. 在本章, 我們將看看這些工具的基本設計理論和嚐試更多的功能, 例如打印工作區中包的文檔和査詢相關的元數據等. 在下一章, 我們將探討探索包的單元測試用法.
|
||||
|
||||
Reference in New Issue
Block a user