鍍金池/ 教程/ GO/ 9.5 自定義包和可見性
4.7 strings 和 strconv 包
13.6 啟動外部命令和程序
?# 11.4 類型判斷:type-switch
12.1 讀取用戶的輸入
10.6 方法
12.2 文件讀寫
13 錯誤處理與測試
9.3 鎖和 sync 包
12.3 文件拷貝
?# 11.7 第一個例子:使用 Sorter 接口排序
?# 11.5 測試一個值是否實現(xiàn)了某個接口
6.4 defer 和追蹤
12.10 XML 數(shù)據(jù)格式
13.10 性能調(diào)試:分析并優(yōu)化 Go 程序
?# 11.1 接口是什么
2.2 Go 環(huán)境變量
2.6 安裝目錄清單
2.5 在 Windows 上安裝 Go
11.11 Printf 和反射
1.2 語言的主要特性與發(fā)展的環(huán)境和影響因素
9.0 包(package)
7.4 切片重組(reslice)
13.2 運行時異常和 panic
10.2 使用工廠方法創(chuàng)建結(jié)構(gòu)體實例
12.8 使用接口的實際例子:fmt.Fprintf
2.4 在 Mac OS X 上安裝 Go
3.8 Go 性能說明
7.2 切片
8.0 Map
3.1 Go 開發(fā)環(huán)境的基本要求
5.6 標(biāo)簽與 goto
6.10 使用閉包調(diào)試
9.5 自定義包和可見性
4.3 常量
?# 11.2 接口嵌套接口
6.5 內(nèi)置函數(shù)
前言
10.8 垃圾回收和 SetFinalizer
2.8 Go 解釋器
13.7 Go 中的單元測試和基準(zhǔn)測試
6.8 閉包
4.9 指針
13.1 錯誤處理
10.1 結(jié)構(gòu)體定義
5.1 if-else 結(jié)構(gòu)
6.6 遞歸函數(shù)
9.9 通過 Git 打包和安裝
2.7 Go 運行時(runtime)
10.7 類型的 String() 方法和格式化描述符
3.7 其它工具
9.6 為自定義包使用 godoc
11.12 接口與動態(tài)類型
13.3 從 panic 中恢復(fù)(Recover)
10.3 使用自定義包中的結(jié)構(gòu)體
11.14 結(jié)構(gòu)體、集合和高階函數(shù)
3.6 生成代碼文檔
9.2 regexp 包
4.1 文件名、關(guān)鍵字與標(biāo)識符
?# 11.6 使用方法集與接口
7.0 數(shù)組與切片
7.1 聲明和初始化
12.11 用 Gob 傳輸數(shù)據(jù)
5.5 Break 與 continue
1.1 起源與發(fā)展
?# 11 接口(Interfaces)與反射(reflection)
6.9 應(yīng)用閉包:將函數(shù)作為返回值
4.2 Go 程序的基本結(jié)構(gòu)和要素
8.6 將 map 的鍵值對調(diào)
6.11 計算函數(shù)執(zhí)行時間
5.0 控制結(jié)構(gòu)
10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體
4.6 字符串
3.0 編輯器、集成開發(fā)環(huán)境與其它工具
13.8 測試的具體例子
7.6 字符串、數(shù)組和切片的應(yīng)用
8.4 map 類型的切片
3.9 與其它語言進行交互
7.3 For-range 結(jié)構(gòu)
9.7 使用 go install 安裝自定義包
6.0 函數(shù)
9.8 自定義包的目錄結(jié)構(gòu)、go install 和 go test
6.3 傳遞變長參數(shù)
13.9 用(測試數(shù)據(jù))表驅(qū)動測試
11.9 空接口
8.1 聲明、初始化和 make
6.2 函數(shù)參數(shù)與返回值
9.11 在 Go 程序中使用外部庫
3.3 調(diào)試器
4.5 基本類型和運算符
?# 11.8 第二個例子:讀和寫
12.5 用 buffer 讀取文件
總結(jié):Go 中的面向?qū)ο?/span>
11.10 反射包
12.7 用 defer 關(guān)閉文件
9.4 精密計算和 big 包
4.4 變量
6.1 介紹
13.4 自定義包中的錯誤處理和 panicking
12.4 從命令行讀取參數(shù)
9.10 Go 的外部包和項目
8.3 for-range 的配套用法
3.5 格式化代碼
10.4 帶標(biāo)簽的結(jié)構(gòu)體
7.5 切片的復(fù)制與追加
?# 11.3 類型斷言:如何檢測和轉(zhuǎn)換接口變量的類型
5.4 for 結(jié)構(gòu)
4.8 時間和日期
2.3 在 Linux 上安裝 Go
12 讀寫數(shù)據(jù)
6.12 通過內(nèi)存緩存來提升性能
9.1 標(biāo)準(zhǔn)庫概述
12.6 用切片讀寫文件
10 結(jié)構(gòu)(struct)與方法(method)
8.5 map 的排序
12.9 JSON 數(shù)據(jù)格式
13.5 一種用閉包處理錯誤的模式
3.2 編輯器和集成開發(fā)環(huán)境
12.12 Go 中的密碼學(xué)
5.2 測試多返回值函數(shù)的錯誤
6.7 將函數(shù)作為參數(shù)
8.2 測試鍵值對是否存在及刪除元素
3.4 構(gòu)建并運行 Go 程序
2.1 平臺與架構(gòu)
5.3 switch 結(jié)構(gòu)

9.5 自定義包和可見性

包是 Go 語言中代碼組織和代碼編譯的主要方式。關(guān)于它們的很多基本信息已經(jīng)在 4.2 章節(jié)中給出,最引人注目的便是可見性?,F(xiàn)在我們來看看具體如何來使用自己寫的包。在下一節(jié),我們將回顧一些標(biāo)準(zhǔn)庫中的包,自定義的包和標(biāo)準(zhǔn)庫以外的包。

當(dāng)寫自己包的時候,要使用短小的不含有 _(下劃線)的小寫單詞來為文件命名。這里有個簡單例子來說明包是如何相互調(diào)用以及可見性是如何實現(xiàn)的。

當(dāng)前目錄下(examples/chapter_9/book/)有一個名為 package_test.go 的程序, 它使用了自定義包 pack1 中 pack1.go 的代碼。這段程序(連同編譯鏈接生成的 pack1.a)存放在當(dāng)前目錄下一個名為 pack1 的文件夾下。所以鏈接器將包的對象和主程序?qū)ο箧溄釉谝黄稹?/p>

示例 9.4 pack1.go

package pack1
var Pack1Int int = 42
var PackFloat = 3.14

func ReturnStr() string {
    return "Hello main!"
}

它包含了一個整型變量 Pack1Int 和一個返回字符串的函數(shù) ReturnStr。這段程序在運行時不做任何的事情,因為它不包含有一個 main 函數(shù)。

在主程序 package_test.go 中這個包通過聲明的方式被導(dǎo)入

import "./pack1/pack1"

import 的一般格式如下:

import "包的路徑或 URL 地址" 

例如:

import "github.com/org1/pack1”

路徑是指當(dāng)前目錄的相對路徑。

示例 9.5 package_test.go

package main

import (
    "fmt"
    "./pack1/pack1"
)

func main() {
    var test1 string
    test1 = pack1.ReturnStr()
    fmt.Printf("ReturnStr from package1: %s\n", test1)
    fmt.Printf("Integer from package1: %d\n", pack1.Pack1Int)
    // fmt.Printf("Float from package1: %f\n", pack1.pack1Float)
}

輸出結(jié)果:

ReturnStr from package1: Hello main!
Integer from package1: 42

如果包 pack1 和我們的程序在同一路徑下,我們可以通過 "import ./pack1" 這樣的方式來引入,但這不被視為一個好的方法。

下面的代碼試圖訪問一個未引用的變量或者函數(shù),甚至沒有編譯。將會返回一個錯誤:

fmt.Printf("Float from package1: %f\n", pack1.pack1Float)

錯誤:

cannot refer to unexported name pack1.pack1Float

主程序利用的包必須在主程序編寫之前被編譯。主程序中每個 pack1 項目都要通過包名來使用:pack1.Item。具體使用方法請參見示例 4.6 和 4.7。

因此,按照慣例,子目錄和包之間有著密切的聯(lián)系:為了區(qū)分,不同包存放在不同的目錄下,每個包(所有屬于這個包中的 go 文件)都存放在和包名相同的子目錄下:

Import with . :

import . "./pack1"

當(dāng)使用.來做為包的別名時,你可以不通過包名來使用其中的項目。例如:test := ReturnStr()。

在當(dāng)前的命名空間導(dǎo)入 pack1 包,一般是為了具有更好的測試效果。

Import with _ :

import _ "./pack1/pack1"

pack1包只導(dǎo)入其副作用,也就是說,只執(zhí)行它的init函數(shù)并初始化其中的全局變量。

導(dǎo)入外部安裝包:

如果你要在你的應(yīng)用中使用一個或多個外部包,首先你必須使用 go install(參見第 9.7 節(jié))在你的本地機器上安裝它們。

假設(shè)你想使用 http://codesite.ext/author/goExample/goex 這種托管在 Google Code、GitHub 和 Launchpad 等代碼網(wǎng)站上的包。

你可以通過如下命令安裝:

go install codesite.ext/author/goExample/goex

將一個名為 codesite.ext/author/goExample/goex 的 map 安裝在 $GOROOT/src/ 目錄下。

通過以下方式,一次性安裝,并導(dǎo)入到你的代碼中:

import goex "codesite.ext/author/goExample/goex"

因此該包的 URL 將用作導(dǎo)入路徑。

http://golang.org/cmd/goinstall/go install 文檔中列出了一些廣泛被使用的托管在網(wǎng)絡(luò)代碼倉庫的包的導(dǎo)入路徑

包的初始化:

程序的執(zhí)行開始于導(dǎo)入包,初始化 main 包然后調(diào)用 main 函數(shù)。

一個沒有導(dǎo)入的包將通過分配初始值給所有的包級變量和調(diào)用源碼中定義的包級 init 函數(shù)來初始化。一個包可能有多個 init 函數(shù)甚至在一個源碼文件中。它們的執(zhí)行是無序的。這是最好的例子來測定包的值是否只依賴于相同包下的其他值或者函數(shù)。

init 函數(shù)是不能被調(diào)用的。

導(dǎo)入的包在包自身初始化前被初始化,而一個包在程序執(zhí)行中只能初始化一次。

編譯并安裝一個包(參見第 9.7 節(jié)):

在 Linux/OS X 下可以用類似第 3.9 節(jié)的 Makefile 腳本做到這一點:

include $(GOROOT)/src/Make.inc
TARG=pack1
GOFILES=\
    pack1.go\
    pack1b.go\
include $(GOROOT)/src/Make.pkg

通過 chmod 777 ./Makefile確保它的可執(zhí)行性。

上面腳本內(nèi)的include語引入了相應(yīng)的功能,將自動檢測機器的架構(gòu)并調(diào)用正確的編譯器和鏈接器。

然后終端執(zhí)行 make 或 gomake 工具:他們都會生成一個包含靜態(tài)庫 pack1.a 的 _obj 目錄。

go install(參見第 9.7 節(jié),從 Go1 的首選方式)同樣復(fù)制 pack1.a 到本地的 $GOROOT/pkg 的目錄中一個以操作系統(tǒng)為名的子目錄下。像 import "pack1" 代替 import "path to pack1",這樣只通過名字就可以將包在程序中導(dǎo)入。

當(dāng)?shù)?13 章我們遇到使用測試工具進行測試的時候我們將重新回到自己的包的制作和編譯這個話題。

問題 9.1

a)一個包能分成多個源文件么?

b)一個源文件是否能包含多個包?

練習(xí) 9.3

創(chuàng)建一個程序 main_greetings.go 能夠和用戶說 "Good Day" 或者 "Good Night"。不同的問候應(yīng)該放到 greetings 包中。

在同一個包中創(chuàng)建一個 ISAM 函數(shù)返回一個布爾值用來判斷當(dāng)前時間是 AM 還是 PM,同樣創(chuàng)建 IsAfternoonIsEvening 函數(shù)。

使用 main_greetings 作出合適的問候(提示:使用 time 包)。

練習(xí) 9.4 創(chuàng)建一個程序 main_oddven.go 判斷前 100 個整數(shù)是不是偶數(shù),包內(nèi)同時包含測試的功能。

練習(xí) 9.5 使用第 6.6 節(jié)的斐波那契程序:

1)將斐波那契功能放入自己的 fibo 包中并通過主程序調(diào)用它,存儲最后輸入的值在函數(shù)的全局變量。

2)擴展 fibo 包將通過調(diào)用斐波那契的時候,操作也作為一個參數(shù)。實驗 "+" 和 “*”

main_fibo.go / fibonacci.go

鏈接