文章详情页 您现在的位置是:网站首页>文章详情
Golang编码规范
Jeyrce.Lu 发表于:2021年6月19日 16:45 分类:【Go】 1983次阅读
本文主要结合 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篇
- 加入本站:2004天
作者其他文章
站长推荐
友情链接
站点信息
- 运行天数:2005天
- 累计访问:164169人次
- 今日访问:0人次
- 原创文章:69篇
- 转载文章:4篇
- 微信公众号:第一时间获取更新信息