Go程式設計基礎—程式碼規範
專案目錄結構規範PROJECT_NAME
├── README.md 介紹軟體及文件入口
├── bin 編譯好的二進位制檔案,執行./build.sh自動生成,該目錄也用於程式打包
├── build.sh 自動編譯的指令碼
├── doc 該專案的文件
├── pack 打包後的程式放在此處
├── pack.sh 自動打包的指令碼,生成類似xxxx.20170713_14:45:35.tar.gz的檔案,放在pack檔案下
└── src 該專案的原始碼
├── main 專案主函式
├── models 專案程式碼
├── controllers 專案程式碼
├── research 在實現該專案中探究的一些程式
└── vendor 存放go的庫
├── github.com /xxx 第三方庫
└── xxx.com/obc 公司內部的公共庫
專案的目錄結構儘量做到簡明、層次清楚
檔名命名規範用小寫,儘量見名思義,看見檔名就可以知道這個檔案下的大概內容,對於原始碼裡的檔案,檔名要很好的代表了一個模組實現的功能。
命名規範包名,包名用小寫,使用短命名,儘量和標準庫不要衝突
介面名單個函式的介面名以”er”作為字尾,如Reader,Writer,介面的實現則去掉“er”
type Reader interface {
Read(p []byte) (n int, err error)
}
兩個函式的介面名綜合兩個函式名
type WriteFlusher interface {
Write([]byte) (int, error)
Flush() error
}
三個以上函式的介面名,類似於結構體名
type Car interface {
Start([]byte)
Stop() error
Recover()
}
全域性變數:採用駝峰命名法,僅限在包內的全域性變數,包外引用需要寫介面,提供呼叫
區域性變數:駝峰式,小寫字母開頭
常量常量:大寫,採用下劃線
**import 規範**import在多行的情況下,goimports會自動幫你格式化,在一個檔案裡面引入了一個package,建議採用如下格式:
import (
"fmt"
)
如果你的包引入了三種類型的包,標準庫包,程式內部包,第三方包,建議採用如下方式進行組織你的包:
import (
"encoding/json"
"strings"
"myproject/models"
"myproject/controller"
"git.obc.im/obc/utils"
"git.obc.im/dep/beego"
"git.obc.im/dep/mysql"
)
在專案中不要使用相對路徑引入包:
// 這是不好的匯入
import “../net”
// 這是正確的做法
import “xxxx.com/proj/net”
函式名函式名採用駝峰命名法,儘量不要使用下劃線
錯誤處理error作為函式的值返回,必須儘快對error進行處理
採用獨立的錯誤流進行處理
不要採用這種方式
if err != nil {
// error handling
} else {
// normal code
}
而要採用下面的方式
if err != nil {
// error handling
return // or continue, etc.
}
// normal code
如果返回值需要初始化,則採用下面的方式
x, err := f()
if err != nil {
// error handling
return
}
// use x
在邏輯處理中禁用panic
在main包中只有當實在不可執行的情況採用panic,例如檔案無法開啟,資料庫無法連線導致程式無法正常執行,但是對於其他的package對外的介面不能有panic,只能在包內採用。 建議在main包中使用log.Fatal來記錄錯誤,這樣就可以由log來結束程式。
Recoverrecover用於捕獲runtime的異常,禁止濫用recover,在開發測試階段儘量不要用recover,
recover一般放在你認為會有不可預期的異常的地方。
func server(workChan <-chan *Work) {
for work := range workChan {
go safelyDo(work)
}
}
func safelyDo(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Println("work failed:", err)
}
}()
// do 函式可能會有不可預期的異常
do(work)
}
defer在函式return之前執行,對於一些資源的回收用defer是好的,但也禁止濫用defer,defer是需要消耗效能的,所以頻繁呼叫的函式儘量不要使用defer。
// Contents returns the file's contents as a string.
func Contents(filename string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close() // f.Close will run when we're finished.
var result []byte
buf := make([]byte, 100)
for {
n, err := f.Read(buf[0:])
result = append(result, buf[0:n]...) // append is discussed later.
if err != nil {
if err == io.EOF {
break
}
return "", err // f will be closed if we return here.
}
}
return string(result), nil // f will be closed if we return here.
}
控制結構if,if接受初始化語句,約定如下方式建立區域性變數
if err := file.Chmod(0664); err != nil {
return err
}
for採用短宣告建立區域性變數
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
range如果只需要第一項(key),就丟棄第二個:
for key := range m {
if key.expired() {
delete(m, key)
}
}
如果只需要第二項,則把第一項置為下劃線
sum := 0
for _, value := range array {
sum += value
}
return,儘早return:一旦有錯誤發生,馬上返回
f, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)
方法的接收器名稱 一般採用struct的第一個字母且為小寫,而不是this,me或者self
type T struct{}
func (p *T)Get(){}
如果接收者是map,slice或者chan,不要用指標傳遞
//Mappackage main
import (
"fmt"
)
type mp map[string]string
func (m mp) Set(k, v string) {
m[k] = v
}
func main() {
m := make(mp)
m.Set("k", "v")
fmt.Println(m)
}
//Channelpackage main
import (
"fmt"
)
type ch chan interface{}
func (c ch) Push(i interface{}) {
c <- i
}
func (c ch) Pop() interface{} {
return <-c
}
func main() {
c := make(ch, 1)
c.Push("i")
fmt.Println(c.Pop())
}
如果需要對slice進行修改,通過返回值的方式重新賦值
//Slicepackage main
import (
"fmt"
)
type slice []byte
func main() {
s := make(slice, 0)
s = s.addOne(42)
fmt.Println(s)
}
func (s slice) addOne(b byte) []byte {
return append(s, b)
}
如果接收者是含有sync.Mutex或者類似同步欄位的結構體,必須使用指標傳遞避免複製
package main
import (
"sync"
)
type T struct {
m sync.Mutex
}
func (t *T) lock() {
t.m.Lock()
}
/*
Wrong !!!
func (t T) lock() {
t.m.Lock()
}
*/
func main() {
t := new(T)
t.lock()
}
如果接收者是大的結構體或者陣列,使用指標傳遞會更有效率。
package main
import (
"fmt"
)
type T struct {
data [1024]byte
}
func (t *T) Get() byte {
return t.data[0]
}
func main() {
t := new(T)
fmt.Println(t.Get())
}