Reviewbot 开源 | 这些写 Go 代码的小技巧,你都知道吗?
Reviewbot 是七牛云开源的一个项目,旨在提供一个自托管的代码审查服务, 方便做 code review/静态检查, 以及自定义工程规范的落地。
自从上了 Reviewbot 之后,我发现有些 lint 错误,还是很容易出现的。比如
dao/files_dao.go:119:2: `if state.Valid() || !start.IsZero() || !end.IsZero()` has complex nested blocks (complexity: 6) (nestif)
cognitive complexity 33 of func (*GitlabProvider).Report is high (> 30) (gocognit)
这两个检查,都是圈复杂度相关。
圈复杂度(Cyclomatic complexity)是由 Thomas McCabe 提出的一种度量代码复杂性的指标,用于计算程序中线性独立路径的数量。它通过统计程序控制流中的判定节点(如 if、for、while、switch、&&、|| 等)来计算。圈复杂度越高,表示代码路径越多,测试和维护的难度也就越大。
圈复杂度高的代码,往往意味着代码的可读性和可维护性差,非常容易出 bug。
为什么这么说呢?其实就跟人脑处理信息一样,一件事情弯弯曲曲十八绕,当然容易让人晕。
所以从工程实践角度,我们希望代码的圈复杂度不能太高,毕竟绝大部分代码不是一次性的,是需要人来维护的。
那该怎么做呢?
这里我首先推荐一个简单有效的方法:Early return。
Early return - 逻辑展平,减少嵌套
Early return, 也就是提前返回,是我个人认为最简单,日常很多新手同学容易忽视的方法。
举个例子:
func validate(data *Data) error {
if data != nil {
if data.Field != "" {
if checkField(data.Field) {
return nil
}
}
}
return errors.New("invalid data")
}
这段代码的逻辑应该挺简单的,但嵌套层级有点多,如果以后再复杂一点,就容易出错。
这种情况就可以使用 early return 模式改写,把这个嵌套展平:
func validate(data *Data) error {
if data == nil {
return errors.New("data is nil")
}
if data.Field == "" {
return errors.New("field is empty")
}
if !checkField(data.Field) {
return errors.New("field validation failed")
}
return nil
}
是不是清晰很多,看着舒服多了?
记住这里的诀窍:如果你觉得顺向思维写出的代码有点绕,且嵌套过多的话,就可以考虑使用 early return 来反向展平。
当然,严格意义上讲,early return 只能算是一种小技巧。要想写出高质量的代码,最重要的还是理解 分层、组合、单一职责、高内聚低耦合、SOLID 原则等 这些核心设计理念 和 设计模式了。