送給學(xué)Go或者轉(zhuǎn)Go同學(xué)的一套編碼規(guī)范

有沒有 xdm 是從別的語言轉(zhuǎn) Go 的歹垫,比如 Java 、php 等,有兄弟在剛開始學(xué)的時(shí)候疑惑怎么能寫出來優(yōu)秀的代碼臼寄。最近在項(xiàng)目中也 codereview 了不少 Go 語言的代碼,有必要總結(jié)下代碼規(guī)范溜宽,算是一個(gè)筆記記錄了吉拳。

說在前面,這只是我們團(tuán)隊(duì)的一套規(guī)范而已适揉。

今天我們聊一下 Go 的編碼規(guī)范留攒,大概分為幾大模塊,如注包/變量/常量命名嫉嘀、基本語法炼邀、函數(shù)、錯(cuò)誤處理剪侮、心得等拭宁。

1. 代碼風(fēng)格

1.1 代碼格式

  • 代碼必須用 gofmt 進(jìn)行格式化,goland 可以配置,可以自行搜索一下配置
  • 我們編寫的代碼每行應(yīng)該不超過 120 個(gè)字符红淡,超出部分用換行解決不狮。
  • 單個(gè)文件最大行數(shù)最大不超過 800 行.
  • 單個(gè)函數(shù)最大行數(shù)不超過 80 行。
  • import 規(guī)范
    • 不要使用相對(duì)路徑引入包在旱,例如 import ../util/net
    • 在導(dǎo)入包時(shí)摇零,多個(gè)相同包名沖突時(shí),必須使用導(dǎo)入別名
// bad
"github.com/google/uuid"

// good
uuid "github.com/google/uuid"
  • 導(dǎo)入的包建議分組桶蝎,引用匿名包建議用一個(gè)新的分組驻仅,并加上注釋方便后面小伙伴閱讀
import (
    // Go 標(biāo)準(zhǔn)庫
    "fmt"

    //第三方包
    "github.com/jinzhu/gorm"
    "github.com/google/uuid"
    "github.com/go-redis/redis/v8"

    // 匿名包
    /import mysql driver
    _"github.com/jinzhu/gorm/dialects/mysql"

    // 內(nèi)部包
    slice "xxx.local/pkg/v1/goslice"
    meta "xxx.local/pkg/v1/meta"
    gomap "xxx.local/pkg/v2/gomap"
)

1.2 聲明、初始化和定義

  • 一個(gè)函數(shù)需要使用多個(gè)變量時(shí)登渣,可以在函數(shù)最開頭處使用 var 聲明噪服。在函數(shù)外部聲明的變量不能使用 :=,會(huì)踩坑胜茧,不知道的可以評(píng)論區(qū)留言(要評(píng)論不易呀)粘优!
var (
    port = 8081
    metricServerPort = 2001
)
  • 在初始化結(jié)構(gòu)體用 &struct 代替 new(struct),確保與結(jié)構(gòu)體初始化一致呻顽,初始化結(jié)構(gòu)體時(shí)換行雹顺。
// bad
stu := new(S)
stu.Name = "張三"

// good
stu := &S{
    Name:"李四"
}
  • 使用 make 在聲明 map、array 等應(yīng)該指定容器的容量廊遍,從而達(dá)到預(yù)先分配內(nèi)容嬉愧。
users := make(map[int]string, 10)

tags := make([]int, 0, 10)
  • 使用標(biāo)準(zhǔn) var 關(guān)鍵字事,不要指定類型喉前,除非它與表達(dá)式的類型不同没酣。
// bad
var _f string F()

func F() string {
    return "hello world!"
}

// good 
var _f F()

func F() string {
    return "hello world!"
}

1.3 error 處理

  • 若函數(shù)返回 error, 必須對(duì) error 進(jìn)行處理卵迂,如果業(yè)務(wù)允許可以用 _ 接受忽略裕便。對(duì)應(yīng) defer 可以不用顯式進(jìn)行處理。
// bad
func InitConfig() error {
    ...
}
InitConfig()


// good
func InitConfig() error {
    ...
}
err := InitConfig()
if err != nil {
    ...
}
// or 
_ := InitConfig() 
  • error 作為返回值時(shí)必須作為最后一個(gè)參數(shù)返回
// bad
func InitConfig() (error,int) {
    ...
}

// good 
func InitConfig() (int, error) {
    ...
}
  • 錯(cuò)誤需要單獨(dú)處理狭握,盡量不要與其他的邏輯耦合在一起闪金。
// bad
res, err := InitConfig()
if err != nil || res != nil {
    return err
}

// good
res, err := InitConfig()
if err != nil {
    return err
}
if res != nil {
    return fmt.Errorf("invalid result")
}

1.4 panic處理

  • 業(yè)務(wù)代碼中禁止拋出 panic 錯(cuò)誤。
  • panic 只允許出現(xiàn)在在服務(wù)啟動(dòng)之前论颅,如讀取配置哎垦、鏈接存儲(chǔ)(redis、mysql 等)恃疯。
  • 業(yè)務(wù)代碼中建議用 error 而不是 panic 來傳遞漏设。

1.5 單元測(cè)試

  • 每個(gè)重要的函數(shù)都要編寫測(cè)試用例,合并代碼要自動(dòng)化運(yùn)行一下所有的 test今妄。
  • 文件命名 xxx_test.go郑口。
  • 函數(shù)命名建議使用 Test函數(shù)名鸳碧。

2. 命名規(guī)范

在每個(gè)語言中,命名規(guī)范在代碼規(guī)范中非常重要犬性,一個(gè)統(tǒng)一的瞻离、精確的命名不僅僅可以提高代碼的可讀性,也可以讓人覺的這個(gè)同志真的會(huì)呀乒裆。牛套利!

2.1 包命名規(guī)范

  • 包名必須與目錄名一致(這和其他 php、Java 還是有一點(diǎn)不太一樣的)鹤耍,盡量采取有意義肉迫、簡(jiǎn)短的包名,不要與 go 的標(biāo)準(zhǔn)庫名稱一樣稿黄。
  • 包名小寫喊衫,沒有下劃線,可以使用中劃線隔開杆怕,使用多級(jí)目錄來劃分目錄族购。
  • 包名不要出現(xiàn)復(fù)數(shù)命名。
  • 包名命名盡量簡(jiǎn)單一目了然财著,ge:user联四、log撑碴。

2.2 文件命名規(guī)范

  • 文件名要見名思義撑教,盡量簡(jiǎn)而短
  • 文件名小寫,組合詞用下劃線分割

2.3 函數(shù)命名規(guī)范

  • 與 php醉拓、Java 一樣伟姐,必須遵循駝峰規(guī)范,Go 語言中需要根據(jù)訪問的控制決定大駝峰還是小駝峰亿卤。
  • 單元測(cè)試的函數(shù)用大駝峰愤兵,TestFunc。

2.4 結(jié)構(gòu)體命名規(guī)范

  • 與 php排吴、Java 一樣秆乳,必須遵循駝峰規(guī)范,Go 語言中需要根據(jù)訪問的控制決定大駝峰還是小駝峰钻哩。
  • 避免使用 info 屹堰、data 這種無意義的名稱。
  • 命名使用名詞而非動(dòng)詞街氢。
  • 結(jié)構(gòu)體在聲明和初始化的時(shí)候需要換行扯键,eg:
type Student struct{
    Name string
    Age uint8
}

student := Student{
    Name: "張三",
    Age: 18,
}

2.5 變量命名規(guī)范

  • 和 php、Java 一樣珊肃,必須遵循駝峰規(guī)范荣刑,Go 語言中需要根據(jù)訪問的控制決定大駝峰還是小駝峰馅笙。
  • 若變量為私有時(shí),可以使用小寫命名厉亏。
  • 局部變量可以簡(jiǎn)寫董习,eg:i 表示 index。
  • 若變量代表 bool 值爱只,則可以使用 Is 阱飘、Can、Has 前綴命名虱颗,eg:
var isExit bool
var canReturn bool

2.6 常量命名規(guī)范

  • 必須遵循駝峰規(guī)范沥匈,Go 語言中需要根據(jù)訪問的控制決定大駝峰還是小駝峰。
  • 若代表枚舉值忘渔,需要先創(chuàng)建高帖。
type Code int

const (
    ErrNotFound Code = iota
    ErrFatal
)

3. 類型

3.1 字符串

好像學(xué)過的語言中,都是從字符串開始說起的畦粮。就像寫代碼第一行都是從 Hello World散址!一樣!同意的點(diǎn)贊哈宣赔。

  • 字符串判空值
// bad
if s == "" {
    ...
}

// good
if len(s) == 0 {
    ...
}
  • 字符串去除前后子串预麸。
// bad
var s1 "hello world"
var s2 "hello"
var s3 strings.TrimPrefix(s1, s2)

// good
var s1 "hello world"
var s2 "hello"
var s3 string

if strings.HasPrefix(s1, s2){
    s3 = s1[len(s2):]
}

3.2 切片 slice

  • 聲明 slice。
// bad
s := []string{}
s := make([]string, 10)

// good
var s []string
s := make([]string, 0, 10)
  • 非空判斷儒将。
//bad
if len(slice) >0 {
    ...
}

// good
if slice != nil && len(slice) > 0 {
    ...
}
  • slice copy吏祸。
// bad
var b1,b2 []byte
for i, v := range b1 {
    b2[i] = v
}
for i := range b1 {
    b2[i] = b1[i]
}

// good
copy(b2,b1)
  • slice 新增。
// bad
var a,b []int
for _, v := range a {
    b = append(b,v)
}

// good
var a, b []int
b := append(b, a...)

3.4 結(jié)構(gòu)體 struct

  • 初始化需要多行钩蚊。
type Student struct{
    Name string
    Age uint8
}

student := Student{
    Name: "張三",
    Age: 18,
}

4. 控制語句

4.1 if

  • if 可以用局部變量的方式初始化贡翘。
if err := InitConfig; err != nil {
    return err
}

4.2 for

  • 不允許在 for 中使用 defer, defer 只在函數(shù)結(jié)束時(shí)才會(huì)執(zhí)行砰逻。
// bad
for file := range files {
    fd, err := os.Open(file)
    if err != nil {
        return err
    }
    defer fd.close()
    
}

// good
for file := range files{
    func() {
        fd,err := os.open(file)
        if err鸣驱!=nil {
            return err
        }
        defer fd.close()
    }()
}

4.3 range

  • 如果不需要 key 直接用 _ 忽略,value 也一樣蝠咆。
for _, v := range students {
    ...
}

for i, _ := range students {
    ...
}

for i, v := range students {
    ...
}

注: 若操作指針時(shí)請(qǐng)注意不能直接用 s := v踊东。想知道可以評(píng)論區(qū)告訴我哦!

4.4 switch

  • 和其他語言不一樣刚操,必須要有 defalt
switch type {
    case 1:
        fmt.Println("type = 1")
        break
     case 2:
        fmt.Println("type = 2")
        break
     default :
        fmt.Println("unKnown type")
}

4.5 goto

  • 業(yè)務(wù)中不允許使用 goto闸翅。
  • 框架和公共工具也不允許使用 goto。

5. 函數(shù)

  • 傳參和返回的變量小寫字母赡茸。
  • 傳入?yún)?shù)時(shí)slice缎脾、map、interface占卧、chan 禁止傳遞指針類型遗菠。
  • 采用值傳遞联喘,不用指針傳值。
  • 入?yún)€(gè)數(shù)不能超出 5 個(gè)辙纬,超過的可以用 struct 傳值豁遭。

5.1 函數(shù)參數(shù)

  • 返回值超出 1 個(gè)時(shí),需要用變量名返回贺拣。
  • 多個(gè)返回值可以用 struct 傳蓖谢。

5.2 defer

  • 當(dāng)操作資源、或者事物需要提交回滾時(shí)譬涡,可以在創(chuàng)建開始下方就使用 defer 釋放資源闪幽。
  • 創(chuàng)建資源后判斷 error,非 error 情況后在用 defer 釋放涡匀。

5.3 代碼嵌套

  • 為了代碼可讀性盯腌,為了世界和平,盡量別用太多的嵌套陨瘩,因?yàn)檎娴暮茈y有人類能看懂腕够。

6. 日常使用感悟

  • 能不用全局變量就不用,可以用參數(shù)傳值的方式舌劳,這樣可以大大降低耦合帚湘,更有利于單元測(cè)試。
  • 衣服開發(fā)中甚淡,在函數(shù)間多用 context 傳遞上下文大诸,在請(qǐng)求開始時(shí)可以生成一個(gè) request_id,便于鏈路、日志追蹤病涨。

6.1 提高性能

  • 在業(yè)務(wù)開發(fā)中,盡量使用 strconv 來替代 fmt。
  • 我們?cè)谑褂?string 字符串類型時(shí)稚晚,當(dāng)修改的場(chǎng)景較多,盡量在使用時(shí)用 []byte 來替代缝其。因?yàn)槊看螌?duì) string 的修改都需要重新在申請(qǐng)內(nèi)存盗胀。

6.2 避免踩坑

  • append 要小心自動(dòng)擴(kuò)容的情況,最好在申明時(shí)分配好容量沸手,避免擴(kuò)容所帶來的性能上的損耗以及分配新的內(nèi)存地址外遇。若不能確定容量,應(yīng)選擇一個(gè)比較大一點(diǎn)的值契吉。
  • 并發(fā)場(chǎng)景下跳仿,map 非線程安全,需要加鎖捐晶。還有一種評(píng)論區(qū)告訴我吧菲语。
  • interface 在編譯期間無法被檢查妄辩,使用上會(huì)出現(xiàn) panic,需要注意

7. 總結(jié)

本篇很講了 Go 語言的編碼規(guī)范山上,當(dāng)時(shí)想說的眼耀,規(guī)范是大家預(yù)定的東西,每個(gè)公司佩憾、團(tuán)隊(duì)都會(huì)有不一樣的規(guī)范哮伟,只要大家一起遵循就好啦。你可以根據(jù)自己團(tuán)隊(duì)的需求妄帘,定一套屬于自己團(tuán)隊(duì)的項(xiàng)目規(guī)范楞黄。如果想小伙伴一起遵循,可以借助一些工具來保障執(zhí)行度抡驼。

講了很多谅辣,雖然很基礎(chǔ),希望對(duì)于剛剛轉(zhuǎn) Go 語言婶恼,或者剛學(xué)習(xí) Go 語言的同學(xué)有幫助吧桑阶。今天就到這里了。希望得到大家的一鍵三連勾邦。感謝蚣录!

歡迎點(diǎn)贊關(guān)注,公眾號(hào)搜:程序員祝融

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末眷篇,一起剝皮案震驚了整個(gè)濱河市萎河,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蕉饼,老刑警劉巖虐杯,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異昧港,居然都是意外死亡擎椰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門创肥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來达舒,“玉大人,你說我怎么就攤上這事叹侄」” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵趾代,是天一觀的道長(zhǎng)贯底。 經(jīng)常有香客問我,道長(zhǎng)撒强,這世上最難降的妖魔是什么禽捆? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任笙什,我火速辦了婚禮,結(jié)果婚禮上睦擂,老公的妹妹穿的比我還像新娘得湘。我一直安慰自己,他們只是感情好顿仇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布淘正。 她就那樣靜靜地躺著,像睡著了一般臼闻。 火紅的嫁衣襯著肌膚如雪鸿吆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天述呐,我揣著相機(jī)與錄音惩淳,去河邊找鬼。 笑死乓搬,一個(gè)胖子當(dāng)著我的面吹牛思犁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播进肯,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼激蹲,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了江掩?” 一聲冷哼從身側(cè)響起学辱,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎环形,沒想到半個(gè)月后策泣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抬吟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年萨咕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拗军。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡任洞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出发侵,到底是詐尸還是另有隱情,我是刑警寧澤妆偏,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布刃鳄,位于F島的核電站,受9級(jí)特大地震影響钱骂,放射性物質(zhì)發(fā)生泄漏叔锐。R本人自食惡果不足惜挪鹏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愉烙。 院中可真熱鬧讨盒,春花似錦、人聲如沸步责。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔓肯。三九已至遂鹊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蔗包,已是汗流浹背秉扑。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留调限,地道東北人舟陆。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像耻矮,于是被迫代替她去往敵國(guó)和親秦躯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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

  • 這份規(guī)范,是參考了 Go 官方提供的編碼規(guī)范米母,以及 Go 社區(qū)沉淀的一些比較合理的規(guī)范之后勾扭,加入自己的理解總結(jié)出的...
    拾丨玖閱讀 1,248評(píng)論 0 4
  • Uber Go 語言編碼規(guī)范 Uber 是一家美國(guó)硅谷的科技公司,也是 Go 語言的早期 adopter铁瞒。其開源了...
    知識(shí)鋪閱讀 524評(píng)論 0 0
  • 本規(guī)范旨在為日常Go項(xiàng)目開發(fā)提供一個(gè)代碼的規(guī)范指導(dǎo)妙色,方便團(tuán)隊(duì)形成一個(gè)統(tǒng)一的代碼風(fēng)格,提高代碼的可讀性慧耍,規(guī)范性和統(tǒng)一...
    Shaw_Lee閱讀 1,106評(píng)論 0 1
  • 內(nèi)容列表 指導(dǎo)原則指向interface的指針接收器(receiver)與接口零值Mutex是有效的在邊界處拷貝S...
    金科_閱讀 1,042評(píng)論 0 0
  • 引言 Go 語言是從既有的語言中借鑒了許多理念身辨,但其與眾不同的特性, 使得使用 Go 編程在本質(zhì)上就不同于其它語言...
    望仔閱讀 553評(píng)論 0 0