基本元素设计
[规则5-1]struct规范
- *struct申明和初始化格式采用多行:起始申明占一行,每个成员占一行,结束}占一行; *如果 struct结构简单或者赋值字段1-2个,初始化可以使用一行。
正例:
type User struct {
Username string
Email string
ID int
BirthDay time.Time
Age int
Gender uint8
}
type Worker struct {
Name String
Age int
}
反例:
type User struct
{
Username string;Email string
Id int
BirthDay time.Time
Age int
Gender uint8
}
初始化示例:
正例:
// 多字段初始化
u := User{
Username: "test",
Email: "test@gmail.com",
}
// 单行初始化
u := User{Username: "test"}
w := Worker{"test", 19}
反例:
u := User{Username:"test",Id:121212,BirthDay:20190909,Age:12,Gender:0}
- 如果 struct 中的数据变量需要进行 json 序列化,则需要以大写字母开头,同时需要 json 重命名。
说明: 结构体中的变量以大写字母开头,可以保证 json.Marshal 的时候数据持久化正确。如果结构体中的变量以小写字母开头,则使得 json.Marshal 的时候忽略该字段,使得该字段的值丢失,从而 json.Unmarshal 的时候将该变量的值置为默认值。由于结构体中的变量以大写字母开头, json串中的字段 key 的字符串形式变成了以大写字母开始,这对于追求以 json 串全小写为美的我们来说,需要进行json重命名。
建议:对于序列化的json结构来说,字符串字段慎用[]byte。([]byte序列化会被base64,会引起跨语言解析失败)
正例:
type Position struct {
X int `json:"x"`
Y int `json:"y"`
Z int `json:"z"`
}
type Student struct {
Name string `json:"name"`
Sex string `json:"sex"`
Age int `json:"age"`
Posi Position `json:"position"`
}
反例:
type Position struct {
X int
Y int
Z int
}
type Student struct {
Name string
Sex string
Age int
Posi Position
}
[规则5-2]if
- 若 if语句不会执行到下一条语句时,即其执行体 以break、continue、goto或return结束时,else省略
正例:
if err := file.Chmod(0664); err != nil {
log.Print(err)
return err
}
f, err := os.Open(name)
if err != nil {
return err
}
codeUsing(f)
反例:
-
if err := file.Chmod(0664); err != nil { log.Print(err) return err } else { xxxx } if f, err := os.Open(name); err != nil{ return err } else { codeUsing(f) }对于布尔类型的变量,应直接进行真假判断
示例:
-
正例 if flag /* 表示flag为真 */ if !flag /* 表示flag为假 */ 反例 if flag == true if flag == falseif 语句的嵌套层数不要太多,不大于3,超过优化代码逻辑
[规则5-3]for
- for语句的嵌套层数不要太多,不大于3,超过优化代码逻辑
- 使用for循环时,优先使用range 关键字而不是显式下标递增控制,除非需要显示修改某下标元素内容
示例:
```
正例 for i := 0; i < len(array); i++ { if i & 1 == 0 { array[i] += 1 } } for i, v := range array { fmt.Printf("element %v of array is %v\n", i, v) } 反例 for i := 0; i < len(array); i++ { fmt.Printf("element %v of array is %v\n", i, array[i]) }
- 逻辑表达式已经具有 true 或 false 语义,应尽量简洁
**示例:**
- ```
正例
return i == 3
反例
if i == 3 {
return true
} else {
return false
}
```
单目运算符之间不用空格,双目运算符之间用空格隔开;下列运算符之间不需要空格
- 关系运算符:!
- 位运算符: ^, >>, <<,
- 其他:取地址运算符&, 指针运算符*
**示例:**
# ```
正例 x<<8 + y<<16 a & b (*a).Field 反例 a+b x << 8 + y << 16 a&b
```函数设计
### [规则6-1]**函数命名**
- 要短小精悍和名副其实,避免误导。一般以它" 做什么" 来命名,而不是以它" 怎么做" 来命名
### [规则6-2]函数结构
- 函数应该做一件事,做好这件事,只做这一件事
- ****函数要短小,包括空行和{},这里建议不超过80行,多了拆分多个函数
- 函数的缩进层次不应该超过3层
### [规则6-3]函数参数
- 函数参数个数应该限制,入参控制在5个内
- 建议:出参控制在2个内
- 对于简单结构,不要传递指针;对于大结构数据的struct可以考虑使用指针
- 传入参数是map,slice,chan不要传递指针,因为map,slice,chan是引用类型,不需要传递指针的指针
- 参数列表中若干个相邻的参数类型相同,在参数列表中省略前面变量的类型声明
- 当 channel 作为函数参数时,根据最小权限原则,使用单向 channel
**示例:**
func Parse(ch <-chan int)
func After(d Duration) <-chan Time
**建议:** 形参和返回值在定义时带上名称,使代码更清晰
**示例:**
# ```
正例 func nextInt(b []byte, pos int) (value, nextPos int) func ReadFull(r Reader, buf []byte) (n int, err error) 反例 func nextInt([]byte, int) (int, int) func ReadFull(Reader, []byte) (int, error)
```错误
### [规则7-1]使用规则
- 正确的使用error返回值,禁止将错误值返回(例如用-1表示EOF)和修改通过地址传入的实参
- 对于api中含有err返回值,禁止使用_忽略错误返回值
**示例:**
### ```
正例 data, err := json.Marshal(a) if err != nil { xxx } 反例 data, _ := json.Marshal(a)
```[规则7-2]错误设计
- 包内错误值统一分组定义,而不是零散的分布在各个角落
**正例:**
- ```
// file error objectvar (
ErrEof = errors.New("EOF")
ErrClosedPipe = errors.New("io: read/write on closed pipe")
ErrShortBuffer = errors.New("short buffer")
ErrShortWrite = errors.New("short write")
)
// port error objectvar (
ErrAddPortToOvsBriFailed = errors.New("add port to ovs bri failed")
ErrPortNotEnteredPromiscuousMod = errors.New("port not entered promiscuous mod")
...
)
```
失败的原因只有一个时,不使用error
- 当函数的error返回值始终是nil时,不使用error作为返回值
**正例:**
func (self *AgentContext) IsValidHostType(hostType string) bool {
return hostType == "virtual_machine" || hostType == "bare_metal"
}
**反例:**
- ```
func (self *AgentContext) CheckHostType(hostType string) error {
switch hostType {
case "virtual_machine":
return nil
case "bare_metal":
return nil
}
return ErrInvalidHostType
}
```
****error/bool应放在返回值类型列表的最后
**正例:**
- ```
resp, err := http.Get(url)
if err != nil {
return nil, err
}
value, ok := cache.Lookup(key)
if !ok {
// ...cache[key] does not exist…
}
```
每一层捕获错误,如无必要,无需逐层传递
- 错误处理巧用defer
**正例:**
func deferDemo() error {
err := createResource1()
if err != nil {
return ErrCreateResource1Failed
}
defer func() {
if err != nil {
destroyResource1()
}
}()
err = createResource2()
if err != nil {
return ErrCreateResource2Failed
}
defer func() {
if err != nil {
destroyResource2()
}
}()
err = createResource3()
if err != nil {
return ErrCreateResource3Failed
}
defer func() {
if err != nil {
destroyResource3()
}
}()
err = createResource4()
if err != nil {
return ErrCreateResourc4Failed
}
return nil
}
**反例:**
- ```
func deferDemo() error {
err := createResource1()
if err != nil {
return ErrCreateResource1Failed
}
err = createResource2()
if err != nil {
destroyResource1()
return ErrCreateResource2Failed
}
err = createResource3()
if err != nil {
destroyResource1()
destroyResource2()
return ErrCreateResource3Failed
}
err = createResource4()
if err != nil {
destroyResource1()
destroyResource2()
destroyResource3()
return ErrCreateResource4Failed
}
return nil
}
```
错误消息应以小写字母开头,不应以.结尾。为保持一致性,应对日志消息应用相同的逻辑。
示例:
### ```
正例 logger.Print("something went wrong") ErrReadFailed := errors.New("could not read file") 反例 logger.Print("Something went wrong.") ReadFailError := errors.New("Could not read file")
```[规则7-3]panic&recover
- 尽量不要使用panic,除非你知道你在做什么
- 对于每个协程调用栈,routine1->routine2->routine3,使用defer加入recover,除非你能确保每个goroutine能够正常终止
**示例:**
# ```
func server(workChan <-chan *Work) { for work := range workChan { go safelyDo(work) } } func safelyDo(work *Work) { defer func() { if err := recover(); err != nil { log.Println("work failed:", err) } }() do(work) }
```测试
### [规则8-1]测试设计
- 不要为了测试而对产品代码进行侵入性修改
**说明:** 禁止为了测试而在产品代码中增加条件分支或函数变量。
- 应当为每个线上生产代码补上单元测试,除非生产代码是很简洁通用的代码
- 测试应该是黑盒的。
**说明:** 避免根据代码编写测试。
# 考题
[golang编程规范考题](https://bytedance.feishu.cn/space/doc/doccnVgdmmLdEKImMLxJvJFHFPe)
# 参考:
https://github.com/golang/go/wiki/CodeReviewComments
https://golang.org/doc/effective_go.html
https://go-zh.org/doc/effective_go.html
https://go-zh.org/ref/spec
http://google.github.io/styleguide/
https://www.jianshu.com/p/4540bb8fc9b5
https://github.com/bahlo/go-styleguide
https://github.com/golang/go/tree/master/src