文章详情页 您现在的位置是:网站首页>文章详情
Golang编码规范
Jeyrce.Lu
发表于:2021年6月19日 16:45
分类:【Go】
3087次阅读
本文主要结合 uber的go代码风格和官方一些经验整理,有删改。
一、命名规范
(0)关键字
| break | default | func | interface | select |
| case | defer | go | map | struct |
| chan | else | goto | package | switch |
| const | fallthrough | if | range | type |
| continue | for | import | return | var |
所有小写命名应当避免和关键字相同,用大写开头或添加下划线区分。
// 不推荐的写法 var case string // 推荐的写法 var Case var _case var case_
(1)包名
保持名称和dir一致(main包除外)
简短有意义
尽量不合标准库冲突
全部为小写单词(不要使用下划线、大小写混合)
// 一般写法 package User // 含有大写 package good_lucky_user // 含有下划线,且命名太过冗杂 package os // 和标准库冲突 // 推荐的写法 package user package xxxos
(2)文件名
简短有意义
全部为小写单词
下划线分割单词
// 一般写法 style/model/ ├── simple_healthy_fantistic_task.go └── Task.go // 推荐写法 style/model/ ├── task.go └── task_test.go
(3)结构体名
驼峰命名法,大小写控制私有和共有
声明和初始化每个字段占用一行
不包含包名
// 一般写法
package model
// 不符合驼峰命名法
type file_parser struct {
Path, Size string // 两个字段放在一行
Count int
}
// 包名已经有model
type ModelJob struct {
...
}
// 推荐写法
package model
type fileParser struct {
Path string
Size string
Count int
}
type Job struct {
...
}(4)接口名
同结构体命名规范
单函数接口尽量采用"er"作为后缀,例如标准库中的“Reader、Writer”
// 一般写法
type Read interface {
Read(string) ([]byte, error)
}
type Write interface {
Write(interface{}, string) error
}
// 推荐写法
type Reader interface {
Read(string) ([]byte, error)
}
type Writer interface {
Write(interface{}, string) error
}(5)变量名
驼峰命名法
缩写单词全部保持大写或者小写
bool类型推荐使用“Has、Is、Can、Allow”开头
// 一般写法 var user_email // 下环线连接 var Api // api是一个缩写单词,但是采用大小写混合 var IfRunning bool // 随意命名 // 推荐写法 var userEmail var API var api var IsRunning bool
(6)常量名
常量无需全部大写、下划线分词,遵循驼峰命名即可
// 一般写法 const LINK_PATH = "https://www.woqutech.com" // 推荐写法 const LinkPath "https://www.wouqtech.com"
二、注释
(0)注释方式的选用
块注释 /*注释*/
行注释 // 注释
一般情况下无需关心采用哪种注释方式,但是标准库以及各大开源项目最多使用//
(1)包注释
出现在package语句之前
包内如果有多个文件则在其中一个文件(一般和包名相同)写即可
(2)结构体(接口)注释
每个结构体都应当有注释,放在结构体定义的前一行
重要字段或者意义不明确字段应当有注释说明,位于字段后面对齐
// 一般写法
type Area struct{} // 无注释
type User struct { // 用户模型 // 注释位置
Name string
Password string
Option map[string]interface{}
}
// 推荐写法
// 区域数据模型
type Area struct{}
// 用户数据模型
type User struct {
Name string
Password string
Option map[string]interface{} // 扩展字段,记录用户的一些额外属性
}(3)函数(方法)注释
每个函数都应当书写注释,放在函数声明前
注释一般包含4方面
简要功能说明
参数列表
返回值
重要情况说明(此部分不是所有函数都需要)
逻辑步骤
注意点
一些补充说明的参考链接
关键的位置参数可以使用 /*参数意义*/来注释
(4)代码逻辑注释
关键位置、复杂逻辑需要添加适当注释方便其他开发者理解
全部采用中文注释(要对自己祖国的语言有信息,不要搞些花里胡哨的英文)
同代码语句,尽量不超过120字符每行
三、代码风格
(0)避免panic
panic是一种严重错误,出现之后程序直接崩溃,因此需要尽量避免,除非你明白自己在做什么
使用recover来捕获一些错误
(1)语句缩进、折行
语句尽量不超过120字符,超过后使用\或者小括号折行
使用table缩进
(2)导入
导入多个包时需要遵循一定的顺序
标准库
三方包
本地包
不同类型的导入之间使用空格分开
当导入的包携带版本或和其他包冲突,应当定义别名
// 一般写法 import ( "database/sql/driver" "encoding/json" "fmt" "time" "sourcegraph.com/sqs/byte" "gopkg.in/yaml.v2" "github.com/woqutech/phoenix/discovery" ) // 推荐写法 import ( // 标准库中的包 "fmt" "time" "database/sql/driver" "encoding/json" // 三方包 pbyte "sourcegraph.com/sqs/byte" // 和byte类型冲突,因此定义别名 yaml "gopkg.in/yaml.v2" // 目录和包名不一致,因此定义别名 // 自定义的包 "github.com/woqutech/phoenix/discovery" )
(3)错误处理
接收到error后使用log记录或者返回
尽量在程序中使用if err != nil 而不是 if err == nil,避免程序嵌套太深
函数尽量都设计两个返回值,一个数据和一个error
// 一般写法
func CheckUser() (bool, error) {
...
if err == nil {
...
if err == nil {
...
} else {
...
}
} else {
...
}
}
// 推荐写法
func CheckUser() (bool, error) {
...
if err != nil {
...
}
...
if err != nil{
...
}
...
}(4)资源管理
能够使用defer的尽量使用defer管理,这样的操作通常成对出现
lock——unlock
open——close
defer就算在程序panic的时候依然会执行
不要在for循环中使用defer
// 一般写法
func DoSomething() {
...
lock := resource.Lock()
...
...
lock.UnLock()
}
// 推荐写法
func DoSomething() {
...
lock := resource.Lock()
defer lock.UnLock()
...
...
}(5)表格驱动测试
为每一个功能性函数、结构体等创建测试
测试case需要尽可能包括多种情况
多种情况采用[]保存,range使用,不要一项一项罗列
// 一般写法
host, port, err := net.SplitHostPort("192.0.2.0:8000")
require.NoError(t, err)
assert.Equal(t, "192.0.2.0", host)
assert.Equal(t, "8000", port)
host, port, err = net.SplitHostPort("192.0.2.0:http")
require.NoError(t, err)
assert.Equal(t, "192.0.2.0", host)
assert.Equal(t, "http", port)
host, port, err = net.SplitHostPort(":8000")
require.NoError(t, err)
assert.Equal(t, "", host)
assert.Equal(t, "8000", port)
host, port, err = net.SplitHostPort("1:8")
require.NoError(t, err)
assert.Equal(t, "1", host)
assert.Equal(t, "8", port)
// 推荐写法
tests := []struct{
give string
wantHost string
wantPort string
}{
{
give: "192.0.2.0:8000",
wantHost: "192.0.2.0",
wantPort: "8000",
},
{
give: "192.0.2.0:http",
wantHost: "192.0.2.0",
wantPort: "http",
},
{
give: ":8000",
wantHost: "",
wantPort: "8000",
},
{
give: "1:8",
wantHost: "1",
wantPort: "8",
},
}
for _, tt := range tests {
t.Run(tt.give, func(t *testing.T) {
host, port, err := net.SplitHostPort(tt.give)
require.NoError(t, err)
assert.Equal(t, tt.wantHost, host)
assert.Equal(t, tt.wantPort, port)
})
}(6)channel空间大小
对于可能出现阻塞的地方需要为chan增加缓冲
竞态条件
通道边界
缓冲的size要么为1,要么无缓冲
// 一般写法 // 缓冲区太大 c := make(chan int, 64) // 推荐写法 // 大小:1 c := make(chan int, 1) // 或者 // 无缓冲 channel,大小为 0 c := make(chan int)
(7)指定容器容量
使用make来初始化map和slice
尽可能在初始化阶段指定容器长度,减少容器扩容带来的开销
// 一般写法
m := make(map[string]os.FileInfo)
files, _ := ioutil.ReadDir("./files")
for _, f := range files {
m[f.Name()] = f
}
// 推荐写法
files, _ := ioutil.ReadDir("./files")
m := make(map[string]os.FileInfo, len(files))
for _, f := range files {
m[f.Name()] = f
}(8)尽量避免goto语句
尽量避免使用goto语句将程序逻辑变得复杂
(9)尽量少使用init方法
避免隐式逻辑
如果有一些巧妙的设计,则不排除使用init

四、常用工具
(0)gofmt
(1)goimport
(2)go vet
五、检查工具集成
~待补充
版权声明 本文属于本站 原创作品,文章版权归本站及作者所有,请尊重作者的创作成果,转载、引用自觉附上本文永久地址: http://blog.lujianxin.com/x/art/mxp0cly2ksku
上一篇:git仓库的大文件处理
下一篇:Golang编码规范
文章评论区
作者名片
- 作者昵称:Jeyrce.Lu
- 原创文章:61篇
- 转载文章:3篇
- 加入本站:2386天
作者其他文章
站长推荐
友情链接
站点信息
- 运行天数:2387天
- 累计访问:164169人次
- 今日访问:0人次
- 原创文章:69篇
- 转载文章:4篇
- 微信公众号:第一时间获取更新信息
