1. 代码格式
【强制】采用4个空格缩进,每个 tab 也代表4个空格。
【强制】提交的代码必须经过 gofmt 格式化(配置IDE保存文件时自动格式化)。
【强制】代码最大行宽为120列,超过换行。
2. 命名规范
【强制】命名均不能以特殊字符开始和结束,包含常见的中划线、下划线等。
// 反例:
var _name, -name, _name, $name, %name string
【强制】命名统一采用英文,不能采用拼音等其他方式。
// 正例:
var name string
var address string
// 反例:
var mingzi string
var dizhi string
【强制】参数名、局部变量都统一使用小驼峰风格。
func demo(ctx context.Context, name string) {
var localVar string
// other operations
}
【强制】对于包内非导出的 struct 使用小驼峰风格,对于导出的 struct 采用大驼峰风格。
// 非导出的struct
type user struct {}
// 导出的struct
type User struct {}
【强制】常量命名使用大驼峰风格,不要使用下划线分隔。
// 正例:
var MaxConnectionCount int64
// 反例:
var MAX_CONNECTION_COUNT int64
【强制】包名统一采用小写风格,使用短命名,不能包含特殊字符(下划线、中划线等),建议最好是一个单词。文件夹名称必须与报名保持一致
// 正例:
package client
package clientset
// 反例:
package Demo
package Demo_Case
【强制】对于专有名词在使用时要保证全名统一为大写或小写,不能出现部分大写和小写混用的情况。
// 正例:
func setURL() {
var url string
var URL string
// other operations
}
// 反例:
func setUrl() {
var Url string
}
下面列举了一些常见的特有名词:
"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP","HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UI", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"
【强制】对于多个具有枚举特性的类型,要求定义为类型,并使用以下方式进行枚举。
type OrderStatus int32
var OrderStatusEnum = struct {
WaitPayment OrderStatus
PaySuccess OrderStatus
Cancel OrderStatus
Closed OrderStatus
Deleted OrderStatus
Completed OrderStatus
}{
WaitPayment: 1, // 待付款
PaySuccess: 2, // 已付款
Cancel: 3, // 已取消
Closed: 4, // 已关闭
Deleted: 5, // 已删除
Completed: 6, // 已完成
}
【强制】不允许任何未定义的常量直接在代码中使用。
【强制】
:=
或=
左右两侧必须加一个空格。
3. 控制语句
3.1. if
【强制】if 接受一个初始化语句,对于返回参数不需要流入到下一个语句时,通过建立局部变量的方式构建 if 判断语句。
// 正例:
if err := file.Chmod(0664); err != nil {
return err
}
// 反例:
err := file.Chmod(0664)
if err != nil {
return err
}
【强制】在 if 语句中对异常处理等情况,如果成功的控制流是继续往下走,而对于异常处理结束语 return 语句时,不能使用 else 语句。
// 正例:
f, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)
// 反例:
f, err := os.Open(name)
if err != nil {
return err
} else {
d, err := f.Stat()
if err != nil {
f.Close()
return err
} else {
codeUsing(f, d)
}
}
3.2. for
【强制】采用简短声明建立局部变量。
// 正例:
for i := 0; i < 5; i ++ {
codeUsing(i)
}
// 反例:
var i int
for i = 0; i < 5; i ++ {
codeUsing(i)
}
【强制】对于遍历数据(如 map )的场景,如果只使用第一项,则直接丢弃第二项。
// 正例:
for key := range mapper {
codeUsing(key)
}
// 反例:
for key, _ := range mapper {
codeUsing(key)
}
3.3. switch
【强制】在使用多个 case 时,多个 case 写在一起。
// 正例:
switch type {
case 1, 2:
return "a"
case 3:
return "b"
default:
return "c"
}
// 反例:
switch type {
case 1:
return "a"
case 2:
return "a"
case 3:
return "b"
default:
return "c"
}
【强制】必须添加default默认处理。
4. 函数
【强制】对于少量数据,不要通过指针传递。
【强制】对于大量(>=4)的入参,考虑使用 struct 进行封装,并通过指针传递。
【强制】传参是 map、slice、chan 不要使用指针进行传递,因为这三者是引用类型。
【建议】避免使用函数命名返回值。
// 正例:
func Foo(a, b int) (string, bool) {
var c string = "Hello World"
var ok bool = true
return c, ok
}
// 反例:
func Foo(a, b int) (c string, ok bool) {
c = "Hello World"
ok = true
return
}
【强制】context.Context 不能放在结构体中,且必须为函数的第一个参数。
5. 包引用
【建议】对 import 的包进行分组管理,用换行符分割,而且标准库作为分组的第一组。如果你的包引入了三种类型的包:标准库包、程序内部包、第三方包,建议采用如下方式进行组织你的包,goimports 会自动帮你格式化。
package main
import (
"fmt"
"os"
"kmg/a"
"kmg/b"
"code.google.com/a"
"github.com/b"
)
【强制】引用包使用绝对路径,不能使用相对路径。
// 正例
import "shorturl/model"
// 反例
import "./model"
【强制】引用包不能使用省略包名的方式。
// 正例:
import "fmt"
fmt.Println("Hello World")
// 反例
import . "fmt"
Println("Hello World")
【强制】包别名使用全小写的命名风格,尽量用容易理解的英文表示。
import (
orderpb "gitlab.dailyyoga.com.cn/h2/svr-ecommerce/gen/golang/order"
librarysvr "gitlab.dailyyoga.com.cn/h2/svr-ecommerce/library"
)
6. 结构体
6.1. 定义
【建议】结构体字段加上注释,这样可读性好。
【建议】结构体初始化格式采用多行,定义如下:
// 正例:
u := User{
Username: "test",
Email: "test@gmail.com",
}
// 反例:
u := User{Username: "test", Email: "test@gmail.com"}
u := User{"test","test@gmail.com"}
6.2. 接受者
【建议】名称统一采用1~3个字母,不宜太长。
type User struct {
// 姓名
Name string `json:"name"`
// 邮箱
Email string `jdon:"email"`
}
func (u *User) GetName() string {
return u.Name
}
【建议】对于绝大多数可以使用指针接受者的场景,推荐使用指针接受者会更有效率。
【强制】如果接受者是 map、slice、chan 不能使用指针接受者。
【强制】如果接受者是包含有锁( sync.Mutex 等),必须使用指针接受者。
7. 接口
1)接口(interface)采用大驼峰风格命名,具体细分为以下三种情况:
【建议】单个函数的接口名以 er 作为后缀,如 Reader、Writer,而接口的实现则去掉 er。
type Reader interface {
Read(p []byte) (n int, err error)
}
【建议】两个函数的接口名为两个函数名结合。
type WriteFlusher interface {
Write([]byte) (int, error)
Flush() error
}
【建议】三个以上函数的接口名,类似于结构体名。
type Car interface {
Start([]byte)
Stop() error
Recover()
}
8. 注释
【强制】使用行间注释时,如果注释行与上一行不属于同一层,不用空行。如果属于同行,则空一行再进行注释。
func demo() {
// This is a start line of a new block, do not need a new line
// with the previous code.
say("knock, knock!")
// This is the same block with the previous code,
// you should insert a new line before you start a comment.
echo("who is there ?")
}
【强制】使用//进行注释时,和注释语句之间必须有一个空格,增加可读性。
// validator is used to validate dns's format.
// should not contains dot, underscore character, etc.
func validator(dns string) error {
// do validate.
}
【强制】不要使用尾注释。
// 反例:
func show(name string) {
display(name) // show a man's information
}
【建议】注释的单行长度最大不能超过120列,超过必须换行,一般以80列换行为宜。
【建议】函数与方法的注释需要以函数或方法的名称作为开头。
// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
【强制】大段注释采用
/**/
风格进行注释。
9. 测试
【强制】单元测试文件名命名规范:example_test.go
【强制】测试用例的函数名称必须以 Test 开头,例如:
func TestExample(t *testing.T) {}
10. 异常
10.1. 异常处理
【强制】程序中出现的任何异常都必须处理,不能使用 "_" 忽略。
【强制】程序中尽量避免使用panic来进行异常处理,对于必须要使用panic进行异常处理的场景,要启用recover回收处理异常。
func openFile(fileName string) {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
_, err := os.Open(fileName)
if err != nil {
panic(err)
}
}
11. 工程规范
11.1. 数据库
【建议】数据库定义通过TableName方法指定表名,不推荐使用engine.Table()。
package admin
// User 管理员信息
type User struct {
ID int64 `xorm:"not null pk autoincr int(11) 'id'"`
Email string `xorm:"not null varchar(100) 'email'"`
Name string `xorm:"not null varchar(50) 'name'"`
Password string `xorm:"not null varchar(128) 'password'"`
GroupType library.AdminUserGroup `xorm:"not null tinyint(3) 'group_type'"`
CreateTime int64 `xorm:"not null int(11) 'create_time'"`
UpdateTime int64 `xorm:"not null int(11) 'update_time'"`
}
// TableName 表名
func (User) TableName() string {
return "admin_user"
}
反例:
package admin
type AdminUser struct {
ID int64 `xorm:"not null pk autoincr int(11) 'id'"`
Email string `xorm:"not null varchar(100) 'email'"`
Name string `xorm:"not null varchar(50) 'name'"`
Password string `xorm:"not null varchar(128) 'password'"`
GroupType library.AdminUserGroup `xorm:"not null tinyint(3) 'group_type'"`
CreateTime int64 `xorm:"not null int(11) 'create_time'"`
UpdateTime int64 `xorm:"not null int(11) 'update_time'"`
}
【强制】批量执行增删改时使用事务进行处理。
11.2. 微服务
【强制】proto文件定义请求和返回结果message格式为:PrefixRequest、PrefixResponse的大驼峰命名方式,不推荐使用其他格式。
message CountCartRequest {
int64 UID = 1;
}
message CountCartResponse {
comm.ResultCode ResultCode = 1;
int64 Cnt = 2;
}
【建议】protobuf文件和生成解析的相应代码放在不同的文件夹中,比如:proto文件放在protobuf文件夹中,生成解析的代码放在gen/golang文件夹中,rpc接口实现放在rpc文件夹中。
syntax = "proto3";
option go_package = "xxxx/svr-xxx/gen/golang/proto";
option php_namespace = "SvrXxx\\Proto";
option php_metadata_namespace = "SvrXxx\\GpbMetadata";
package proto;
拖地先生,从事互联网技术工作,在这里每周两篇文章,一起聊聊日常的技术点滴和管理心得。

如果对你有帮助,让大家也看看呗~




