这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记
高质量编程
- 各种边界条件是否考虑完备
- 异常情况处理,稳定性保证
- 易读易维护
- 高质量的编程需要注意以下原则:简单性、可读性、生产力
常见编码规范
代码格式
- 推荐使用
gofmt自动格式化代码 goimports工具:gofmt+依赖包管理
注释
-
解释代码作用:适合注释公共符号
-
// Open opens the named file for reading. If successful, methods on the returned file can be used for reading; // the associated file descriptor has mode O_RDONLY. // If there is an error, it will be of type *PathError. func Open(name string) (*File, error) { return OpenFile(name, O_RDONLY, 0) } - 包中声明的每个公共的符号:变量、常量、函数以及结构都需要添加注释
- 不需要注释实现接口的方法
-
-
解释代码如何做的:适合注释实现过程
-
// Add the Referer header from the most recent // request URL to the new one, if it's not https->http: if ref := refererForURL(reqs[len(reqs)-1].URL, req.URL); ref != "" { req.Header.Set("Referer", ref) }
-
-
解释代码实现原因:适合解释代码的外部因素;提供额外上下文
-
if ireq.GetBody == nil && ireq.outgoingLength() != 0 { // We had a request body, and 307/308 require // re-sending it, but GetBody is not defined. So just // return this response to the user instead of an // error, like we did in Go 1.7 and earlier. shouldRedirect = false }
-
-
解释代码什么情况下会出错:适合解释代码的限制条件
命名规范
-
变量
- 简洁
- 缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写
- 变量距离其被使用的地方越远,则需要携带越多的上下文信息
-
函数
- 函数名不携带包名的上下文信息
- 函数名尽量简短
- 当名为
foo的包某个函数返回类型Foo时,可以省略类型信息而不导致歧义,比如http.Serve而不是http.ServeHTTP - 当名为
foo的包某个函数返回类型T时,可以在函数名中加入类型信息
-
package包
- 只由小写字母组成。不包含大写字母和下划线等字符
- 简短并包含一定的上下文信息。例如 schema、task 等
- 不要与标准库同名。例如不要使用 sync 或者 strings
-
尽量满足
- 不使用常用变量名作为包名。例如使用
bufio而不是buf - 使用单数而不是复数。例如使用
encoding而不是encodings - 谨慎地使用缩写。例如使用
fmt比format更简洁
- 不使用常用变量名作为包名。例如使用
控制流程
- 避免嵌套,保持正常流程清晰
- 尽量保持正常代码路径为最小缩进:优先处理错误/特殊情况,尽早返回或继续循环来减少嵌套
错误与异常处理
-
简单错误:仅出现一次的错误,且在其它地方不需要捕获该错误
- 优先使用
errors.New来创建匿名变量直接表示简单错误 - 如果有格式化的需求,使用
fmt.Errorf -
func defaultCheckRedirect(req *Request, via []*Request) error { if len(via) >= 10 { return errors.New("stopped after 10 redirects") } return nil }
- 优先使用
-
错误的
Warp和Unwrap- 错误的
Wrap提供一个error嵌套另一个error的能力,从而生成一个error跟踪链 - 在
fmt.Errorf中使用%w关键字将一个错误关联至错误链中 -
list, _, err := c.GetBytes(cache.Subkey(a.actionID, "srcfiles")) if err != nil { return fmt.Errorf("reading srcfiles list: %w", err) }
- 错误的
-
错误判定
- 判定一个错误是否为特定错误,使用
errors.Is - 不同于
==,该方法可以判定错误链上是否包含该特定错误 - 在错误链上获取特定种类的错误,使用
errors.As(err, &pathError) //err错误链里获取pathError错误
- 判定一个错误是否为特定错误,使用
-
panic:不建议在业务代码中使用panic- 如果当前 goroutine 中所有 deferred 函数都不包含 recover 就会造成整个程序崩溃
- 当程序启动阶段发生不可逆转的错误时,可以在 init 或 main 函数中使用 panic
-
recover:处理panic,defer语句是后进先出 -
error尽可能提供简明的上下文信息链,方便定位问题 -
panic用于真正异常的情况 -
recover在当前的goroutine的被defer的函数中生效