如何寫 Go 代碼

sunset-5990540_1920.jpg

在學完 Go 的語法之后猬仁,就可以開始寫代碼了帝璧,但一個項目中不可能只有幾個代碼文件,而是由很多代碼組成湿刽,下面這篇文章將告訴你如何組織這些 Go 代碼的烁。

原文地址:https://golang.org/doc/code


寫在前面

這篇文檔演示了基于 Go Modules(模塊) 的包的開發(fā)流程,并介紹了獲取诈闺、構(gòu)建和安裝 Go 模塊渴庆、包、命令時使用的 Go 工具雅镊。

注意:這篇文章會假設(shè)你在使用 Go1.13或更高版本襟雷,并且沒有設(shè)置 GO111MODULE 環(huán)境變量。如果需要是使用 Go Modules 的版本仁烹,看這里耸弄。

組織代碼

Go 代碼是通過 package(包) 來組織。包是同一目錄下所有源文件的集合卓缰,它們會被編譯在一起叙赚。一個源文件中定義的函數(shù)、類型僚饭、變量和常量對同一個包下的其他源文件都是可見的。

一個 repository(倉庫)中包含一個或多個 module(模塊)胧砰。模塊由多個相關(guān)的包組成鳍鸵,并且會一起發(fā)布。一個倉庫中通常只會包含一個模塊尉间,這個模塊位于倉庫的根目錄偿乖。go.mod 中會聲明模塊路徑,該模塊中的所有包再被導入的時候哲嘲,到會把這個路徑當做前綴加上贪薪。一個模塊的范圍是從包含 go.mod 的文件夾及文件夾中所有的文件夾(包括所有的子文件夾),到那些包含了 go.mod 的子文件夾(如果有的話)為止眠副。

在代碼可以編譯之前画切,你不需要把代碼推送到遠程的倉庫。模塊可以不屬于任何殘酷囱怕,僅僅在本地定義霍弹。把要發(fā)布的代碼組織好是一個好習慣。

每個模塊的路徑不僅僅用于作為導入包的前綴娃弓,在使用 go 命令下載包的時候也需要用到典格。比如,為了下載 golang.org/x/tools 模塊台丛,go 命令就會找到 https://golang.org/x/tools(詳情看這里)耍缴。

導入路徑是用來導入包的字符串。一個包的導入路徑就是模塊的路徑加上這個包在模塊中的子目錄路徑。比如模塊 github.com/google/go-cmp 包含一個包防嗡,在 cmp 目錄下变汪。那么這個包的導入路徑就是 github.com/google/go-cmp/cmp。標準庫中的包沒有模塊路徑前綴本鸣。

你的第一個程序

在編譯和運行一個簡單程序之前疫衩,要選擇一個模塊路徑(在這里用 example.com/user/hello)并且創(chuàng)建一個 go.mod 文件來描述這個模塊:

$ mkdir hello # 可選,如果已經(jīng)存在荣德,直接把項目拉取到本地
$ cd hello
$ go mod init example.com/user/hello
go: creating new go.mod: module example.com/user/hello
$ cat go.mod
module example.com/user/hello

go 1.14

Go 源文件的第一個語句必須是包名闷煤。可執(zhí)行的文件必須使用 main 作為包名涮瞻。

接下來鲤拿,在當前目錄下創(chuàng)建一個 hello.go 文件,寫入以下代碼:

package main

import "fmt"

func main() {
    fmt.Println("Hello, world.")
}

現(xiàn)在你可以使用 go 命令來編譯并安裝這個程序:

$ go install example.com/user/hello

這個命令將會編譯 hello 程序署咽,產(chǎn)生一個可執(zhí)行文件近顷。這個命令會被安裝為 $HOME/go/bin/hello(在 Windows 系統(tǒng)中,路徑是 %USERPROFILE%\go\bin\hello.exe)宁否。

安裝的目錄通過 GOPATH 和 GOBIN 環(huán)境變量來控制窒升。如果 GOBIN 設(shè)置了,就會安裝到 GOBIN 指定的目錄慕匠。如果 GOPATH 設(shè)置了饱须,這個就會安裝到 GOPATH 目錄下的子目錄 bin 中。否則就會被安裝到默認的 GOPATH 子目錄下 bin 下($HOME/go 或者 %USERPROFILE%\go)台谊。

你可以使用 go env命令來設(shè)置環(huán)境變量的默認值:

$ go env -w GOBIN=/somewhere/else/bin

使用 go env -u 刪除 go env -w 設(shè)置的變量值:

$ go env -u GOBIN

go install 這樣的命令可以在模塊的根目錄及其子目錄中使用蓉媳。如果當前的工作目錄不在 example.com/user/hello 模塊中,go install 會失敗锅铅。

為了方便起見酪呻,go 命令接受工作目錄的相對路徑,如果沒有給出其他路徑盐须,則默認為當前工作目錄下的包玩荠。因此,在我們的工作目錄中贼邓,以下命令都是等價的:

$ go install example.com/user/hello

$ go install .

$ go install

接下來姨蟋,我們來運行一下這個程序,確保功能正常立帖。為了使用方便眼溶,我們把安裝目錄增加到系統(tǒng) PATH 路徑下,這樣就可以直接在命令行使用程序:

# windows 用戶看這里: https://github.com/golang/go/wiki/SettingGOPATH
# 設(shè)置 %PATH%.
$ export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))
$ hello
Hello, world.

如果你在用版本管理系統(tǒng)晓勇,現(xiàn)在就可以初始化倉庫堂飞,添加文件灌旧,并提交第一次修改。這個步驟是可選的绰筛,在寫代碼的時候枢泰,不一定要使用版本管理系統(tǒng):

$ git init
Initialized empty Git repository in /home/user/hello/.git/
$ git add go.mod hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
 1 file changed, 7 insertion(+)
 create mode 100644 go.mod hello.go

go 命令通過請求相應的 HTTPS URL 并讀取 HTML 響應中的元數(shù)據(jù)來定位給定模塊的存儲庫(參見 go help importpath)。許多代碼托管服務已經(jīng)為包含 Go 代碼的存儲庫提供了元數(shù)據(jù)铝噩,因此衡蚂,為了讓你的模塊可以順利的被人找到,直接使用存儲庫的 URL 作為你的模塊路徑骏庸。

導入當前模塊內(nèi)的包

讓我們寫一個 morestrings 包并在 hello 程序中使用它毛甲。首先,為這個包創(chuàng)建一個目錄:$HOME/hello/morestrings具被,并創(chuàng)建一個 reverse.go 文件玻募,填充下面的內(nèi)容:

// Package morestrings implements additional functions to manipulate UTF-8
// encoded strings, beyond what is provided in the standard "strings" package.
package morestrings

// ReverseRunes returns its argument string reversed rune-wise left to right.
func ReverseRunes(s string) string {
    r := []rune(s)
    for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r)
}

因為我們的 ReverseRunes 方法以大寫字母開頭,所以它是導出的一姿,可以在其他的包中導入 morestrings 包來使用.

使用 go build 命令編譯測試一下這個包:

$ cd $HOME/hello/morestrings
$ go build

這不會產(chǎn)生輸出文件七咧,相反,它將已編譯的包保存在本地構(gòu)建緩存中叮叹。

在確認 morestrings 是可編譯的艾栋,就可以在 hello 程序中使用它。然后修改 HOME/hello/hello.go 來使用 morestrings 包:

package main

import (
    "fmt"

    "example.com/user/hello/morestrings"
)

func main() {
    fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}

安裝 hello 程序:

$ go install example.com/user/hello

運行這個新版的程序蛉顽,你應該可以看見一個反轉(zhuǎn)的字符串:

$ hello

導入遠程模塊的包

導入路徑可以使用版本管理系統(tǒng)(如 Git 或 Mercurial)獲取源碼蝗砾,go 工具會自動從遠程倉庫獲取包。例如蜂林,在程序中使用 github.com/google/go-cmp/cmp:

package main

import (
    "fmt"

    "example.com/user/hello/morestrings"
    "github.com/google/go-cmp/cmp"
)

func main() {
    fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
    fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}

當你運行 go install,go build拇泣,或者 go run 等命令時噪叙,就會自動下載這些遠程的模塊并記錄到 go.mod 文件中:

$ go install example.com/user/hello
go: finding module for package github.com/google/go-cmp/cmp
go: downloading github.com/google/go-cmp v0.4.0
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.4.0
$ hello
Hello, Go!
  string(
-   "Hello World",
+   "Hello Go",
  )
$ cat go.mod
module example.com/user/hello

go 1.14

require github.com/google/go-cmp v0.4.0

模塊依賴關(guān)系會自動下載到 GOPATH 下的 pkg/mod 子目錄。某個模塊的給定版本的下載內(nèi)容在所有需要該版本的其他模塊之間共享霉翔,因此 go 命令將這些文件和目錄定義為只讀睁蕾。如果要刪除這些下載的模塊,可以使用:

$ go clean -modcache

測試

Go 有一個輕量級的測試框架债朵,包含 go test 命令和 testing 包子眶。

測試文件的結(jié)尾必須以 _test.go 結(jié)尾,并且測試函數(shù)的名稱都是 TestXXX序芦,方法簽名是 func(t *testing.T)。測試框架會運行每一個函數(shù),如果函數(shù)調(diào)用了t.Error 或者 t.Fail 函數(shù)斋日,那么這個測試就可以被認為失敗。

為 morestrings 添加一個測試 $HOME/hello/morestrings/reverse_test.go寥枝,寫入以下代碼:

package morestrings

import "testing"

func TestReverseRunes(t *testing.T) {
    cases := []struct {
        in, want string
    }{
        {"Hello, world", "dlrow ,olleH"},
        {"Hello, 世界", "界世 ,olleH"},
        {"", ""},
    }
    for _, c := range cases {
        got := ReverseRunes(c.in)
        if got != c.want {
            t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
        }
    }
}

使用 go test 來運行測試:

$ go test
PASS
ok      example.com/user/morestrings 0.165s

運行 go help test 或者查看文檔來看更多細節(jié)。

\color{red}{譯 / Rayjun}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末磁奖,一起剝皮案震驚了整個濱河市囊拜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌比搭,老刑警劉巖冠跷,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異身诺,居然都是意外死亡蜜托,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門戚长,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盗冷,“玉大人,你說我怎么就攤上這事同廉∫翘牵” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵迫肖,是天一觀的道長锅劝。 經(jīng)常有香客問我,道長蟆湖,這世上最難降的妖魔是什么故爵? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮隅津,結(jié)果婚禮上诬垂,老公的妹妹穿的比我還像新娘。我一直安慰自己伦仍,他們只是感情好结窘,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著充蓝,像睡著了一般隧枫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谓苟,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天官脓,我揣著相機與錄音,去河邊找鬼涝焙。 笑死卑笨,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的仑撞。 我是一名探鬼主播湾趾,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼芭商,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了搀缠?” 一聲冷哼從身側(cè)響起铛楣,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎艺普,沒想到半個月后簸州,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡歧譬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年岸浑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瑰步。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡矢洲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缩焦,到底是詐尸還是另有隱情读虏,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布袁滥,位于F島的核電站盖桥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏题翻。R本人自食惡果不足惜揩徊,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嵌赠。 院中可真熱鬧塑荒,春花似錦、人聲如沸姜挺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽初家。三九已至偎窘,卻和暖如春乌助,著一層夾襖步出監(jiān)牢的瞬間溜在,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工他托, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留掖肋,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓赏参,卻偏偏與公主長得像志笼,于是被迫代替她去往敵國和親沿盅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容