統(tǒng)一規(guī)范篇
合理規(guī)劃目錄
本篇主要描述了公司內(nèi)部同事都必須遵守的一些開發(fā)規(guī)矩炒瘸,如統(tǒng)一開發(fā)空間萝风,既使用統(tǒng)一的開發(fā)工具來保證代碼最后的格式的統(tǒng)一胸哥,開發(fā)中對(duì)文件和代碼長度的控制橘忱,必須經(jīng)過go語言自帶的檢測機(jī)制等赴魁。
【原則1.1】合理規(guī)劃目錄,一個(gè)目錄中只包含一個(gè)包(實(shí)現(xiàn)一個(gè)模塊的功能)钝诚,如果模塊功能復(fù)雜考慮拆分子模塊颖御,或者拆分目錄。
GOPATH設(shè)置
【建議1.2】使用單一的 GOPATH
import 規(guī)范
【規(guī)則1.3.1】在非測試文件(*_test.go)中凝颇,禁止使用 . 來簡化導(dǎo)入包的對(duì)象調(diào)用潘拱。
【規(guī)則1.3.2】禁止使用相對(duì)路徑導(dǎo)入(./subpackage),所有導(dǎo)入路徑必須符合 go get 標(biāo)準(zhǔn)拧略。
【建議1.3.3】建議使用goimports工具或者IDE工具來管理多行import
代碼風(fēng)格
【規(guī)則1.4.1】提交代碼時(shí)芦岂,必須使用gofmt對(duì)代碼進(jìn)行格式化。
【規(guī)則1.4.2】提交代碼時(shí)垫蛆,必須使用golint對(duì)代碼進(jìn)行檢查禽最。
【建議1.4.3】提交代碼前,必須使用go vet對(duì)代碼進(jìn)行檢查袱饭。
大小約定
【建議1.5.1】單個(gè)文件長度不超過500行川无。
【建議1.5.2】單個(gè)函數(shù)長度不超過50行。
【規(guī)則1.5.3】單個(gè)函數(shù)圈復(fù)雜度最好不要超過10虑乖,禁止超過15懦趋。
【規(guī)則1.5.4】單行語句不能過長,如不能拆分需要分行寫决左。一行最多120個(gè)字符愕够。
【建議1.5.5】函數(shù)中縮進(jìn)嵌套必須小于等于3層走贪。
【原則1.5.6】保持函數(shù)內(nèi)部實(shí)現(xiàn)的組織粒度是相近的。
命名篇
本篇以開發(fā)時(shí)從上往下的順序既:開發(fā)前約定的基本命名規(guī)范惑芭、包坠狡、常量、變量遂跟、結(jié)構(gòu)體逃沿、參數(shù)、返回值的順序講解了開發(fā)中各個(gè)環(huán)節(jié)的命名規(guī)范幻锁。
基本命令規(guī)范
【規(guī)則2.1.1】需要注釋來補(bǔ)充的命名就不算是好命名凯亮。
【規(guī)則2.1.2】使用可搜索的名稱
【規(guī)則2.1.3】做有意義的區(qū)分
項(xiàng)目目錄名
【規(guī)則2.2.1】目錄名必須為全小寫單詞,允許加中劃線‘-’組合方式哄尔,但是頭尾不能為中劃線假消。
【建議2.2.2】雖然允許出現(xiàn)中劃線,但是盡量避免或少加中劃線岭接。
包名
【原則2.3.1】取名盡量采取有意義的包名富拗,簡單和可閱讀。
【規(guī)則2.3.2】包名必須全部為小寫單詞鸣戴,無下劃線啃沪,越短越好。盡量不要與標(biāo)準(zhǔn)庫重名窄锅。
【規(guī)則2.3.3】禁止通過中劃線連接多個(gè)單詞的方式來命名包名创千。
【建議2.3.4】包名盡量與所在目錄名一致,引用時(shí)比較方便入偷。
文件名
【規(guī)則2.4.1】文件名必須為小寫單詞追驴,允許加下劃線‘_’組合方式,但是頭尾不能為下劃線
【建議2.4.2】雖然允許出現(xiàn)下劃線盯串,但是盡量避免氯檐。
【建議2.4.3】文件名以功能為指引,名字中不需再出現(xiàn)模塊名或者組件名体捏。
常量
【規(guī)則2.5.1】常量&枚舉名采用大小寫混排的駝峰模式(Golang官方要求),不允許出現(xiàn)下劃線
【建議2.5.2】按照功能來區(qū)分糯崎,而不是將所有類型都分在一組几缭,并建議將公共常量置于私有常量之前
【規(guī)則2.2.3】如果是枚舉類型的常量,需要先創(chuàng)建相應(yīng)類型
【建議2.5.4】如果模塊的功能較為復(fù)雜沃呢、常量名稱容易混淆的情況下年栓,為了更好地區(qū)分枚舉類型,可以使用完整的前綴
變量
變量申明
【規(guī)則2.6.1】變量命名基本上遵循相應(yīng)的英文表達(dá)或簡寫薄霜,在相對(duì)簡單的環(huán)境(對(duì)象數(shù)量少某抓、針對(duì)性強(qiáng))中纸兔,可以將一些名稱由完整單詞簡寫為單個(gè)字母
變量命名慣例
【規(guī)則2.6.2】變量名稱一般遵循駝峰法,并且不允許出現(xiàn)下劃線否副,當(dāng)遇到特有名詞時(shí)汉矿,需要遵循以下規(guī)則:
- 如果變量為私有,且特有名詞為首個(gè)單詞备禀,則使用小寫洲拇,如:apiClient
- 其它情況都應(yīng)當(dāng)使用該名詞原有的寫法,如 APIClient曲尸、repoID赋续、UserID
【規(guī)則2.6.3】不要使用_來命名變量名,多個(gè)變量申明放在一起
【規(guī)則2.6.4】在函數(shù)外部申明必須使用var,不要采用:=另患,容易踩到變量的作用域的問題纽乱。
全局變量名
【規(guī)則2.6.5】全局變量必須為大小寫混排的駝峰模式,不允許出現(xiàn)下劃線昆箕。首字母根據(jù)作為范圍確定大小寫鸦列。
【建議2.6.6】盡量避免跨package使用全局變量,盡量減少全局變量的使用为严。
局部變量名
【規(guī)則2.6.7】局部變量名必須為大小寫混排敛熬,且首字母小寫,不能有下劃線第股。
循環(huán)變量
【建議2.6.8】for循環(huán)變量可以使用單字母应民。
結(jié)構(gòu)體(struct)
【規(guī)則2.7.1】struct申明和初始化格式采用多行
【規(guī)則2.7.2】結(jié)構(gòu)體名必須為大小寫混排的駝峰模式,不允許出現(xiàn)下劃線夕吻,可被包外部引用則首字母大寫诲锹;如僅包內(nèi)使用,則首字母小寫涉馅。
【建議2.7.3】結(jié)構(gòu)名建議采用名詞归园、動(dòng)名詞為好。
接口名
【規(guī)則2.8.1】接口名必須為大小寫混排稚矿,支持包外引用則首字母大寫庸诱,僅包內(nèi)使用則首字母小寫。不能有下劃線晤揣,整體必須為名詞桥爽。
【建議2.8.2】單個(gè)函數(shù)的接口名以”er”作為后綴。
函數(shù)和方法名
【規(guī)則2.9.1】函數(shù)名必須為大小寫混排的駝峰模式
【建議2.9.2】函數(shù)名力求精簡準(zhǔn)確昧识,并采用用動(dòng)詞或動(dòng)詞短
【規(guī)則2.9.3】方法接收名必須為大小寫混排钠四,首字母小寫。方法接收者命名要能夠體現(xiàn)接收者對(duì)象跪楞。
【建議2.9.4】接收者名通常1個(gè)或者2個(gè)字母就夠缀去,最長不能超過4個(gè)字母侣灶。
【建議2.9.5】接收者名不要使用me,this 或者 self 這種泛指的名字缕碎。
【建議2.9.6】定義方法時(shí)褥影,如果方法內(nèi)不會(huì)直接引用接收者,則省略掉接收者名阎曹。
參數(shù)名
【規(guī)則2.10】參數(shù)名必須為大小寫混排伪阶,且首字母小寫,不能有下劃線处嫌。
返回值
【規(guī)則2.11.1】返回值如果是命名的栅贴,則必須大小寫混排,首字母小寫熏迹。
【建議2.11.2】 函數(shù)的返回值應(yīng)避免使用命名的參數(shù)檐薯。
開發(fā)篇
本篇主要是講解開發(fā)中各個(gè)環(huán)節(jié)的開發(fā)規(guī)范和對(duì)一些代碼的優(yōu)化寫法。在本文中有一些特別標(biāo)黃的建議注暗,我真的建議你好好看看那些代碼坛缕,因?yàn)槟强赡軐?duì)你提高代碼開發(fā)會(huì)很有幫助。
包
【建議3.1.1】項(xiàng)目倉庫中包含全量的代碼
【建議3.1.2】建議采用 Glide 來管理第三方包
魔鬼數(shù)字
【規(guī)則3.2】代碼中禁止使用魔鬼數(shù)字捆昏。
常量 & 枚舉
【建議3.3.1】 為整數(shù)常量添加 String() 方法
【建議3.3.2】讓 iota 從 a +1 開始增量
結(jié)構(gòu)體
【規(guī)則3.4.1】對(duì)于要使用json轉(zhuǎn)換的結(jié)構(gòu)體代碼赚楚,變量名必須為大寫,否則你只會(huì)得到一個(gè)為空的對(duì)象
【建議3.4.2】 在初始化結(jié)構(gòu)體時(shí)使用帶有標(biāo)簽的語法
【建議3.4.3】將結(jié)構(gòu)體的初始化拆分到多行
運(yùn)算符
【規(guī)則3.5】運(yùn)算符前后骗卜、逗號(hào)后面宠页、if后面等需有單空格隔開。
函數(shù)
【原則3.6.1】保持函數(shù)內(nèi)部實(shí)現(xiàn)的組織粒度是相近的寇仓。
【建議3.6.2】 返回函數(shù)調(diào)用
【建議3.6.3】 withContext 封裝函數(shù)
參數(shù)
【建議3.7.1】參數(shù)按邏輯緊密程度安排位置, 同種類型的參數(shù)放在相鄰位置举户。
【建議3.7.2】避免使用標(biāo)識(shí)參數(shù)來控制函數(shù)的執(zhí)行邏輯。
【建議3.7.3】參數(shù)個(gè)數(shù)不要超過5個(gè)
返回值
【規(guī)則3.8.1】函數(shù)返回值個(gè)數(shù)不要超過3個(gè)遍烦。
【建議3.8.2】如果函數(shù)的返回值超過3個(gè)俭嘁,建議將其中關(guān)系密切的返回值參數(shù)封裝成一個(gè)結(jié)構(gòu)體。
注釋
【原則3.9.1】編寫代碼首先考慮如何代碼自我解釋服猪,然后才是添加注釋進(jìn)行補(bǔ)充說明
【原則3.9.2】注釋的內(nèi)容要清楚供填、明了,含義準(zhǔn)確罢猪,防止注釋二義性捕虽。
【原則3.9.3】在代碼的功能、意圖層次上進(jìn)行注釋坡脐,即注釋用于解釋代碼難以直接表達(dá)的意圖,而不是重復(fù)描述代碼房揭。
【規(guī)則3.9.4】所有導(dǎo)出對(duì)象都需要注釋說明其用途备闲;非導(dǎo)出對(duì)象根據(jù)情況進(jìn)行注釋晌端。必須時(shí),應(yīng)該說明值的取值范圍恬砂,及默認(rèn)值咧纠。
【規(guī)則3.9.5】注釋的單行長度不能超過 80 個(gè)字符。
【規(guī)則3.9.6】注釋需要緊貼對(duì)應(yīng)的包聲明和函數(shù)之前泻骤,不能有空行漆羔、
【規(guī)則3.9.7】非跨度很長的注釋,盡量使用 // 方式狱掂。
【規(guī)則3.9.8】避免多余的空格演痒,兩句注釋之間保持一個(gè)空格。
【原則3.9.9】注釋第一條語句應(yīng)該為一條概括語句趋惨,并且使用被聲明的名字作為開頭鸟顺。
【建議3.9.10】//與注釋的文檔之間空一格。
【規(guī)則3.9.11】每個(gè)程序包都應(yīng)該有一個(gè)包注釋器虾,一個(gè)位于package子句之前的塊注釋讯嫂。
【規(guī)則3.9.12】不要依靠用空格進(jìn)行對(duì)齊。
【建議3.27】類型定義一般都以單數(shù)信息描述兆沙。
【建議3.9.13】函數(shù)聲明處注釋描述函數(shù)功能欧芽、性能及用法,包括輸入和輸出參數(shù)葛圃、函數(shù)返回值千扔、可重入的要求等;定義處詳細(xì)描述函數(shù)功能和實(shí)現(xiàn)要點(diǎn)装悲,如實(shí)現(xiàn)的簡要步驟昏鹃、實(shí)現(xiàn)的理由、設(shè)計(jì)約束等
【建議3.9.14】如果函數(shù)或者方法為判斷類型(返回值主要為bool類型)诀诊,則以 <name style="margin: 0px; padding: 0px; box-sizing: border-box;">returns true if 開頭洞渤。</name>
錯(cuò)誤
【原則3.10.1】錯(cuò)誤處理的原則就是不能丟棄任何有返回err的調(diào)用,不要采用_丟棄属瓣,必須全部處理载迄。接收到錯(cuò)誤,要么返回err抡蛙,要么實(shí)在不行就panic护昧,或者使用log記錄下來
【規(guī)則3.10.2】error的信息不要采用大寫字母,盡量保持你的錯(cuò)誤簡短粗截,但是要足夠表達(dá)你的錯(cuò)誤的意思惋耙。
【規(guī)則3.10.3】導(dǎo)出的錯(cuò)誤變量的命名,以Err開始,如ErrSomething绽榛,無需導(dǎo)出的錯(cuò)誤變量命名湿酸,以Error作為后綴,如specificError
【規(guī)則3.10.4】公共包內(nèi)禁止使用panic灭美,如果有panic需要內(nèi)部recover并返回error。
其他
【建議3.11.1】在代碼中編寫字符串形式的json時(shí)届腐,使用反單引號(hào),而不是雙引號(hào)犁苏。
【規(guī)則3.11.2】相對(duì)獨(dú)立的程序塊之間、變量說明之后必須加空行傀顾,而邏輯緊密相關(guān)的代碼則放在一起襟铭。
【規(guī)則3.11.3】盡早return:一旦有錯(cuò)誤發(fā)生,馬上返回寒砖。
【建議3.11.4】禁止出現(xiàn)2處及以上的重復(fù)代碼。
【建議3.11.5】if條件判斷, 同時(shí)使用超過3個(gè)表達(dá)式以上的時(shí)候, 使用switch替代哩都。
【建議3.11.6】定義bool變量時(shí),要避免判斷時(shí)出現(xiàn)雙重否定婉徘,應(yīng)使用肯定形式的表達(dá)式漠嵌。
【建議3.11.7】for循環(huán)初始值從0開始,判斷條件使用<無等號(hào)的方式盖呼。
【建議3.11.8】長句子打印或者調(diào)用,使用參數(shù)進(jìn)行格式化分行
【建議3.11.9】 將 for-select 封裝到函數(shù)中
【建議3.11.10】把 slice约炎、map 等定義為自定義類型
【建議3.11.11】 為訪問 map 增加 setter,getters
參數(shù)傳遞
【建議3.11.12】 對(duì)于少量數(shù)據(jù)圾浅,不要傳遞指針
【建議3.11.13】 對(duì)于大量數(shù)據(jù)的 struct 可以考慮使用指針
【建議3.11.14】 傳入的參數(shù)是 map憾朴,slice狸捕,chan 不要傳遞指針众雷,因?yàn)?map做祝,slice株搔,chan 是引用類型,不需要傳遞指針的指針
注意閉包的調(diào)用
【原則3.11.15】在循環(huán)中調(diào)用函數(shù)或者goroutine方法纤房,一定要采用顯示的變量調(diào)用翻诉,不要再閉包函數(shù)里面調(diào)用循環(huán)的參數(shù)
優(yōu)化篇
本篇的意義是為開發(fā)提供一些經(jīng)過驗(yàn)證的開發(fā)規(guī)則和建議,讓開發(fā)在開發(fā)過程中避免低級(jí)錯(cuò)誤碰煌,從而提高代碼的質(zhì)量保證和性能效率
質(zhì)量保證
代碼質(zhì)量保證優(yōu)先原則
【原則4.1.1】代碼質(zhì)量保證優(yōu)先原則:
(1)正確性,指程序要實(shí)現(xiàn)設(shè)計(jì)要求的功能芦圾。
(2)簡潔性,指程序易于理解并且易于實(shí)現(xiàn)洪乍。
(3)可維護(hù)性夜焦,指程序被修改的能力壳澳,包括糾錯(cuò)茫经、改進(jìn)、新需求或功能規(guī)格變化的適應(yīng)能力卸伞。
(4)可靠性,指程序在給定時(shí)間間隔和環(huán)境條件下垮耳,按設(shè)計(jì)要求成功運(yùn)行程序的概率弃酌。
(5)代碼可測試性氨菇,指軟件發(fā)現(xiàn)故障并隔離妓湘、定位故障的能力,以及在一定的時(shí)間和成本前提下榜贴,進(jìn)行測試設(shè)計(jì)妹田、測試執(zhí)行的能力鹃共。
(6)代碼性能高效,指是盡可能少地占用系統(tǒng)資源霜浴,包括內(nèi)存和執(zhí)行時(shí)間。
(7)可移植性晌纫,指為了在原來設(shè)計(jì)的特定環(huán)境之外運(yùn)行,對(duì)系統(tǒng)進(jìn)行修改的能力锹漱。
對(duì)外接口原則
【原則4.1.2】對(duì)于主要功能模塊抽象模塊接口慕嚷,通過interface提供對(duì)外功能。
值與指針(T/*T)的使用原則
【建議4.1.3.1】基本類型傳遞時(shí)喝检,盡量使用值傳遞。
【建議4.1.3.2】如果傳遞字符串或者接口對(duì)象時(shí)蛇耀,建議直接實(shí)例傳遞而不是指針傳遞。
【建議4.1.3.3】如果是map译暂、func撩炊、chan外永,那么直接用T拧咳。
【建議4.1.3.4】如果是slice,method里面不重新reslice之類的就用T骆膝。
【建議4.1.3.5】如果想通過method改變里面的屬性,那么請(qǐng)使用*T阅签。
【建議4.1.3.6】如果是struct,并且里面包含了sync.Mutex之類的同步原語政钟,那么請(qǐng)使用*T樟结,避免copy精算。
【建議4.1.3.7】如果是一個(gè)大型的struct或者array,那么使用*T會(huì)比較輕量灰羽,效率更高。
【建議4.1.3.8】如果是struct疲吸、slice前鹅、array里面的元素是一個(gè)指針類型峭梳,然后調(diào)用函數(shù)又會(huì)改變這個(gè)數(shù)據(jù)舰绘,那么對(duì)于讀者來說采用*T比較容易懂葱椭。
【建議4.1.3.9】其它情況下,建議采用*T秦陋。
init的使用原則
【規(guī)則4.1.4.1】一個(gè)文件只定義一個(gè)init函數(shù)。
【規(guī)則4.1.4.2】一個(gè)包內(nèi)的如果存在多個(gè)init函數(shù)驳概,不能有任何的依賴關(guān)系旷赖。
defer的使用原則
【建議4.1.5.1】如果函數(shù)存在多個(gè)返回的地方,則采用defer來完成如關(guān)閉資源等孵、解鎖等清理操作。
【建議4.1.5.2】defer會(huì)消耗更多的系統(tǒng)資源俯萌,不建議用于頻繁調(diào)用的方法中。
【建議4.1.5.3】避免在for循環(huán)中使用defer弱恒。
Goroutine使用原則
【規(guī)則4.1.6.1】確保每個(gè)goroutine都能退出糖声。
【規(guī)則4.1.6.2】禁止在閉包中直接引用閉包外部的循環(huán)變量斤彼。
Channel使用原則
【規(guī)則4.1.7.1】傳遞channel類型的參數(shù)時(shí)應(yīng)該區(qū)分其職責(zé)分瘦。
【規(guī)則4.1.7.2】確保對(duì)channel是否關(guān)閉做檢查琉苇。
【規(guī)則4.1.7.3】禁止重復(fù)釋放channel。
其它
【建議4.1.8.1】使用go vet --shadow檢查變量覆蓋去团,以避免無意的變量覆蓋穷蛹。
【建議4.1.8.2】GO的結(jié)構(gòu)體中控制使用Slice和Map土陪。
【規(guī)則4.1.8.3】避免在循環(huán)引用調(diào)用 runtime.SetFinalizer肴熏。
【規(guī)則4.1.8.4】避免在for循環(huán)中使用time.Tick()函數(shù)。
性能效率
Memory優(yōu)化
【建議4.2.1.1】將多次分配小對(duì)象組合為一次分配大對(duì)象源哩。
【建議4.2.1.2】將多個(gè)不同的小對(duì)象綁成一個(gè)大結(jié)構(gòu)鸦做,可以減少內(nèi)存分配的次數(shù)。
【建議4.2.1.3】組合內(nèi)存分配的一個(gè)特殊情形是對(duì)分片數(shù)組進(jìn)行預(yù)分配泼诱。
【建議4.2.1.4】盡可能使用小數(shù)據(jù)類型,并盡可能滿足硬件流水線(Pipeline)的操作治筒,如對(duì)齊數(shù)據(jù)預(yù)取邊界。
【建議4.2.1.5】使用對(duì)象池來重用臨時(shí)對(duì)象系瓢,減少內(nèi)存分配句灌。
GC 優(yōu)化
【建議4.2.2.1】設(shè)置GOMAXPROCS為CPU的核心數(shù)目夷陋,或者稍高的數(shù)值胰锌。
【建議4.2.2.2】避免頻繁創(chuàng)建對(duì)象導(dǎo)致GC處理性能問題。
其它優(yōu)化建議
【建議4.2.3.1】減少[]byte和string之間的轉(zhuǎn)換酬土,盡量使用[]byte來處理字符。
【建議4.2.3.2】make申請(qǐng)slice/map時(shí)撤缴,根據(jù)預(yù)估大小來申請(qǐng)合適內(nèi)存。
【建議4.2.3.3】字符串拼接優(yōu)先考慮bytes.Buffer屈呕。
【建議4.2.3.4】避免使用CGO或者減少跨CGO調(diào)用次數(shù)。
【建議4.2.3.5】避免高并發(fā)調(diào)用同步系統(tǒng)接口蟋软。
【建議4.2.3.6】高并發(fā)時(shí)避免共享對(duì)象互斥。
【建議4.2.3.7】長調(diào)用鏈或在函數(shù)中避免申明較多較大臨時(shí)變量岳守。
【建議4.2.3.8】為高并發(fā)的輕量級(jí)任務(wù)處理創(chuàng)建routine池碌冶。
【建議4.2.3.9】建議版本提供性能/內(nèi)存監(jiān)控的功能,并動(dòng)態(tài)開啟關(guān)閉扑庞,但不要長期開啟pprof提供的CPU與MEM profile功能。