Go编码规范指南

参考:https://studygolang.com/articles/2059

Go编码规范指南

序言

看过很多方面的编码规范,可能每一家公司都有不同的规范,这份编码规范是写给我自己的,同时希望我们公司内部同事也能遵循这个规范来写Go代码。

如果你的代码没有办法找到下面的规范,那么就遵循标准库的规范,多阅读标准库的源码,标准库的代码可以说是我们写代码参考的标杆。

一、go命名规范

命名规则

  1. golang的变量函数命名需要使驼峰命名法,且不能出现下划线, 文件名使用下划线
  2. golang中根据首字母的大小写来确定可以访问的权限。无论是方法名、常量、变量名还是结构体的名称,首字母大写表示结构体中的变量或者是包中的函数public,如果是小写则表示是private
  3. xxx_test.go包名和文件夹名字最好一样

变量命名规则

变量命名命名必须清晰、明了,有明确含义的单词,命名中禁止使用缩写,除非已是业界通用或标准化的缩写;
单字母名称仅适用于短方法中的局部变量,名称长短应与其作用域相对应。若变量或常量可能在代码中多处使用,则应赋其以便于搜索的名称且有意义的名称。
变量名称一般遵循驼峰法,但遇到特有名词时,需要遵循以下规则:如果变量为私有,且特有名词为首个单词,则使用小写,如apiClient,其它情况都应当使用该名词原有的写法,如APIClient、repoID、UserID
错误用例:UrlArray,应该写成urlArray或者URLArray

驼峰式命名,名字可以长但是得把功能,必要的参数描述清楚,
多个变量申明放在一起
var (
Found bool
count int
)

函数命名规则

函数名应当是动词或动词短语,如postPayment、deletePage、save。
并依Javabean标准加上get、set、is前缀。例如:xxx + With + 需要的参数名 + And + 需要的参数名 + ……

结构体命名规则

结构体名应该是名词或名词短语,如Custome、WikiPage、Account、AddressParser,WriteDbMgr,ConfMgr,避免使用
Manager、Processor、Data、Info等类名,并且类名不应当是动词。
结构体中属性名大写,如果属性名小写则在数据解析(如json解析,或将结构体作为请求或访问参数)时无法解析

包名命名规则

包名应该为小写单词,不要使用下划线或者混合大小写。
保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库不要冲突。

接口命名规则

//接口名通常以er结尾,如果里面只规定了一个方法,命名"方法名+er"
type Reader interface {
Read(p []byte)(n int,err error )
}
//两个函数的接口名综合两个函数名
type WriteFlusher interface {
Write([]byte) (int, error)
Flush() error
}
//三个以上函数的接口名,抽象这个接口的功能,类似于结构体名
type Carer interface {
Start([]byte)
Stop() error
Recover()
}
log包实现了常⽤的log相关的函数

常量

常量均需使用全部大写字母组成,并使用下划线分词:const APP_VER = “1.0”
如果是枚举类型的常量,需要先创建相应类型:
type Scheme string
const (
HTTP Scheme = “http”
HTTPS Scheme = “https”
)
如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀:
type PullRequestStatus int
const (
PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota
PULL_REQUEST_STATUS_CHECKING
PULL_REQUEST_STATUS_MERGEABLE
)

变量规则举例

变量命名基本上遵循相应的英文表达或简写,在相对简单的环境(对象数量少、针对性强)中,可以将一些名称由完整单词简写为单个字母,例如:
– user 可以简写为 u
– userID 可以简写 uid
– 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头:
var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool

参数传递

对于少量数据,不要传递指针
对于大量数据的struct可以考虑使用指针
传入参数是map,slice,chan不要传递指针,因为map,slice,chan是引入类型,不需要传递指针的指针

单元测试

单元测试文件名命名规范为 example_test.go
测试用例的函数名称必须以 Test 开头,例如:TestExample

二、格式化规范

go默认已经有了gofmt工具,但是我们强烈建议使用goimport工具,这个在gofmt的基础上增加了自动删除和引入包.
go get golang.org/x/tools/cmd/goimports
不同的编辑器有不同的配置, sublime的配置教程:http://michaelwhatcott.com/gosublime-goimports/
LiteIDE默认已经支持了goimports,如果你的不支持请点击属性配置->golangfmt->勾选goimports保存之前自动fmt你的代码。

行长约定

一行最长不超过80个字符,超过的请使用换行展示,尽量保持格式优雅。

go vet

vet工具可以帮我们静态分析我们的源码存在的各种问题,例如多余的代码,提前return的逻辑,struct的tag是否符合标准等。
安装:go get golang.org/x/tools/cmd/vet
使用如下:go vet .

import

对import的包进行分组管理,用换行符分割,并且标准库作为分组的第一组。如果你的包引用了三种类型的包,标准库包,程序内部包,第三方包,建议采用如下方式进行组织你的包

import (
“encoding/json”
“strings”

"myproject/models"
"myproject/controller"
"myproject/utils"

"github.com/astaxie/beego"
"github.com/go-sql-driver/mysql"

)
有顺序的引入包,不同的类型采用空格分离,第一种实标准库,第二是项目包,第三是第三方包。

在项目中不要使用相对路径引入包:
// 这是不好的导入
import “…/net”
// 这是正确的做法
import “github.com/repo/proj/src/net”

变量声名

在函数外部申明必须使用var,不要采用:=,容易踩到变量的作用域的问题。

自定义类型的string循环问题

如果自定义的类型定义了String方法,那么在打印的时候会产生隐藏的一些bug

type MyInt int
func (m MyInt) String() string {
return fmt.Sprint(m) //BUG:死循环
}

func(m MyInt) String() string {
return fmt.Sprint(int(m)) //这是安全的,因为我们内部进行了类型转换
}

避免返回命名的参数

如果你的函数很短小,少于10行代码,那么可以使用,不然请直接使用类型,因为如果使用命名变量很容易引起隐藏的bug

func Foo(a int, b int) (string, ok){

}
当然如果是有多个相同类型的参数返回,那么命名参数可能更清晰:
func (f *Foo) Location() (float64, float64, error)
下面的代码就更清晰了:
// Location returns f’s latitude and longitude.
// Negative values mean south and west, respectively.
func (f *Foo) Location() (lat, long float64, err error)

错误处理

错误处理的原则就是不能丢弃任何有返回err的调用,不要采用_丢弃,必须全部处理。接收到错误,要么返回err,要么实在不行就panic,或者使用log记录下来
error 信息
error的信息不要采用大写字母,尽量保持你的错误简短,但是要足够表达你的错误的意思。

长句子打印或者调用,使用参数进行格式化分行

我们在调用fmt.Sprint或者log.Sprint之类的函数时,有时候会遇到很长的句子,我们需要在参数调用处进行多行分割:
下面是错误的方式:

log.Printf(“A long format string: %s %d %d %s”, myStringParameter, len(a),
    expected.Size, defrobnicate(“Anotherlongstringparameter”,
        expected.Growth.Nanoseconds() /1e6))

应该是如下的方式:

log.Printf( 
    “A long format string: %s %d %d %s”, 
    myStringParameter,
    len(a),
    expected.Size,
    defrobnicate(
        “Anotherlongstringparameter”,
        expected.Growth.Nanoseconds()/1e6, 
    ),
)

注意闭包的调用

在循环中调用函数或者goroutine方法,一定要采用显示的变量调用,不要再闭包函数里面调用循环的参数

fori:=0;i<limit;i++{
go func(){ DoSomething(i) }() //错误的做法
go func(i int){ DoSomething(i) }(i)//正确的做法
}
http://golang.org/doc/articles/race_detector.html#Race_on_loop_counter

在逻辑处理中禁用panic

在main包中只有当实在不可运行的情况采用panic,例如文件无法打开,数据库无法连接导致程序无法
正常运行,但是对于其他的package对外的接口不能有panic,只能在包内采用。

强烈建议在main包中使用log.Fatal来记录错误,这样就可以由log来结束程序。

注释规范

注释 参考:http://golang.org/doc/effective_go.html#commentary

在编码阶段应该同步写好变量、函数、包的注释,最后可以利用godoc导出文档。注释必须是完整的句子,句子的结尾应该以句号作为结尾(英文句号)。注释推荐用英文,可以在写代码过程中锻炼英文的阅读和书写能力。且用英文不会出现各种编码的问题。
每个包都应该有一个包注释,一个位于package语句之前的块注释或行注释。包如果有多个go文件,只需要出现在一个go文件中即可

bug注释

针对代码中出现的bug,可以采用如下教程使用特殊的注释,在godocs可以做到注释高亮:

// BUG(astaxie):This divides by zero. 
var i float = 1/0
http://blog.golang.org/2011/03/godoc­documenting­go­code.html

注释的意义#
注释可以帮我们很好的完成文档的工作,写得好的注释可以方便我们以后的维护。
/**/ 的块注释和 // 的单行注释两种注释风格, 在我们的项目中为了风格的统一,全部使用单行注释,注释的质量决定了生成的文档的质量。
下面从包注释、结构体(接口)注释、函数(方法)注释、代码逻辑注释以及注释规范方面进行说明。
包注释#
每个包都应该有一个包注释,一个位于 package 子句之前行注释
包注释应该包含下面基本信息
Copy
// @Title 请填写文件名称(需要改)
// @Description 请填写文件描述(需要改)
// @Author 请填写自己的真是姓名(需要改) ${DATE} ${TIME}
// @Update 请填写自己的真是姓名(需要改) ${DATE} ${TIME}
package ${GO_PACKAGE_NAME}
结构(接口)注释#
每个自定义的结构体或者接口都应该有注释说明,该注释对结构进行简要介绍,放在结构体定义的前一行,格式为: 结构体名, 结构体说明。同时结构体内的每个成员变量都要有说明,该说明放在成员变量的后面(注意对齐),实例如下:

Copy
// User 用户对象,定义了用户的基础信息
type User struct{
Username string // 用户名
Email string // 邮箱
}
函数(方法)注释#
每个函数,或者方法(结构体或者接口下的函数称为方法)都应该有注释说明
函数的注释应该包括三个方面
Copy
// @title 函数名称
// @description 函数的详细描述
// @auth 作者 时间(2019/6/18 10:57 )
// @param 输入参数名 参数类型 “解释”
// @return 返回参数名 参数类型 “解释”
代码逻辑注释#
每个代码块都要添加单行注释
注视使用 TODO 开始 详细如下
Copy
// TODO 代码块的执行解释
if userAge < 18 {

}
注释要求#
统一使用中文注释,对于中英文字符之间严格使用空格分隔, 这个不仅仅是中文和英文之间,英文和中文标点之间也都要使用空格分隔
全部使用单行注释,禁止使用多行注释
和代码的规范一样,单行注释不要过长,禁止超过 120 字符。
缩进和折行#
缩进必须使用 gofmt 工具格式化
折行方面,一行最长不超过 120 个字符,超过的请使用换行展示,尽量保持格式优雅
括号和空格#
括号和空格方面,也可以直接使用 gofmt 工具格式化(go 会强制左大括号不换行,换行会报语法错误),所有的运算符和操作数之间要留空格。

struct规范 更多的请参考:https://code.google.com/p/go-wiki/wiki/CodeReviewComments#Receiver_Type

struct申明和初始化格式采用多行:
定义如下:

type User struct{
Username string
Email string
}
初始化如下:

u := User{
Username: “astaxie”,
Email: “astaxie@gmail.com”,
}
recieved是值类型还是指针类型
到底是采用值类型还是指针类型主要参考如下原则:

func(w Win) Tally(playerPlayer)int //w不会有任何改变
func(w *Win) Tally(playerPlayer)int //w会改变数据

带mutex的struct必须是指针receivers
如果你定义的struct中带有mutex,那么你的receivers必须是指针

摘自https://www.cnblogs.com/zhichaoma/p/12509999.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值