## 2.6. 包和文件
Go語言中的包和其他語言的庫或模塊的概念類似,目的都是為了支持模塊化、封裝、單獨編譯和代碼重用。一個包的源代碼保存在一個或多個以.go為文件后綴名的源文件中,通常一個包所在目錄路徑的后綴是包的導入路徑;例如包gopl.io/ch1/helloworld對應的目錄路徑是$GOPATH/src/gopl.io/ch1/helloworld。
每個包都對應一個獨立的名字空間。例如,在image包中的Decode函數和在unicode/utf16包中的 Decode函數是不同的。要在外部引用該函數,必須顯式使用image.Decode或utf16.Decode形式訪問。
包還可以讓我們通過控制哪些名字是外部可見的來隱藏內部實現信息。在Go語言中,一個簡單的規則是:如果一個名字是大寫字母開頭的,那么該名字是導出的(譯注:因為漢字不區分大小寫,因此漢字開頭的名字是沒有導出的)。
為了演示包基本的用法,先假設我們的溫度轉換軟件已經很流行,我們希望到Go語言社區也能使用這個包。我們該如何做呢?
讓我們創建一個名為gopl.io/ch2/tempconv的包,這是前面例子的一個改進版本。(這里我們沒有按照慣例按順序對例子進行編號,因此包路徑看起來更像一個真實的包)包代碼存儲在兩個源文件中,用來演示如何在一個源文件聲明然后在其他的源文件訪問;雖然在現實中,這樣小的包一般只需要一個文件。
我們把變量的聲明、對應的常量,還有方法都放到tempconv.go源文件中:
<u><i>gopl.io/ch2/tempconv</i></u>
```Go
// Package tempconv performs Celsius and Fahrenheit conversions.
package tempconv
import "fmt"
type Celsius float64
type Fahrenheit float64
const (
AbsoluteZeroC Celsius = -273.15
FreezingC Celsius = 0
BoilingC Celsius = 100
)
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
func (f Fahrenheit) String() string { return fmt.Sprintf("%g°F", f) }
```
轉換函數則放在另一個conv.go源文件中:
```Go
package tempconv
// CToF converts a Celsius temperature to Fahrenheit.
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
// FToC converts a Fahrenheit temperature to Celsius.
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
```
每個源文件都是以包的聲明語句開始,用來指明包的名字。當包被導入的時候,包內的成員將通過類似tempconv.CToF的形式訪問。而包級別的名字,例如在一個文件聲明的類型和常量,在同一個包的其他源文件也是可以直接訪問的,就好像所有代碼都在一個文件一樣。要注意的是tempconv.go源文件導入了fmt包,但是conv.go源文件并沒有,因為這個源文件中的代碼并沒有用到fmt包。
因為包級別的常量名都是以大寫字母開頭,它們可以像tempconv.AbsoluteZeroC這樣被外部代碼訪問:
```Go
fmt.Printf("Brrrr! %v\n", tempconv.AbsoluteZeroC) // "Brrrr! -273.15°C"
```
要將攝氏溫度轉換為華氏溫度,需要先用import語句導入gopl.io/ch2/tempconv包,然后就可以使用下面的代碼進行轉換了:
```Go
fmt.Println(tempconv.CToF(tempconv.BoilingC)) // "212°F"
```
在每個源文件的包聲明前緊跟著的注釋是包注釋(§10.7.4)。通常,包注釋的第一句應該先是包的功能概要說明。一個包通常只有一個源文件有包注釋(譯注:如果有多個包注釋,目前的文檔工具會根據源文件名的先后順序將它們鏈接為一個包注釋)。如果包注釋很大,通常會放到一個獨立的doc.go文件中。
**練習 2.1:** 向tempconv包添加類型、常量和函數用來處理Kelvin絕對溫度的轉換,Kelvin 絕對零度是?273.15°C,Kelvin絕對溫度1K和攝氏度1°C的單位間隔是一樣的。
{% include "./ch2-06-1.md" %}
{% include "./ch2-06-2.md" %}
- 前言
- Go語言起源
- Go語言項目
- 本書的組織
- 更多的信息
- 致謝
- 入門
- Hello, World
- 命令行參數
- 查找重復的行
- GIF動畫
- 獲取URL
- 并發獲取多個URL
- Web服務
- 本章要點
- 程序結構
- 命名
- 聲明
- 變量
- 賦值
- 類型
- 包和文件
- 作用域
- 基礎數據類型
- 整型
- 浮點數
- 復數
- 布爾型
- 字符串
- 常量
- 復合數據類型
- 數組
- Slice
- Map
- 結構體
- JSON
- 文本和HTML模板
- 函數
- 函數聲明
- 遞歸
- 多返回值
- 錯誤
- 函數值
- 匿名函數
- 可變參數
- Deferred函數
- Panic異常
- Recover捕獲異常
- 方法
- 方法聲明
- 基于指針對象的方法
- 通過嵌入結構體來擴展類型
- 方法值和方法表達式
- 示例: Bit數組
- 封裝
- 接口
- 接口是合約
- 接口類型
- 實現接口的條件
- flag.Value接口
- 接口值
- sort.Interface接口
- http.Handler接口
- error接口
- 示例: 表達式求值
- 類型斷言
- 基于類型斷言識別錯誤類型
- 通過類型斷言查詢接口
- 類型分支
- 示例: 基于標記的XML解碼
- 補充幾點
- Goroutines和Channels
- Goroutines
- 示例: 并發的Clock服務
- 示例: 并發的Echo服務
- Channels
- 并發的循環
- 示例: 并發的Web爬蟲
- 基于select的多路復用
- 并發的退出
- 示例: 聊天服務
- 基于共享變量的并發
- 競爭條件
- sync.Mutex互斥鎖
- sync.RWMutex讀寫鎖
- 內存同步
- 競爭條件檢測
- 示例: 并發的非阻塞緩存
- Goroutines和線程
- 包和工具
- 包簡介
- 導入路徑
- 包聲明
- 導入聲明
- 包的匿名導入
- 包和命名
- 工具
- 測試
- go test
- 測試函數
- 測試覆蓋率
- 基準測試
- 剖析
- 示例函數
- 反射
- 為何需要反射?
- reflect.Type和reflect.Value
- Display遞歸打印
- 示例: 編碼S表達式
- 通過reflect.Value修改值
- 示例: 解碼S表達式
- 顯示一個類型的方法集
- 幾點忠告
- 底層編程
- unsafe.Sizeof, Alignof 和 Offsetof
- unsafe.Pointer
- 示例: 深度相等判斷
- 通過cgo調用C代碼
- 幾點忠告
- 附錄
- 附錄A:原文勘誤
- 附錄B:作者譯者
- 附錄C:譯文授權
- 附錄D:其它語言