Go编码规范
2023-08-19 本文已影响0人
无心Y
命名规范
文件名
- 小写单词,使用下划线分割,简短有意义
base_test.go
包命名
- 包名与目录名相同,小写单词,不要使用下划线,不要和标准库冲突
package main
package utils
结构体
- 驼峰式命名,首字母根据访问控制大小写
type User struct {
Name string
Age uint32
Gender uint8
}
接口名
- 单个方法,接口以er结尾
interface Reader {
Read(content []byte) (n int, err error)
}
- 两个方法,接口综合两个方法名
interface ReadWriter {
Read(content []byte) (n int, err error)
Write(content []byte) (n int, err error)
}
- 三个以上的方法,接口命名同结构体
type Car interface {
Start([]byte)
Stop() error
Recover()
}
函数名
-
函数/方法名称应该直接表明函数的用途
-
对于Seter和Geter的命名方式,Owner, SetOwner
type Pen struct{
Length float64
owner string
}
(p *Pen) func Owner() string {
return p.owner
}
(p *Pen) func SetOwner(owner string) {
p.owner = owner
}
- 对于判断类型的函数,以Has, Is, Can, Allow开头
func HasPrefix(name string, prefixes []string) bool { ... }
func IsEntry(name string, entries []string) bool { ... }
func CanManage(name string) bool { ... }
func AllowGitHook() bool { ... }
常量名
- 全部大写,用下划线分割
const APP_VERSION = "1.0"
- 枚举类型的常量,需要先创建类型
type Protocol string
const (
HTTP Protocol = "http"
HTTPS Protocol = "https"
)
- 功能较复杂的情况下,常量名容易混淆,可以使用完整的前缀
type PullRequestStatus int
const (
PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota
PULL_REQUEST_STATUS_CHECKING
PULL_REQUEST_STATUS_MERGEABLE
)
变量名
- 全局变量,驼峰式命名
var (
ProjectName string
ProjectOwner string
)
- 局部变量,驼峰式命名,使用短变量赋值
projectName := "venus"
projectOwner := "loda"
-
变量缩写
- user可以简写为 u
- userId可简写为 uid
- context可简写为 ctx
- error可以简写为 err
- ...
-
bool类型变量,以Is, Has, Can, Allow等开头
注释规范
注释风格
-
全部使用单行注释
-
单行注释不要太长,不能超过120个字符
包注释
-
位于package之前
-
包的基本简介
-
创建者
-
创建时间
// util 包含一些公用的函数
// Author: loda
// Date: 20210723
package util
结构体(接口)注释
- 结构体名称,结构体说明
// User , 用户对象,定义了用户的基础信息
type User struct{
Username string // 用户名
Email string // 邮箱
}
函数(方法)注释
- Title, Description, Author, Params, Return
// @Title NewtAttrModel
// @Description 属性数据层操作类的工厂方法
// @Author loda
// @Param ctx 上下文信息
// @Return 属性操作类指针
func NewAttrModel(ctx *common.Context) *AttrModel {
}
代码逻辑注释
-
关键位置的代码逻辑
-
局部较复杂的逻辑说明
Bug注释
- 针对代码中出现的Bug
// BUG(astaxie):This divides by zero.
var i float = 1/0
代码风格
缩进和换行
-
缩进使用gofmt工具即可
-
一行不超过120个字符,超过换行显示
控制结构
-
语句的结尾
- 不需要分号,默认一行就是一句
- 可以显示使用分号连接两行,但不建议使用
-
括号和空格
- 左大括号不换行
- 所有的运算符和操作数之间要有空格
-
if else
- 条件语句不需要小括号
- 省略不必要的else
- 可加上合适的初始化语句
result := query()
if err := check(result); err != nil {
return err
}
- for
for i:=0; i++; i<10 {
// do sth
}
- 遍历
import fmt
for pos, str := range "hello" {
fmt.Printf("%q: %d\n", str, pos)
}
// 遍历 array, slice, map, chan, string
for k, v := range mapA {
// do sth
}
- switch
- 表达式可以为变量、常量等
- case可以列举多个条件
- 可使用break体检结束
func Factory(name string, value interface{}) interface{} {
var object interface{}
switch name {
case "A1", "A2":
object = NewA()
case "B":
object = newB()
if value == nil {
break
}
object.SetValue(value)
}
return object
}
- switch type
func ErrorWrap(e interface{}) *TraceableError {
var message string
switch e := e.(type) {
case TraceableError:
return &e
case *TraceableError:
return e
case error:
message = e.Error()
default:
message = fmt.Sprintf("%v", e)
}
return ErrorNew(message, 2)
}
- break
- break在switch中可以提前结束
- break在循环中需要借助标签提前结束
package main
import (
"fmt"
)
func main() {
Loop:
for index := 1; index < 10; index++ {
switch index % 5 {
case 1:
break
case 0:
break Loop
default:
fmt.Printf("%v\n", index)
}
}
}
- select类似switch, 用于多个管道的读取
结构体和接口
- 结构体初始化
type MyStruct struct {
Value int
}
type MyStruct2 struct {
MyStruct
ID int
}
p1 := new (MyStruct) // type *SyncedBuffer
p2 := &MyStruct{} // type *SyncedBuffer
s1 := MyStruct{
Value: 0,
}
s2 := MyStruct2{
ID: 0,
MyStruct1: MyStruct1 {
Value: 1,
},
}
method receiver
-
map, chan, interface, func: 不要使用指针(它们隐式使用指针)
-
如果不存在对切片重新分配,则不要使用指针
-
如果方法会改变receiver,则使用指针
-
如果receiver中有类似sync.Mutex等用于同步的成员,则必须使用指针
-
一般接受者都声明为指针类型
defer
- 打开文件、连接等后,需要defer关闭连接
- 慎用defer处理锁
chan
-
创建管道时使用make
-
只读管道: ch <-chan int
-
只写管道: ch chan<- int
ch := make(chan int 1)
func handle(readCh <-chan int, writeCh chan<- int) {
go func() {
v := <-readCh
writeCh <- 2 * v
}()
}
gorouting
-
线程安全
- gorouting是在线程池中执行的,在gorouting中访问闭包需要考虑线程安全问题
-
Once
- 提供一个线程安全的单次执行的接口,用于单例模式或初始化场景
package main
import (
"sync"
)
type singleton struct {}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
- WaitGroup
- 初始化 WaitGroup,加上特定的值
- goroutine结束时记得调用WaitGroup.Done()
- 主流程执行 WaitGroup.Wait()
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
fmt.Printf("hello ")
wg.Done()
}()
wg.Wait()
fmt.Printf("world\n")
}
- Atomic
- sync.atomic包提供了一系列同步原语来控制临界资源的访问
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var value int64
var wg sync.WaitGroup
wg.Add(2)
fun := func(count int) {
for index := 0; index < count; index++ {
atomic.AddInt64(&value, 1)
}
wg.Done()
}
go fun(100)
go fun(100)
wg.Wait()
fmt.Printf("%v\n", value)
}
import规范
-
依次导入标准包、内部包、第三方包
-
不要使用相对路径
import (
"encoding/json" //标准包
"strings"
"myproject/models"
"myproject/controller" //内部包
"myproject/utils"
"github.com/astaxie/beego" //第三方包
"github.com/go-sql-driver/mysql"
)
错误处理
-
不要使用'_'丢弃任何有返回err的调用
-
一旦有错误发生,尽早return
-
采用独立的错误流进行处理
-
尽量不要使用panic
// 错误写法
if err != nil {
// error handling
} else {
// normal code
}
// 正确写法
if err != nil {
// error handling
return // or continue, etc.
}
// normal code
参数传递
-
对于少量数据不要使用指针
-
对于大量数据的struct可以考虑使用指针
-
传入参数是map,slice,chan不要传递指针,因为map,slice,chan是引用类型,不需要传递指针的指针
单元测试
-
单元测试代码的go文件必须以_test.go结尾
-
单元测试的函数名必须以Test开头,是可导出公开的函数
-
测试函数的签名必须接收一个指向testing.T类型的指针作为参数,并且该测试函数不能返回任何值