mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-17 11:14:20 +08:00
回到简体
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
## 10.1. 包簡介
|
||||
## 10.1. 包简介
|
||||
|
||||
任何包繫統設計的目的都是爲了簡化大型程序的設計和維護工作,通過將一組相關的特性放進一個獨立的單元以便於理解和更新,在每個單元更新的同時保持和程序中其它單元的相對獨立性。這種模塊化的特性允許每個包可以被其它的不同項目共享和重用,在項目范圍內、甚至全球范圍統一的分發和複用。
|
||||
任何包系统设计的目的都是为了简化大型程序的设计和维护工作,通过将一组相关的特性放进一个独立的单元以便于理解和更新,在每个单元更新的同时保持和程序中其它单元的相对独立性。这种模块化的特性允许每个包可以被其它的不同项目共享和重用,在项目范围内、甚至全球范围统一的分发和复用。
|
||||
|
||||
每個包一般都定義了一個不同的名字空間用於它內部的每個標識符的訪問。每個名字空間關聯到一個特定的包,讓我們給類型、函數等選擇簡短明了的名字,這樣可以避免在我們使用它們的時候減少和其它部分名字的衝突。
|
||||
每个包一般都定义了一个不同的名字空间用于它内部的每个标识符的访问。每个名字空间关联到一个特定的包,让我们给类型、函数等选择简短明了的名字,这样可以避免在我们使用它们的时候减少和其它部分名字的冲突。
|
||||
|
||||
每個包還通過控製包內名字的可見性和是否導出來實現封裝特性。通過限製包成員的可見性併隱藏包API的具體實現,將允許包的維護者在不影響外部包用戶的前提下調整包的內部實現。通過限製包內變量的可見性,還可以強製用戶通過某些特定函數來訪問和更新內部變量,這樣可以保證內部變量的一致性和併發時的互斥約束。
|
||||
每个包还通过控制包内名字的可见性和是否导出来实现封装特性。通过限制包成员的可见性并隐藏包API的具体实现,将允许包的维护者在不影响外部包用户的前提下调整包的内部实现。通过限制包内变量的可见性,还可以强制用户通过某些特定函数来访问和更新内部变量,这样可以保证内部变量的一致性和并发时的互斥约束。
|
||||
|
||||
當我們脩改了一個源文件,我們必須重新編譯該源文件對應的包和所有依賴該包的其他包。卽使是從頭構建,Go語言編譯器的編譯速度也明顯快於其它編譯語言。Go語言的閃電般的編譯速度主要得益於三個語言特性。第一點,所有導入的包必須在每個文件的開頭顯式聲明,這樣的話編譯器就沒有必要讀取和分析整個源文件來判斷包的依賴關繫。第二點,禁止包的環狀依賴,因爲沒有循環依賴,包的依賴關繫形成一個有向無環圖,每個包可以被獨立編譯,而且很可能是被併發編譯。第三點,編譯後包的目標文件不僅僅記録包本身的導出信息,目標文件同時還記録了包的依賴關繫。因此,在編譯一個包的時候,編譯器隻需要讀取每個直接導入包的目標文件,而不需要遍歷所有依賴的的文件(譯註:很多都是重複的間接依賴)。
|
||||
当我们修改了一个源文件,我们必须重新编译该源文件对应的包和所有依赖该包的其他包。即使是从头构建,Go语言编译器的编译速度也明显快于其它编译语言。Go语言的闪电般的编译速度主要得益于三个语言特性。第一点,所有导入的包必须在每个文件的开头显式声明,这样的话编译器就没有必要读取和分析整个源文件来判断包的依赖关系。第二点,禁止包的环状依赖,因为没有循环依赖,包的依赖关系形成一个有向无环图,每个包可以被独立编译,而且很可能是被并发编译。第三点,编译后包的目标文件不仅仅记录包本身的导出信息,目标文件同时还记录了包的依赖关系。因此,在编译一个包的时候,编译器只需要读取每个直接导入包的目标文件,而不需要遍历所有依赖的的文件(译注:很多都是重复的间接依赖)。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## 10.2. 導入路徑
|
||||
## 10.2. 导入路径
|
||||
|
||||
每個包是由一個全局唯一的字符串所標識的導入路徑定位。出現在import語句中的導入路徑也是字符串。
|
||||
每个包是由一个全局唯一的字符串所标识的导入路径定位。出现在import语句中的导入路径也是字符串。
|
||||
|
||||
```Go
|
||||
import (
|
||||
@@ -14,6 +14,6 @@ import (
|
||||
)
|
||||
```
|
||||
|
||||
就像我們在2.6.1節提到過的,Go語言的規范併沒有指明包的導入路徑字符串的具體含義,導入路徑的具體含義是由構建工具來解釋的。在本章,我們將深入討論Go語言工具箱的功能,包括大家經常使用的構建測試等功能。當然,也有第三方擴展的工具箱存在。例如,Google公司內部的Go語言碼農,他們就使用內部的多語言構建繫統(譯註:Google公司使用的是類似[Bazel](http://bazel.io)的構建繫統,支持多種編程語言,目前該構件繫統還不能完整支持Windows環境),用不同的規則來處理包名字和定位包,用不同的規則來處理單元測試等等,因爲這樣可以更緊密適配他們內部環境。
|
||||
就像我们在2.6.1节提到过的,Go语言的规范并没有指明包的导入路径字符串的具体含义,导入路径的具体含义是由构建工具来解释的。在本章,我们将深入讨论Go语言工具箱的功能,包括大家经常使用的构建测试等功能。当然,也有第三方扩展的工具箱存在。例如,Google公司内部的Go语言码农,他们就使用内部的多语言构建系统(译注:Google公司使用的是类似[Bazel](http://bazel.io)的构建系统,支持多种编程语言,目前该构件系统还不能完整支持Windows环境),用不同的规则来处理包名字和定位包,用不同的规则来处理单元测试等等,因为这样可以更紧密适配他们内部环境。
|
||||
|
||||
如果你計劃分享或發布包,那麽導入路徑最好是全球唯一的。爲了避免衝突,所有非標準庫包的導入路徑建議以所在組織的互聯網域名爲前綴;而且這樣也有利於包的檢索。例如,上面的import語句導入了Go糰隊維護的HTML解析器和一個流行的第三方維護的MySQL驅動。
|
||||
如果你计划分享或发布包,那么导入路径最好是全球唯一的。为了避免冲突,所有非标准库包的导入路径建议以所在组织的互联网域名为前缀;而且这样也有利于包的检索。例如,上面的import语句导入了Go团队维护的HTML解析器和一个流行的第三方维护的MySQL驱动。
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
## 10.3. 包聲明
|
||||
## 10.3. 包声明
|
||||
|
||||
在每個Go語音源文件的開頭都必須有包聲明語句。包聲明語句的主要目的是確定當前包被其它包導入時默認的標識符(也稱爲包名)。
|
||||
在每个Go语音源文件的开头都必须有包声明语句。包声明语句的主要目的是确定当前包被其它包导入时默认的标识符(也称为包名)。
|
||||
|
||||
例如,math/rand包的每個源文件的開頭都包含`package rand`包聲明語句,所以當你導入這個包,你就可以用rand.Int、rand.Float64類似的方式訪問包的成員。
|
||||
例如,math/rand包的每个源文件的开头都包含`package rand`包声明语句,所以当你导入这个包,你就可以用rand.Int、rand.Float64类似的方式访问包的成员。
|
||||
|
||||
```Go
|
||||
package main
|
||||
@@ -17,10 +17,10 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
通常來説,默認的包名就是包導入路徑名的最後一段,因此卽使兩個包的導入路徑不同,它們依然可能有一個相同的包名。例如,math/rand包和crypto/rand包的包名都是rand。稍後我們將看到如何同時導入兩個有相同包名的包。
|
||||
通常来说,默认的包名就是包导入路径名的最后一段,因此即使两个包的导入路径不同,它们依然可能有一个相同的包名。例如,math/rand包和crypto/rand包的包名都是rand。稍后我们将看到如何同时导入两个有相同包名的包。
|
||||
|
||||
關於默認包名一般采用導入路徑名的最後一段的約定也有三種例外情況。第一個例外,包對應一個可執行程序,也就是main包,這時候main包本身的導入路徑是無關緊要的。名字爲main的包是給go build(§10.7.3)構建命令一個信息,這個包編譯完之後必須調用連接器生成一個可執行程序。
|
||||
关于默认包名一般采用导入路径名的最后一段的约定也有三种例外情况。第一个例外,包对应一个可执行程序,也就是main包,这时候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。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## 10.4. 導入聲明
|
||||
## 10.4. 导入声明
|
||||
|
||||
可以在一個Go語言源文件包聲明語句之後,其它非導入聲明語句之前,包含零到多個導入包聲明語句。每個導入聲明可以單獨指定一個導入路徑,也可以通過圓括號同時導入多個導入路徑。下面兩個導入形式是等價的,但是第二種形式更爲常見。
|
||||
可以在一个Go语言源文件包声明语句之后,其它非导入声明语句之前,包含零到多个导入包声明语句。每个导入声明可以单独指定一个导入路径,也可以通过圆括号同时导入多个导入路径。下面两个导入形式是等价的,但是第二种形式更为常见。
|
||||
|
||||
```Go
|
||||
import "fmt"
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
```
|
||||
|
||||
導入的包之間可以通過添加空行來分組;通常將來自不同組織的包獨自分組。包的導入順序無關緊要,但是在每個分組中一般會根據字符串順序排列。(gofmt和goimports工具都可以將不同分組導入的包獨立排序。)
|
||||
导入的包之间可以通过添加空行来分组;通常将来自不同组织的包独自分组。包的导入顺序无关紧要,但是在每个分组中一般会根据字符串顺序排列。(gofmt和goimports工具都可以将不同分组导入的包独立排序。)
|
||||
|
||||
```Go
|
||||
import (
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
```
|
||||
|
||||
如果我們想同時導入兩個有着名字相同的包,例如math/rand包和crypto/rand包,那麽導入聲明必須至少爲一個同名包指定一個新的包名以避免衝突。這叫做導入包的重命名。
|
||||
如果我们想同时导入两个有着名字相同的包,例如math/rand包和crypto/rand包,那么导入声明必须至少为一个同名包指定一个新的包名以避免冲突。这叫做导入包的重命名。
|
||||
|
||||
```Go
|
||||
import (
|
||||
@@ -34,8 +34,8 @@ import (
|
||||
)
|
||||
```
|
||||
|
||||
導入包的重命名隻影響當前的源文件。其它的源文件如果導入了相同的包,可以用導入包原本默認的名字或重命名爲另一個完全不同的名字。
|
||||
导入包的重命名只影响当前的源文件。其它的源文件如果导入了相同的包,可以用导入包原本默认的名字或重命名为另一个完全不同的名字。
|
||||
|
||||
導入包重命名是一個有用的特性,它不僅僅隻是爲了解決名字衝突。如果導入的一個包名很笨重,特别是在一些自動生成的代碼中,這時候用一個簡短名稱會更方便。選擇用簡短名稱重命名導入包時候最好統一,以避免包名混亂。選擇另一個包名稱還可以幫助避免和本地普通變量名産生衝突。例如,如果文件中已經有了一個名爲path的變量,那麽我們可以將"path"標準包重命名爲pathpkg。
|
||||
导入包重命名是一个有用的特性,它不仅仅只是为了解决名字冲突。如果导入的一个包名很笨重,特别是在一些自动生成的代码中,这时候用一个简短名称会更方便。选择用简短名称重命名导入包时候最好统一,以避免包名混乱。选择另一个包名称还可以帮助避免和本地普通变量名产生冲突。例如,如果文件中已经有了一个名为path的变量,那么我们可以将"path"标准包重命名为pathpkg。
|
||||
|
||||
每個導入聲明語句都明確指定了當前包和被導入包之間的依賴關繫。如果遇到包循環導入的情況,Go語言的構建工具將報告錯誤。
|
||||
每个导入声明语句都明确指定了当前包和被导入包之间的依赖关系。如果遇到包循环导入的情况,Go语言的构建工具将报告错误。
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
## 10.5. 包的匿名導入
|
||||
## 10.5. 包的匿名导入
|
||||
|
||||
如果隻是導入一個包而併不使用導入的包將會導致一個編譯錯誤。但是有時候我們隻是想利用導入包而産生的副作用:它會計算包級變量的初始化表達式和執行導入包的init初始化函數(§2.6.2)。這時候我們需要抑製“unused import”編譯錯誤,我們可以用下劃線`_`來重命名導入的包。像往常一樣,下劃線`_`爲空白標識符,併不能被訪問。
|
||||
如果只是导入一个包而并不使用导入的包将会导致一个编译错误。但是有时候我们只是想利用导入包而产生的副作用:它会计算包级变量的初始化表达式和执行导入包的init初始化函数(§2.6.2)。这时候我们需要抑制“unused import”编译错误,我们可以用下划线`_`来重命名导入的包。像往常一样,下划线`_`为空白标识符,并不能被访问。
|
||||
|
||||
```Go
|
||||
import _ "image/png" // register PNG decoder
|
||||
```
|
||||
|
||||
這個被稱爲包的匿名導入。它通常是用來實現一個編譯時機製,然後通過在main主程序入口選擇性地導入附加的包。首先,讓我們看看如何使用該特性,然後再看看它是如何工作的。
|
||||
这个被称为包的匿名导入。它通常是用来实现一个编译时机制,然后通过在main主程序入口选择性地导入附加的包。首先,让我们看看如何使用该特性,然后再看看它是如何工作的。
|
||||
|
||||
標準庫的image圖像包包含了一個`Decode`函數,用於從`io.Reader`接口讀取數據併解碼圖像,它調用底層註冊的圖像解碼器來完成任務,然後返迴image.Image類型的圖像。使用`image.Decode`很容易編寫一個圖像格式的轉換工具,讀取一種格式的圖像,然後編碼爲另一種圖像格式:
|
||||
标准库的image图像包包含了一个`Decode`函数,用于从`io.Reader`接口读取数据并解码图像,它调用底层注册的图像解码器来完成任务,然后返回image.Image类型的图像。使用`image.Decode`很容易编写一个图像格式的转换工具,读取一种格式的图像,然后编码为另一种图像格式:
|
||||
|
||||
<u><i>gopl.io/ch10/jpeg</i></u>
|
||||
```Go
|
||||
@@ -42,7 +42,7 @@ func toJPEG(in io.Reader, out io.Writer) error {
|
||||
}
|
||||
```
|
||||
|
||||
如果我們將`gopl.io/ch3/mandelbrot`(§3.3)的輸出導入到這個程序的標準輸入,它將解碼輸入的PNG格式圖像,然後轉換爲JPEG格式的圖像輸出(圖3.3)。
|
||||
如果我们将`gopl.io/ch3/mandelbrot`(§3.3)的输出导入到这个程序的标准输入,它将解码输入的PNG格式图像,然后转换为JPEG格式的图像输出(图3.3)。
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch3/mandelbrot
|
||||
@@ -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函數註冊,一般是在每個格式包的init初始化函數中調用,例如image/png包是這樣註冊的:
|
||||
下面的代码演示了它的工作机制。标准库还提供了GIF、PNG和JPEG等格式图像的解码器,用户也可以提供自己的解码器,但是为了保持程序体积较小,很多解码器并没有被全部包含,除非是明确需要支持的格式。image.Decode函数在解码时会依次查询支持的格式列表。每个格式驱动列表的每个入口指定了四件事情:格式的名称;一个用于描述这种图像数据开头部分模式的字符串,用于解码器检测识别;一个Decode函数用于完成解码图像工作;一个DecodeConfig函数用于解码图像的大小和颜色空间的信息。每个驱动入口是通过调用image.RegisterFormat函数注册,一般是在每个格式包的init初始化函数中调用,例如image/png包是这样注册的:
|
||||
|
||||
```Go
|
||||
package png // image/png
|
||||
@@ -73,9 +73,9 @@ func init() {
|
||||
}
|
||||
```
|
||||
|
||||
最終的效果是,主程序隻需要匿名導入特定圖像驅動包就可以用image.Decode解碼對應格式的圖像了。
|
||||
最终的效果是,主程序只需要匿名导入特定图像驱动包就可以用image.Decode解码对应格式的图像了。
|
||||
|
||||
數據庫包database/sql也是采用了類似的技術,讓用戶可以根據自己需要選擇導入必要的數據庫驅動。例如:
|
||||
数据库包database/sql也是采用了类似的技术,让用户可以根据自己需要选择导入必要的数据库驱动。例如:
|
||||
|
||||
```Go
|
||||
import (
|
||||
@@ -89,6 +89,6 @@ db, err = sql.Open("mysql", dbname) // OK
|
||||
db, err = sql.Open("sqlite3", dbname) // returns error: unknown driver "sqlite3"
|
||||
```
|
||||
|
||||
**練習 10.1:** 擴展jpeg程序,以支持任意圖像格式之間的相互轉換,使用image.Decode檢測支持的格式類型,然後通過flag命令行標誌參數選擇輸出的格式。
|
||||
**练习 10.1:** 扩展jpeg程序,以支持任意图像格式之间的相互转换,使用image.Decode检测支持的格式类型,然后通过flag命令行标志参数选择输出的格式。
|
||||
|
||||
**練習 10.2:** 設計一個通用的壓縮文件讀取框架,用來讀取ZIP(archive/zip)和POSIX tar(archive/tar)格式壓縮的文檔。使用類似上面的註冊技術來擴展支持不同的壓縮格式,然後根據需要通過匿名導入選擇導入要支持的壓縮格式的驅動包。
|
||||
**练习 10.2:** 设计一个通用的压缩文件读取框架,用来读取ZIP(archive/zip)和POSIX tar(archive/tar)格式压缩的文档。使用类似上面的注册技术来扩展支持不同的压缩格式,然后根据需要通过匿名导入选择导入要支持的压缩格式的驱动包。
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
## 10.6. 包和命名
|
||||
|
||||
在本節中,我們將提供一些關於Go語言獨特的包和成員命名的約定。
|
||||
在本节中,我们将提供一些关于Go语言独特的包和成员命名的约定。
|
||||
|
||||
當創建一個包,一般要用短小的包名,但也不能太短導致難以理解。標準庫中最常用的包有bufio、bytes、flag、fmt、http、io、json、os、sort、sync和time等包。
|
||||
当创建一个包,一般要用短小的包名,但也不能太短导致难以理解。标准库中最常用的包有bufio、bytes、flag、fmt、http、io、json、os、sort、sync和time等包。
|
||||
|
||||
它們的名字都簡潔明了。例如,不要將一個類似imageutil或ioutilis的通用包命名爲util,雖然它看起來很短小。要盡量避免包名使用可能被經常用於局部變量的名字,這樣可能導致用戶重命名導入包,例如前面看到的path包。
|
||||
它们的名字都简洁明了。例如,不要将一个类似imageutil或ioutilis的通用包命名为util,虽然它看起来很短小。要尽量避免包名使用可能被经常用于局部变量的名字,这样可能导致用户重命名导入包,例如前面看到的path包。
|
||||
|
||||
包名一般采用單數的形式。標準庫的bytes、errors和strings使用了複數形式,這是爲了避免和預定義的類型衝突,同樣還有go/types是爲了避免和type關鍵字衝突。
|
||||
包名一般采用单数的形式。标准库的bytes、errors和strings使用了复数形式,这是为了避免和预定义的类型冲突,同样还有go/types是为了避免和type关键字冲突。
|
||||
|
||||
要避免包名有其它的含義。例如,2.5節中我們的溫度轉換包最初使用了temp包名,雖然併沒有持續多久。但這是一個糟糕的嚐試,因爲temp幾乎是臨時變量的同義詞。然後我們有一段時間使用了temperature作爲包名,雖然名字併沒有表達包的眞實用途。最後我們改成了和strconv標準包類似的tempconv包名,這個名字比之前的就好多了。
|
||||
要避免包名有其它的含义。例如,2.5节中我们的温度转换包最初使用了temp包名,虽然并没有持续多久。但这是一个糟糕的尝试,因为temp几乎是临时变量的同义词。然后我们有一段时间使用了temperature作为包名,虽然名字并没有表达包的真实用途。最后我们改成了和strconv标准包类似的tempconv包名,这个名字比之前的就好多了。
|
||||
|
||||
現在讓我們看看如何命名包的成員。由於是通過包的導入名字引入包里面的成員,例如fmt.Println,同時包含了包名和成員名信息。因此,我們一般併不需要關註Println的具體內容,因爲fmt包名已經包含了這個信息。當設計一個包的時候,需要考慮包名和成員名兩個部分如何很好地配合。下面有一些例子:
|
||||
现在让我们看看如何命名包的成员。由于是通过包的导入名字引入包里面的成员,例如fmt.Println,同时包含了包名和成员名信息。因此,我们一般并不需要关注Println的具体内容,因为fmt包名已经包含了这个信息。当设计一个包的时候,需要考虑包名和成员名两个部分如何很好地配合。下面有一些例子:
|
||||
|
||||
```
|
||||
bytes.Equal flag.Int http.Get json.Marshal
|
||||
```
|
||||
|
||||
我們可以看到一些常用的命名模式。strings包提供了和字符串相關的諸多操作:
|
||||
我们可以看到一些常用的命名模式。strings包提供了和字符串相关的诸多操作:
|
||||
|
||||
```Go
|
||||
package strings
|
||||
@@ -30,9 +30,9 @@ type Reader struct{ /* ... */ }
|
||||
func NewReader(s string) *Reader
|
||||
```
|
||||
|
||||
字符串string本身併沒有出現在每個成員名字中。因爲用戶會這樣引用這些成員strings.Index、strings.Replacer等。
|
||||
字符串string本身并没有出现在每个成员名字中。因为用户会这样引用这些成员strings.Index、strings.Replacer等。
|
||||
|
||||
其它一些包,可能隻描述了單一的數據類型,例如html/template和math/rand等,隻暴露一個主要的數據結構和與它相關的方法,還有一個以New命名的函數用於創建實例。
|
||||
其它一些包,可能只描述了单一的数据类型,例如html/template和math/rand等,只暴露一个主要的数据结构和与它相关的方法,还有一个以New命名的函数用于创建实例。
|
||||
|
||||
```Go
|
||||
package rand // "math/rand"
|
||||
@@ -41,6 +41,6 @@ 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等。
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
### 10.7.1. 工作區結構
|
||||
### 10.7.1. 工作区结构
|
||||
|
||||
對於大多數的Go語言用戶,隻需要配置一個名叫GOPATH的環境變量,用來指定當前工作目録卽可。當需要切換到不同工作區的時候,隻要更新GOPATH就可以了。例如,我們在編寫本書時將GOPATH設置爲`$HOME/gobook`:
|
||||
对于大多数的Go语言用户,只需要配置一个名叫GOPATH的环境变量,用来指定当前工作目录即可。当需要切换到不同工作区的时候,只要更新GOPATH就可以了。例如,我们在编写本书时将GOPATH设置为`$HOME/gobook`:
|
||||
|
||||
```
|
||||
$ export GOPATH=$HOME/gobook
|
||||
$ go get gopl.io/...
|
||||
```
|
||||
|
||||
當你用前面介紹的命令下載本書全部的例子源碼之後,你的當前工作區的目録結構應該是這樣的:
|
||||
当你用前面介绍的命令下载本书全部的例子源码之后,你的当前工作区的目录结构应该是这样的:
|
||||
|
||||
```
|
||||
GOPATH/
|
||||
@@ -34,11 +34,11 @@ GOPATH/
|
||||
...
|
||||
```
|
||||
|
||||
GOPATH對應的工作區目録有三個子目録。其中src子目録用於存儲源代碼。每個包被保存在與$GOPATH/src的相對路徑爲包導入路徑的子目録中,例如gopl.io/ch1/helloworld相對應的路徑目録。我們看到,一個GOPATH工作區的src目録中可能有多個獨立的版本控製繫統,例如gopl.io和golang.org分别對應不同的Git倉庫。其中pkg子目録用於保存編譯後的包的目標文件,bin子目録用於保存編譯後的可執行程序,例如helloworld可執行程序。
|
||||
GOPATH对应的工作区目录有三个子目录。其中src子目录用于存储源代码。每个包被保存在与$GOPATH/src的相对路径为包导入路径的子目录中,例如gopl.io/ch1/helloworld相对应的路径目录。我们看到,一个GOPATH工作区的src目录中可能有多个独立的版本控制系统,例如gopl.io和golang.org分别对应不同的Git仓库。其中pkg子目录用于保存编译后的包的目标文件,bin子目录用于保存编译后的可执行程序,例如helloworld可执行程序。
|
||||
|
||||
第二個環境變量GOROOT用來指定Go的安裝目録,還有它自帶的標準庫包的位置。GOROOT的目録結構和GOPATH類似,因此存放fmt包的源代碼對應目録應該爲$GOROOT/src/fmt。用戶一般不需要設置GOROOT,默認情況下Go語言安裝工具會將其設置爲安裝的目録路徑。
|
||||
第二个环境变量GOROOT用来指定Go的安装目录,还有它自带的标准库包的位置。GOROOT的目录结构和GOPATH类似,因此存放fmt包的源代码对应目录应该为$GOROOT/src/fmt。用户一般不需要设置GOROOT,默认情况下Go语言安装工具会将其设置为安装的目录路径。
|
||||
|
||||
其中`go env`命令用於査看Go語音工具涉及的所有環境變量的值,包括未設置環境變量的默認值。GOOS環境變量用於指定目標操作繫統(例如android、linux、darwin或windows),GOARCH環境變量用於指定處理器的類型,例如amd64、386或arm等。雖然GOPATH環境變量是唯一必需要設置的,但是其它環境變量也會偶爾用到。
|
||||
其中`go env`命令用于查看Go语音工具涉及的所有环境变量的值,包括未设置环境变量的默认值。GOOS环境变量用于指定目标操作系统(例如android、linux、darwin或windows),GOARCH环境变量用于指定处理器的类型,例如amd64、386或arm等。虽然GOPATH环境变量是唯一必需要设置的,但是其它环境变量也会偶尔用到。
|
||||
|
||||
```
|
||||
$ go env
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
### 10.7.2. 下載包
|
||||
### 10.7.2. 下载包
|
||||
|
||||
使用Go語言工具箱的go命令,不僅可以根據包導入路徑找到本地工作區的包,甚至可以從互聯網上找到和更新包。
|
||||
使用Go语言工具箱的go命令,不仅可以根据包导入路径找到本地工作区的包,甚至可以从互联网上找到和更新包。
|
||||
|
||||
使用命令`go get`可以下載一個單一的包或者用`...`下載整個子目録里面的每個包。Go語言工具箱的go命令同時計算併下載所依賴的每個包,這也是前一個例子中golang.org/x/net/html自動出現在本地工作區目録的原因。
|
||||
使用命令`go get`可以下载一个单一的包或者用`...`下载整个子目录里面的每个包。Go语言工具箱的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地址是go.googlesource.com。這其實是Go語言工具的一個特性,可以讓包用一個自定義的導入路徑,但是眞實的代碼卻是由更通用的服務提供,例如googlesource.com或github.com。因爲頁面 https://golang.org/x/net/html 包含了如下的元數據,它告訴Go語言的工具當前包眞實的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
|
||||
@@ -33,8 +33,8 @@ $ ./fetch https://golang.org/x/net/html | grep go-import
|
||||
content="golang.org/x/net git https://go.googlesource.com/net">
|
||||
```
|
||||
|
||||
如果指定`-u`命令行標誌參數,`go get`命令將確保所有的包和依賴的包的版本都是最新的,然後重新編譯和安裝它們。如果不包含該標誌參數的話,而且如果包已經在本地存在,那麽代碼那麽將不會被自動更新。
|
||||
如果指定`-u`命令行标志参数,`go get`命令将确保所有的包和依赖的包的版本都是最新的,然后重新编译和安装它们。如果不包含该标志参数的话,而且如果包已经在本地存在,那么代码那么将不会被自动更新。
|
||||
|
||||
`go get -u`命令隻是簡單地保證每個包是最新版本,如果是第一次下載包則是比較很方便的;但是對於發布程序則可能是不合適的,因爲本地程序可能需要對依賴的包做精確的版本依賴管理。通常的解決方案是使用vendor的目録用於存儲依賴包的固定版本的源代碼,對本地依賴的包的版本更新也是謹慎和持續可控的。在Go1.5之前,一般需要脩改包的導入路徑,所以複製後golang.org/x/net/html導入路徑可能會變爲gopl.io/vendor/golang.org/x/net/html。最新的Go語言命令已經支持vendor特性,但限於篇幅這里併不討論vendor的具體細節。不過可以通過`go help gopath`命令査看Vendor的幫助文檔。
|
||||
`go get -u`命令只是简单地保证每个包是最新版本,如果是第一次下载包则是比较很方便的;但是对于发布程序则可能是不合适的,因为本地程序可能需要对依赖的包做精确的版本依赖管理。通常的解决方案是使用vendor的目录用于存储依赖包的固定版本的源代码,对本地依赖的包的版本更新也是谨慎和持续可控的。在Go1.5之前,一般需要修改包的导入路径,所以复制后golang.org/x/net/html导入路径可能会变为gopl.io/vendor/golang.org/x/net/html。最新的Go语言命令已经支持vendor特性,但限于篇幅这里并不讨论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`参数,以区别普通的浏览器请求)。
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
### 10.7.3. 構建包
|
||||
### 10.7.3. 构建包
|
||||
|
||||
`go build`命令編譯命令行參數指定的每個包。如果包是一個庫,則忽略輸出結果;這可以用於檢測包的可以正確編譯的。如果包的名字是main,`go build`將調用連接器在當前目録創建一個可執行程序;以導入路徑的最後一段作爲可執行程序的名字。
|
||||
`go build`命令编译命令行参数指定的每个包。如果包是一个库,则忽略输出结果;这可以用于检测包的可以正确编译的。如果包的名字是main,`go build`将调用连接器在当前目录创建一个可执行程序;以导入路径的最后一段作为可执行程序的名字。
|
||||
|
||||
因爲每個目録隻包含一個包,因此每個對應可執行程序或者叫Unix術語中的命令的包,會要求放到一個獨立的目録中。這些目録有時候會放在名叫cmd目録的子目録下面,例如用於提供Go文檔服務的golang.org/x/tools/cmd/godoc命令就是放在cmd子目録(§10.7.4)。
|
||||
因为每个目录只包含一个包,因此每个对应可执行程序或者叫Unix术语中的命令的包,会要求放到一个独立的目录中。这些目录有时候会放在名叫cmd目录的子目录下面,例如用于提供Go文档服务的golang.org/x/tools/cmd/godoc命令就是放在cmd子目录(§10.7.4)。
|
||||
|
||||
每個包可以由它們的導入路徑指定,就像前面看到的那樣,或者用一個相對目録的路徑知指定,相對路徑必須以`.`或`..`開頭。如果沒有指定參數,那麽默認指定爲當前目録對應的包。 下面的命令用於構建同一個包, 雖然它們的寫法各不相同:
|
||||
每个包可以由它们的导入路径指定,就像前面看到的那样,或者用一个相对目录的路径知指定,相对路径必须以`.`或`..`开头。如果没有指定参数,那么默认指定为当前目录对应的包。 下面的命令用于构建同一个包, 虽然它们的写法各不相同:
|
||||
|
||||
```
|
||||
$ cd $GOPATH/src/gopl.io/ch1/helloworld
|
||||
@@ -25,7 +25,7 @@ $ cd $GOPATH
|
||||
$ go build ./src/gopl.io/ch1/helloworld
|
||||
```
|
||||
|
||||
但不能這樣:
|
||||
但不能这样:
|
||||
|
||||
```
|
||||
$ cd $GOPATH
|
||||
@@ -33,7 +33,7 @@ $ go build src/gopl.io/ch1/helloworld
|
||||
Error: cannot find package "src/gopl.io/ch1/helloworld".
|
||||
```
|
||||
|
||||
也可以指定包的源文件列表,這一般這隻用於構建一些小程序或做一些臨時性的實驗。如果是main包,將會以第一個Go源文件的基礎文件名作爲最終的可執行程序的名字。
|
||||
也可以指定包的源文件列表,这一般这只用于构建一些小程序或做一些临时性的实验。如果是main包,将会以第一个Go源文件的基础文件名作为最终的可执行程序的名字。
|
||||
|
||||
```
|
||||
$ cat quoteargs.go
|
||||
@@ -52,22 +52,22 @@ $ ./quoteargs one "two three" four\ five
|
||||
["one" "two three" "four five"]
|
||||
```
|
||||
|
||||
特别是對於這類一次性運行的程序,我們希望盡快的構建併運行它。`go run`命令實際上是結合了構建和運行的兩個步驟:
|
||||
特别是对于这类一次性运行的程序,我们希望尽快的构建并运行它。`go run`命令实际上是结合了构建和运行的两个步骤:
|
||||
|
||||
```
|
||||
$ go run quoteargs.go one "two three" four\ five
|
||||
["one" "two three" "four five"]
|
||||
```
|
||||
|
||||
第一行的參數列表中,第一個不是以`.go`結尾的將作爲可執行程序的參數運行。
|
||||
第一行的参数列表中,第一个不是以`.go`结尾的将作为可执行程序的参数运行。
|
||||
|
||||
默認情況下,`go build`命令構建指定的包和它依賴的包,然後丟棄除了最後的可執行文件之外所有的中間編譯結果。依賴分析和編譯過程雖然都是很快的,但是隨着項目增加到幾十個包和成韆上萬行代碼,依賴關繫分析和編譯時間的消耗將變的可觀,有時候可能需要幾秒種,卽使這些依賴項沒有改變。
|
||||
默认情况下,`go build`命令构建指定的包和它依赖的包,然后丢弃除了最后的可执行文件之外所有的中间编译结果。依赖分析和编译过程虽然都是很快的,但是随着项目增加到几十个包和成千上万行代码,依赖关系分析和编译时间的消耗将变的可观,有时候可能需要几秒种,即使这些依赖项没有改变。
|
||||
|
||||
`go install`命令和`go build`命令很相似,但是它會保存每個包的編譯成果,而不是將它們都丟棄。被編譯的包會被保存到$GOPATH/pkg目録下,目録路徑和 src目録路徑對應,可執行程序被保存到$GOPATH/bin目録。(很多用戶會將$GOPATH/bin添加到可執行程序的蒐索列表中。)還有,`go install`命令和`go build`命令都不會重新編譯沒有發生變化的包,這可以使後續構建更快捷。爲了方便編譯依賴的包,`go build -i`命令將安裝每個目標所依賴的包。
|
||||
`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类型:
|
||||
|
||||
<u><i>gopl.io/ch10/cross</i></u>
|
||||
```Go
|
||||
@@ -76,7 +76,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
下面以64位和32位環境分别執行程序:
|
||||
下面以64位和32位环境分别执行程序:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch10/cross
|
||||
@@ -87,19 +87,19 @@ $ ./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
|
||||
```
|
||||
|
||||
更多細節,可以參考go/build包的構建約束部分的文檔。
|
||||
更多细节,可以参考go/build包的构建约束部分的文档。
|
||||
|
||||
```
|
||||
$ go doc go/build
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
### 10.7.4. 包文檔
|
||||
### 10.7.4. 包文档
|
||||
|
||||
Go語言的編碼風格鼓勵爲每個包提供良好的文檔。包中每個導出的成員和包聲明前都應該包含目的和用法説明的註釋。
|
||||
Go语言的编码风格鼓励为每个包提供良好的文档。包中每个导出的成员和包声明前都应该包含目的和用法说明的注释。
|
||||
|
||||
Go語言中包文檔註釋一般是完整的句子,第一行是包的摘要説明,註釋後僅跟着包聲明語句。註釋中函數的參數或其它的標識符併不需要額外的引號或其它標記註明。例如,下面是fmt.Fprintf的文檔註釋。
|
||||
Go语言中包文档注释一般是完整的句子,第一行是包的摘要说明,注释后仅跟着包声明语句。注释中函数的参数或其它的标识符并不需要额外的引号或其它标记注明。例如,下面是fmt.Fprintf的文档注释。
|
||||
|
||||
```Go
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
@@ -10,13 +10,13 @@ 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语言的风格更喜欢简洁的文档,并且文档也是需要像代码一样维护的。对于一组声明语句,可以用一个精炼的句子描述,如果是显而易见的功能则并不需要注释。
|
||||
|
||||
在本書中,隻要空間允許,我們之前很多包聲明都包含了註釋文檔,但你可以從標準庫中發現很多更好的例子。有兩個工具可以幫到你。
|
||||
在本书中,只要空间允许,我们之前很多包声明都包含了注释文档,但你可以从标准库中发现很多更好的例子。有两个工具可以帮到你。
|
||||
|
||||
首先是`go doc`命令,該命令打印包的聲明和每個成員的文檔註釋,下面是整個包的文檔:
|
||||
首先是`go doc`命令,该命令打印包的声明和每个成员的文档注释,下面是整个包的文档:
|
||||
|
||||
```
|
||||
$ go doc time
|
||||
@@ -34,7 +34,7 @@ type Time struct { ... }
|
||||
...many more...
|
||||
```
|
||||
|
||||
或者是某個具體包成員的註釋文檔:
|
||||
或者是某个具体包成员的注释文档:
|
||||
|
||||
```
|
||||
$ go doc time.Since
|
||||
@@ -44,7 +44,7 @@ func Since(t Time) Duration
|
||||
It is shorthand for time.Now().Sub(t).
|
||||
```
|
||||
|
||||
或者是某個具體包的一個方法的註釋文檔:
|
||||
或者是某个具体包的一个方法的注释文档:
|
||||
|
||||
```
|
||||
$ go doc time.Duration.Seconds
|
||||
@@ -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,12 +63,12 @@ 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 ,包含了成千上万的开源包的检索工具。
|
||||
|
||||
你也可以在自己的工作區目録運行godoc服務。運行下面的命令,然後在瀏覽器査看 http://localhost:8000/pkg 頁面:
|
||||
你也可以在自己的工作区目录运行godoc服务。运行下面的命令,然后在浏览器查看 http://localhost:8000/pkg 页面:
|
||||
|
||||
```
|
||||
$ godoc -http :8000
|
||||
```
|
||||
|
||||
其中`-analysis=type`和`-analysis=pointer`命令行標誌參數用於打開文檔和代碼中關於靜態分析的結果。
|
||||
其中`-analysis=type`和`-analysis=pointer`命令行标志参数用于打开文档和代码中关于静态分析的结果。
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
### 10.7.5. 內部包
|
||||
### 10.7.5. 内部包
|
||||
|
||||
在Go語音程序中,包的封裝機製是一個重要的特性。沒有導出的標識符隻在同一個包內部可以訪問,而導出的標識符則是面向全宇宙都是可見的。
|
||||
在Go语音程序中,包的封装机制是一个重要的特性。没有导出的标识符只在同一个包内部可以访问,而导出的标识符则是面向全宇宙都是可见的。
|
||||
|
||||
有時候,一個中間的狀態可能也是有用的,對於一小部分信任的包是可見的,但併不是對所有調用者都可見。例如,當我們計劃將一個大的包拆分爲很多小的更容易維護的子包,但是我們併不想將內部的子包結構也完全暴露出去。同時,我們可能還希望在內部子包之間共享一些通用的處理包,或者我們隻是想實驗一個新包的還併不穩定的接口,暫時隻暴露給一些受限製的用戶使用。
|
||||
有时候,一个中间的状态可能也是有用的,对于一小部分信任的包是可见的,但并不是对所有调用者都可见。例如,当我们计划将一个大的包拆分为很多小的更容易维护的子包,但是我们并不想将内部的子包结构也完全暴露出去。同时,我们可能还希望在内部子包之间共享一些通用的处理包,或者我们只是想实验一个新包的还并不稳定的接口,暂时只暴露给一些受限制的用户使用。
|
||||
|
||||

|
||||
|
||||
爲了滿足這些需求,Go語言的構建工具對包含internal名字的路徑段的包導入路徑做了特殊處理。這種包叫internal包,一個internal包隻能被和internal目録有同一個父目録的包所導入。例如,net/http/internal/chunked內部包隻能被net/http/httputil或net/http包導入,但是不能被net/url包導入。不過net/url包卻可以導入net/http/httputil包。
|
||||
为了满足这些需求,Go语言的构建工具对包含internal名字的路径段的包导入路径做了特殊处理。这种包叫internal包,一个internal包只能被和internal目录有同一个父目录的包所导入。例如,net/http/internal/chunked内部包只能被net/http/httputil或net/http包导入,但是不能被net/url包导入。不过net/url包却可以导入net/http/httputil包。
|
||||
|
||||
```
|
||||
net/http
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
### 10.7.6. 査詢包
|
||||
### 10.7.6. 查询包
|
||||
|
||||
`go list`命令可以査詢可用包的信息。其最簡單的形式,可以測試包是否在工作區併打印它的導入路徑:
|
||||
`go list`命令可以查询可用包的信息。其最简单的形式,可以测试包是否在工作区并打印它的导入路径:
|
||||
|
||||
```
|
||||
$ go list github.com/go-sql-driver/mysql
|
||||
github.com/go-sql-driver/mysql
|
||||
```
|
||||
|
||||
`go list`命令的參數還可以用`"..."`表示匹配任意的包的導入路徑。我們可以用它來列表工作區中的所有包:
|
||||
`go list`命令的参数还可以用`"..."`表示匹配任意的包的导入路径。我们可以用它来列表工作区中的所有包:
|
||||
|
||||
```
|
||||
$ go list ...
|
||||
@@ -20,7 +20,7 @@ cmd/api
|
||||
...many more...
|
||||
```
|
||||
|
||||
或者是特定子目録下的所有包:
|
||||
或者是特定子目录下的所有包:
|
||||
|
||||
```
|
||||
$ go list gopl.io/ch3/...
|
||||
@@ -33,7 +33,7 @@ gopl.io/ch3/printints
|
||||
gopl.io/ch3/surface
|
||||
```
|
||||
|
||||
或者是和某個主題相關的所有包:
|
||||
或者是和某个主题相关的所有包:
|
||||
|
||||
```
|
||||
$ go list ...xml...
|
||||
@@ -41,7 +41,7 @@ encoding/xml
|
||||
gopl.io/ch7/xmlselect
|
||||
```
|
||||
|
||||
`go list`命令還可以獲取每個包完整的元信息,而不僅僅隻是導入路徑,這些元信息可以以不同格式提供給用戶。其中`-json`命令行參數表示用JSON格式打印每個包的元信息。
|
||||
`go list`命令还可以获取每个包完整的元信息,而不仅仅只是导入路径,这些元信息可以以不同格式提供给用户。其中`-json`命令行参数表示用JSON格式打印每个包的元信息。
|
||||
|
||||
```
|
||||
$ go list -json hash
|
||||
@@ -71,7 +71,7 @@ $ go list -json hash
|
||||
}
|
||||
```
|
||||
|
||||
命令行參數`-f`則允許用戶使用text/template包(§4.6)的模闆語言定義輸出文本的格式。下面的命令將打印strconv包的依賴的包,然後用join模闆函數將結果鏈接爲一行,連接時每個結果之間用一個空格分隔:
|
||||
命令行参数`-f`则允许用户使用text/template包(§4.6)的模板语言定义输出文本的格式。下面的命令将打印strconv包的依赖的包,然后用join模板函数将结果链接为一行,连接时每个结果之间用一个空格分隔:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
@@ -80,7 +80,7 @@ errors math runtime unicode/utf8 unsafe
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
譯註:上面的命令在Windows的命令行運行會遇到`template: main:1: unclosed action`的錯誤。産生這個錯誤的原因是因爲命令行對命令中的`" "`參數進行了轉義處理。可以按照下面的方法解決轉義字符串的問題:
|
||||
译注:上面的命令在Windows的命令行运行会遇到`template: main:1: unclosed action`的错误。产生这个错误的原因是因为命令行对命令中的`" "`参数进行了转义处理。可以按照下面的方法解决转义字符串的问题:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
@@ -88,7 +88,7 @@ $ go list -f "{{join .Deps \" \"}}" strconv
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
下面的命令打印compress子目録下所有包的依賴包列表:
|
||||
下面的命令打印compress子目录下所有包的依赖包列表:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
@@ -101,7 +101,7 @@ compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
譯註:Windows下有同樣有問題,要避免轉義字符串的榦擾:
|
||||
译注:Windows下有同样有问题,要避免转义字符串的干扰:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
@@ -109,8 +109,8 @@ $ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
`go list`命令對於一次性的交互式査詢或自動化構建或測試腳本都很有幫助。我們將在11.2.4節中再次使用它。每個子命令的更多信息,包括可設置的字段和意義,可以用`go help list`命令査看。
|
||||
`go list`命令对于一次性的交互式查询或自动化构建或测试脚本都很有帮助。我们将在11.2.4节中再次使用它。每个子命令的更多信息,包括可设置的字段和意义,可以用`go help list`命令查看。
|
||||
|
||||
在本章,我們解釋了Go語言工具中除了測試命令之外的所有重要的子命令。在下一章,我們將看到如何用`go test`命令去運行Go語言程序中的測試代碼。
|
||||
在本章,我们解释了Go语言工具中除了测试命令之外的所有重要的子命令。在下一章,我们将看到如何用`go test`命令去运行Go语言程序中的测试代码。
|
||||
|
||||
**練習 10.4:** 創建一個工具,根據命令行指定的參數,報告工作區所有依賴指定包的其它包集合。提示:你需要運行`go list`命令兩次,一次用於初始化包,一次用於所有包。你可能需要用encoding/json(§4.5)包來分析輸出的JSON格式的信息。
|
||||
**练习 10.4:** 创建一个工具,根据命令行指定的参数,报告工作区所有依赖指定包的其它包集合。提示:你需要运行`go list`命令两次,一次用于初始化包,一次用于所有包。你可能需要用encoding/json(§4.5)包来分析输出的JSON格式的信息。
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
## 10.7. 工具
|
||||
|
||||
本章剩下的部分將討論Go語言工具箱的具體功能,包括如何下載、格式化、構建、測試和安裝Go語言編寫的程序。
|
||||
本章剩下的部分将讨论Go语言工具箱的具体功能,包括如何下载、格式化、构建、测试和安装Go语言编写的程序。
|
||||
|
||||
Go語言的工具箱集合了一繫列的功能的命令集。它可以看作是一個包管理器(類似於Linux中的apt和rpm工具),用於包的査詢、計算的包依賴關繫、從遠程版本控製繫統和下載它們等任務。它也是一個構建繫統,計算文件的依賴關繫,然後調用編譯器、滙編器和連接器構建程序,雖然它故意被設計成沒有標準的make命令那麽複雜。它也是一個單元測試和基準測試的驅動程序,我們將在第11章討論測試話題。
|
||||
Go语言的工具箱集合了一系列的功能的命令集。它可以看作是一个包管理器(类似于Linux中的apt和rpm工具),用于包的查询、计算的包依赖关系、从远程版本控制系统和下载它们等任务。它也是一个构建系统,计算文件的依赖关系,然后调用编译器、汇编器和连接器构建程序,虽然它故意被设计成没有标准的make命令那么复杂。它也是一个单元测试和基准测试的驱动程序,我们将在第11章讨论测试话题。
|
||||
|
||||
Go語言工具箱的命令有着類似“瑞士軍刀”的風格,帶着一打子的子命令,有一些我們經常用到,例如get、run、build和fmt等。你可以運行go或go help命令査看內置的幫助文檔,爲了査詢方便,我們列出了最常用的命令:
|
||||
Go语言工具箱的命令有着类似“瑞士军刀”的风格,带着一打子的子命令,有一些我们经常用到,例如get、run、build和fmt等。你可以运行go或go help命令查看内置的帮助文档,为了查询方便,我们列出了最常用的命令:
|
||||
|
||||
```
|
||||
$ go
|
||||
@@ -26,7 +26,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 list std | wc -l`命令査看標準包的具體數目),標準庫爲大多數的程序提供了必要的基礎構件。在Go的社區,有很多成熟的包被設計、共享、重用和改進,目前互聯網上已經發布了非常多的Go語音開源包,它們可以通過 http://godoc.org 檢索。在本章,我們將演示如果使用已有的包和創建新的包。
|
||||
Go语言有超过100个的标准包(译注:可以用`go list std | wc -l`命令查看标准包的具体数目),标准库为大多数的程序提供了必要的基础构件。在Go的社区,有很多成熟的包被设计、共享、重用和改进,目前互联网上已经发布了非常多的Go语音开源包,它们可以通过 http://godoc.org 检索。在本章,我们将演示如果使用已有的包和创建新的包。
|
||||
|
||||
Go還自帶了工具箱,里面有很多用來簡化工作區和包管理的小工具。在本書開始的時候,我們已經見識過如何使用工具箱自帶的工具來下載、構件和運行我們的演示程序了。在本章,我們將看看這些工具的基本設計理論和嚐試更多的功能,例如打印工作區中包的文檔和査詢相關的元數據等。在下一章,我們將探討探索包的單元測試用法。
|
||||
Go还自带了工具箱,里面有很多用来简化工作区和包管理的小工具。在本书开始的时候,我们已经见识过如何使用工具箱自带的工具来下载、构件和运行我们的演示程序了。在本章,我们将看看这些工具的基本设计理论和尝试更多的功能,例如打印工作区中包的文档和查询相关的元数据等。在下一章,我们将探讨探索包的单元测试用法。
|
||||
|
||||
Reference in New Issue
Block a user