不一樣的go語言-gopher


前言

  gopher原意地鼠,在golang 的世界里解釋為地道的go程序員。在其他語言的世界里也有PHPer,Pythonic的說法,反而Java是個例外。雖然也有Javaer之類的說法,但似乎並不被認可。而地道或者說道地,說的是gopher寫的代碼無不透露出go的獨特氣息,比如項目結構、命名方式、代碼格式、編碼風格、構建方式等等。用gopher的話說,用go編寫代碼就像是在畫一幅中國山水畫,成品美不勝收,心曠神怡。

環境變量

  gopher第一條:把東西放對地方。

  go程序的運行,需要依賴於兩個基礎的環境變量,GOROOT與GOPATH。環境變量幾乎在各類編程語言中都存在,比如java的JAVA_HOME,其實也就是編譯器及相關工具或標准庫所在目錄。但go除了GOROOT之外,還增加了GOPATH,它指的是go程序依賴的第三方庫或自有庫所在目錄,以指示編譯器從這些地方找到依賴。GOPATH支持多個目錄,通常一個目錄就是一個項目,並且GOPATH目錄按約定由src、pkg、bin三個目錄組成。gopher們的做法是定義Global GOPATH、Project GOPATH,而更大的項目還會定義Module GOPATH。當使用go get下載依賴時,會選擇GOPATH環境變量中的第一個目錄存放依賴包。

變量 含義 說明
GOROOT go運行環境根目錄 通常指go sdk安裝目錄,包含編譯器、官方工具及標准庫
GOPATH 工作環境目錄列表 通常指第三方庫、自有庫及項目目錄
GOPATH
  1. src: 源代碼目錄
  2. pkg: 編譯時中間文件所在目錄
  3. bin: 編譯后生成可執行文件所在目錄
├── src
|   └── eventer.com
|       └── test
|           └── main.go
├── pkg
└── bin
|   └── test.bin
Global GOPATH

  全局GOPATH,通常將第三方庫的代碼保存至此處,便於所有項目引用,而不用重復下載。因此在GOPATH中,建議將global gopath放在最前面。

Project GOPATH

  如果項目比較簡單的話,可以采用global GOPATH與project GOPATH的組合,即GOPATH設置為global GOPATH與project OGPATH兩個目錄。比如引用了需要對源碼稍作修改的開源項目時,開源項目可以跟當前項目一起放在src目錄下。

Module GOPATH

  在比較復雜的項目中,如果划分的模塊比較多,則可以采用global, project, module三種GOPATH組合的方式,即即GOPATH設置為global GOPATH、project OGPATH、module GOPATH三個目錄。

  最后請注意,global GOPATH、project GOPATH、module GOPATH不是go語言中的概念,go要求的只有GOPATH。而因為GOPATH是一個目錄列表,所以我只是在實踐中將GOPATH細分為上述三者,同時便於講述GOPATH的概念。

項目結構

  gopher第二條:按東西放在約定的地方。

  不同復雜度的項目,大致可以有兩種類型的項目結構,第一種是依賴較少、較簡單的項目,如下圖所示,其中mydriver項目是假設要引用的開源項目,但要在當前項目的使用中稍修改,因此與當前項目的源碼放在一起。

├── src
|   ├── github.com
|       └── eventer
|           └── mydriver
|               └── driver.go
|   └── eventer.com
|       └── test
|           └── main.go
├── pkg
└── bin

  第二種是依賴較多、模塊划分較細、較復雜的項目,如下圖所示:

├── src
|   └── eventer.com
|           └── test
|               └── main.go
├── pkg
├── bin
├── module1
|   ├── src
|       └── eventer.com
|           └── lib
|               └── lib1.go
|   ├── pkg
|   └── bin
├── module2
|   ├── src
|       └── eventer.com
|           └── lib
|               └── lib2.go
|   ├── pkg
|   └── bin
└── module3
|   ├── src
|       └── eventer.com
|           └── lib
|               └── lib3.go
|   ├── pkg
|   └── bin

  其中的module1,module2,module3是基礎模塊,被主項目依賴。這個時候除了將主項目路徑放在GOPATH外,還應當將基礎模塊路徑放在GOPATH中。這樣的項目結構不僅讓項目清晰,還可以做不同的分工。

命名規范

  gopher第三條:把名字起得go一點。

  go語言的命名與其他語言最大的不同在於首字母的大小寫。大寫代表公開(導出,可以在其他包內訪問);小寫代表私有(不導出,只能在包內訪問)。除此之外,與其他語言並無二致,比如不能以數字開頭。而由於關鍵字、保留字的減少,因而減少了一些命名上的忌諱。更為突出的是,go語言有一些建議性的命名規范,這也是gophers的聖經,理應嚴格遵守。

約定 范圍 說明 示例
駝峰命名法 全局 統一使用駝峰命名法 var isLive = false
大小寫一致 縮寫短語,慣用詞 如HTML,CSS, HTTP等 htmlEscape,HTMLEscape
簡短命名法 局部變量 方法內、循環等使用的局部變量可以使用簡短命名 比如for循環中的i,buf代表buffer等
參數命名法 函數參數、返回值、方法接收者 如果參數類型能說明含義,則參數名可以簡短,否則應該采用有文檔說明能力的命名 比如d Duration,t Time
通用命名法 作用域越大或者使用的地方離聲明的地方太遠,則應采用清晰有意義的命名 -
導出命名法 導出變量、函數、結構等 包名與導出名意義不要重復,同時包的命名要與導出的內容相關,不要使用寬泛的名字,如common,util bytes.Buffer比bytes.ByteBuffer要好
文件命名 go文件,單元測試文件 go文件名盡量以一個單詞來命名,多個單詞使用下線划分隔,單元測試文件以對應go文件名加_test結尾 proto_test
包命名 包的一級名稱應是頂級域名,二級名稱則應是項目名稱,項目名稱單詞間以-分隔 github.com/mysql

代碼格式

  gopher第四條:按統一的格式來。

  在多人協作團隊中,統一的代碼格式化模板是第一要義。在Java語言中,檢驗新人經驗的一大法寶就是他有沒有主動索要代碼模板。而在go語言中,則沒有這個必要了。因為go已經有默認的代碼格式化工具了,而且代碼格式化在go語言中是強制規范。所以這使得所有go程序員寫出來的代碼格式都是一樣的。

  go默認的代碼格式化工具是gofmt。另外還有一個增強工具goimport,在gofmt的基礎上增加了自動刪除和引入依賴包。而行長則以不超過80個字符為佳,超過請主動以多行展示。

編碼風格

  gopher第五條:請學會約定

約定 說明
import 按標准庫、內部包、第三方包的順序導入包 只引一個包時使用單行模式,否則使用多行模式
變量聲明 如果連續聲明多個變量,應放在一起 參見例子
錯誤處理 不要忽略每一個error,即使只是打一行日志 go的error處理方式與C同出一轍,通過返回值來標明錯誤或異常,引來的爭議也很多,甚至官方已經開始醞釀在go2解決這個問題
長語句打印 使用格式化方式打印 -
注釋規范 變量、方法、結構等的注釋直接加上聲明前,並且不要加空行。廢棄方法加Deprecated:即可 其中的第一行注釋會被godoc識別為簡短介紹,第二行開始則被認為是注釋詳情。注釋對godoc的生成至關重要,因此關於注釋會有一些技巧,我將在后面用專門的章節探討

多變量聲明

var (
    name string
    age int
)

注釋規范

// Add 兩數相加
// 兩個整數相加,並返回和。
func Add(n1, n2 int)int{
    return n1 + n2
}

依賴管理

  gopher第六條:使用依賴管理工具管理自有依賴與第三方依賴

  一個語言的生態是否完善甚至是否強大,除了github上面的開源項目數量之外,還有一大特征就是是否有優秀的依賴管理工具。依賴管理工具在業界已經是無處不在,yum、maven、gradle、pip、npm、cargo這些工具的大名如雷貫耳。那么go有什么呢?

  早期go的依賴是混亂的,因為沒有一個工具能得到普遍認可,而官方又遲遲不出來解決問題。歷數存在的工具包括godep、glide、govender等等。甚至早期還需要使用GOPATH來管理依賴,即項目的所有依賴都通過go get下載到指定的GOPATH中去。當然這種方案還可以撐大多數時間,但隨着時間的流逝,隨着開發人員的變動,這種管理依賴的弊端就慢慢顯現出來。其實這些老路早期的java也走過,曾幾何時,每個java項目里面都會有一個叫lib或libs的目錄,這里放的就是當前項目依賴的包。當GO采用GOPATH來管理依賴時,開發人員只能被倒逼着用java的方式在源碼庫中自行管理依賴。這樣相當於給依賴包做了隔離,同時又具備了版本管理(因為放在源碼庫)。

  后來在go1.5的時候,官方引入了vender的概念,其實這也沒改變多少,只是官方讓大家存放依賴包的目錄名稱不要亂起了,統一叫vender吧。這個方案我覺得比依賴GOPATH還糟糕,因為vendor目錄脫離了版本管理,導致更換依賴包版本很困難,在當前項目對依賴包的版本更新可能會影響其他項目的使用(如果新版本的依賴包有較大變動的話),同時如何將依賴包放到vendor下呢?等等。當然官方做出的這些變動可能是想像maven那樣,推動社區來完成這件事,因而直接推動了上文提到的基於vendor的依賴管理工具的誕生。直至后來官方默認的社區做出來dep,這下安靜了,盡管剛開始時也不怎么好用,但有總比沒有好。

  go1.11在vgo的基礎上,官方推出了go module。在發布前,官方與社區的大神們還為此開吵,認為官方太不厚道且獨斷專行。完全忽視dep社區的存在,無視dep在go語言中的地位與貢獻。喜歡八卦的朋友們,可搜索《關於Go Module的爭吵》一覽大神是怎么吵架的,也可從中學習他們的思想。

  作為dep與module的親身實踐者,還是乖乖地用dep吧。除非你有足夠的熱情去折騰,比如弄個高速的科學上網工具,或者搭建Go module proxy。開源的go module proxy比如athens,goproxy。《Hello,Go module proxy》一文詳細介紹了go module的幸福與煩惱,反正我是沒有感到幸福。此文很全面地介紹go module后遺症,goproxy可以解決科學上網的問題,然后也有了athens這樣的工具出現,go的依賴管理是朝着越來越好的方向發展。dep與module的爭議在於respect and free,至少目前看來,兩者是可以共存的,特別是在國內。

  相對於java的依賴管理工具maven或gradle來說,gradle是maven的升級版,同時帶來了DSL與元編程的特性,這無疑使得gradle異常地強大。但gradle.io在國內的可達情況也不盡如人意,好就好在其與maven倉庫標准的兼容,使得從maven轉到gradle幾乎沒有額外的成本及阻力。

  扯了這么多,依賴管理對於一門語言是必不可少的。c有cmake,java有maven、gradle,rust有cargo,那么go的dep或者module就用起來吧,看完大神吵架之后,喜歡哪個就選哪個。是不可能產生一個能滿足所有人要求的依賴管理工具的,就連號稱最牛逼的cargo也不例外。在一般的項目中,能用到的依賴管理功能也就那常用的幾個而已,對大多數項目來說,適用好用就行。

構建方式

  gopher第七條:按需構建

  構建的目標是讓代碼成為可運行程序。構建的過程應該是低成本並且讓人愉悅的,顯然C在這一方面讓人抓狂,而go確實做得不錯。並且能在任何平台下編譯出另外一個平台的可執行程序。不管你的go程序是CLI、GUI、WEB或者其他形式的網絡通訊程序,在go的世界里都只需要一個命令構建成可執行程序(依賴也一並被打包),即可在目標系統上運行。在這一點上,java是望塵莫及了。

下面是用來構建go程序常用的參數,其他參數可通過go help environment命令查看。

參數 說明
CGO_ENABLED 0 or 1 是否支持cgo命令,如果go代碼中有c代碼,需要設置為1
GOOS darwin, freebsd, linux, windows 可執行程序運行的目標操作系統
GOARCH 386, amd64, arm 可執行程序運行的目標操作系統架構
# Linux下編譯Mac 64位可執行程序
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go

# Linux下編譯windows 64位可執行程序
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go

# 如果想減少二進制程序的大小,可以加上-ldflags "-s -w",但同時會丟掉調試信息,即不能用gdb調試了。
# 如果想更進一步減少程序大小,可以使用加殼工具,比如upx

請關注公眾號

不一樣的go語言


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM