Golang开发规范 -- Go Code Review Comments

发表于 Golang comment...
疫情期间入职新公司一个月了,部门每月会有知识分享,由大家轮流进行! 我们的主力开发语言由PHP在逐步转向Go,参考Go Code Review Comments 在上一次的部门分享我做的是Go语言的开发规范,在官方的基础上做了一个简单的分类,供大家参考共同学习。最后有彩蛋!!!

格式化

gofmt

通过gofmt自动格式化代码,以保证所有的go代码与官方推荐的格式保持一致。

首字母大写和缩写

当一个单词在代码中,可以是全小写的。也可以选择首字母大写,或者缩写。值得注意的是,一旦该单词选择了首字母大写或缩写的风格,就应当在整份代码中保持这种风格,不要首字母大写和缩写两种风格混用。
以URL为例,如果选择了缩写URL这种风格,则应在整份代码中保持,以下命名都是不错的:URLPonyurlPony;切勿使用UrlPony这样的风格。

代码行长度

Golang中,没有严格限制代码行长度,但我们应该尽量避免一行内写过长的代码,以及将长代码进行断行。
每行不超过80个字符,依然是一个不错的建议。

尽可能减少正常逻辑代码的缩进

当函数调用返回错误时,我们需要判断错误是否为空,若不为空要进入错误处理的代码,结束后再进入正常逻辑代码。应当尽可能减少正常逻辑代码的缩进,这有利于提高代码的可读性,便于快速分辨出哪些还是正常逻辑代码。

段落引用尽早 return :一有错误发生,马上返回。

这是一个不好的代码风格,正常逻辑代码被缩进在else分支里面:

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

命名

文件命名

文件命名一律采用小写,不用驼峰,尽量见名思义,非测试文件禁止出现 *_test.go

包命名

包名应该是全小写单词,不要使用下划线;包名应该尽可能简短,长单词并不有助于可读性,尽量不要与标准库重名。

变量命名

一般采用驼峰,应该尽可能短,尤其是局部变量;对于特殊或全局变量可能需要对它有更多的描述建议使用长命名。

函数与结构体命名

必须为大小写驼峰模式,不要使用下划线,可以长,但是需要把函数功能描述清楚。例如:updateByIdgetUserInfo
函数名建议动词或者动宾结构单词,结构体建议名词或者动名词。

单个变量、结构体的声明应该使用单行声明,两个或以上使用圆括号声明方式。
枚举常量:使用类型前缀区分(理论上和文件名相同),采用驼峰。

声明空数组切片

这是一个推荐的做法:

var t[]string

这是不好的:
golang t := []string{}
原因是,前者能避免分配内存空间。有些时候,可能你从没向这个数组分片里面append元素。

给函数返回值命名

这是一个不好的代码风格,我们只知道函数返回的类型,但不知道每个返回值的名字:

func (n *Node) Parent1 *Node func (n *Node) parent2 (*Node, error)

这是一个不错的代码风格,我们准确知道每个返回值的名字:

func (n *Node) Parent1 (node *Node) func (n *Node) parent2 (node *Node, err error)

这条建议几乎不需要过多的解释!尤其对于一种场景,当你需要在函数结束的defer中对返回值做一些事情,给返回值名字实在是太必要了。

接受者命名

结构体函数中,接受者的命名不应该采用 methisself 等通用的名字,而应该采用简短的(1或2个字符)并且能反映出结构体名的命名风格。
例如,结构体名为 Client ,接受者可以命名为 c 或者 cl
这样做的好处是,当生成了godoc后,过长或者过于具体的命名,会影响搜索体验。

接受者类型

编写结构体函数时,接受者的类型到底是选择值还是指针通常难以决定。

一条万能的建议:如果你不知道要使用哪种传递时,请选择指针传递吧!

以下是一些不错的建议:

  1. 当接受者是map, chan, func, 不要使用指针传递,因为它们本身就是引用类型。
  2. 当接受者是slice,而函数内部不会对slice进行切片或者重新分配空间,不要使用指针传递。
  3. 当函数内部需要修改接受者,必须使用指针传递。
  4. 当接受者是一个结构体,并且包含了sync.Mutex或者类似的用于同步的成员。必须使用指针传递,避免成员拷贝。
  5. 当接受者类型是一个结构体并且很庞大,或者是一个大数组,建议使用指针传递来提高性能。
  6. 当接受者是结构体,数组或slice,并且其中的元素是指针,并且函数内部可能修改这些元素,那么使用指针传递是个不错的选择,这能使得函数的语义更加明确。
  7. 当接受者是小型结构体,小数组,并且不需要修改里面的元素,里面的元素又是一些基础类型,使用值传递是个不错的选择。

传递值而不是指针

不要为了节省一点空间就传递指针而不是传递值。
除非要传递的是一个庞大的结构体或者可预知在将来会变得非常庞大的结构体,指针是一个不错的选择。

注释

注释应当是一个完整的句子

所有的注释都应该是一个完整的句子。句子应该以主语开头,句号结尾。这样做,能使注释在转化成godoc时有一个不错的格式。

文档注释

Go提供两种注释风格,C的块注释风格 /**/ ,C++的行注释风格 //

每一个包都应该有包注释,位于文件的顶部,在包名出现之前。

如果一个包有多个文件,包注释只需要出现在一个文件的顶部即可。

包注释建议使用C注释风格,如果这个包特别简单,需要的注释很少,也可以选择使用C++注释风格。

每个public函数都应该有注释,注释句子应该以该函数名开头,如:

// Encode writes the JSON encoding of req to w. func Encode(w io.Writer, req *Request) { ...

error处理

不要抛出panic

尽量不要使用 panic 处理错误。函数应该设计成多返回值,其中含括返回响应的error类型。

error提示

错误提示不需要大写字母开头的单词,即使是句子的首字母也不需要。除非那是个专有名词或者缩写。同时,错误提示也不需要以句号结尾,因为通常在打印完错误提示后还需要跟随别的提示信息。

处理错误

不要将error赋值给匿名变量_(因为你不可以使用匿名变量,当把error赋值给匿名变量后,相当于抛弃了这个error)。

如果一个函数返回error,一定要检查它是否为空,判断函数调用是否成功。如果不为空,说明发生了错误,一定要处理它。

imports

当import多个包时,应该对包进行分组。同一组的包之间不需要有空行,不同组之间的包需要一个空行。标准库的包应该放在第一组。

package main import ( "fmt" "hash/adler32" "os" "appengine/foo" "appengine/user" "github.com/foo/bar" "rsc.io/goversion/version" )

流程控制

if

If 条件语句与PHP不同不需要圆括号,省略不必要的else语句。(左花括号必须与条件语句在同一行)

// if接受初始化变量,约定如下方式建立局部变量 if err := file.Chmod(666); err != nil { return err }

for

采用短声明建立局部变量,并且++--是操作语句而非表达式

for i := 0; i < 10; i++{}

工程根目录规范

${GOPATH}/src/${GIT_HOST}/{$GIT_USER}|${GIT_GROUP}/${PROJ_ROOT} GIT_HOST git 仓库 host GIT_USER git 用户名称 同 GIT_GROUP 二选一 GIT_GROUP git 工作组名称 GIT_USER 二选一 PROJ_ROOT 项目根 比如我的就在:github.com/wkk/goblog

写在最后:

疫情当下,健康是福!

码农生涯的第一次跳槽,颠沛流离的一个时间段,沉淀了好多也成长了一些!难得有时间系统整理几年的过往,收获颇丰,加上对go的一个自我实践和学习,确实对工作起到了很大的帮助和提升!

一段时间对新工作的熟悉和融入,终于没有了开始的手忙脚乱,接下来还会再进行goblog的优化,近期进行一次简单整理后会更新git地址供大家一起讨论学习,能接受粗粮的同学也可以开箱即用,现在有需要的可以Email wkekai@163.com

做为小学生年前开始了开源的脚步,虽然很晚很慢,但是一段时间也认识了好几个大神,疫情期间也加入了几个‘武汉’开源团队,种种原因没有贡献多少力量深感惭愧!最近自己的项目也有了几个star,在github go项目中排名进入了前·30·,小有收获!也有点小骄傲和成就感!也有几个外国友人follow了我,受宠若惊!

感恩感谢!

过往都值得回味,都值得回想!不想只是枯燥的技术分享(虽然鄙人技术有点low),也会有一些自己碎家常的唠叨和‘分享’!

立一个近期简单的规划FLAG:
1、短期内将git版本库进行一个整理开放,并做一个结构的完善(初构已有)
2、长期打算做一个Go系列读书笔记的分享(很宏伟很有挑战)
3、会做一个面试的系列(前阵子在牛客分享过一次面试题反响还不错,差不多有3000的阅读量),会包含:MySQLRedisPHP常见算法网络的几个专题(这个争取五一做前三个,因为内容都在印象笔记中)。

如有需要更正和不足之处请大家Email:wkekai@163.com

参考:[Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)