Go-高质量编程简介&编码规范| 青训营笔记

105 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

高质量编程简介及编码规范

目录

  1. 高质量编程
  • 高质量编程简介
  • 编码规范
  • 性能优化建议
  1. 性能调优实战
  • 性能调优简介
  • 性能分析工具pprof实战
  • 性能调优案例

1. 高质量编程

1.1 简介

什么是高质量的代码?

  • 各种边界条件是否考虑完备
  • 异常情况处理,稳定性保证
  • 易读易维护 编程原则
    实际应用场景千变万化,各种语言的特性和语法各不相同。
    但是高质量编程遵循的原则是相通的。
  1. 简单性
  • 消除“多余的复杂性”,以简单清晰的逻辑写代码。
  • 不理解的代码无法进行修复改进。
  1. 可读性
  • 代码是给人看的,而不是机器。
  • 编写可维护代码的第一步是确保代码可读。
  1. 生产力
  • 团队整体工作效率非常重要。

编码规范

如何编写高质量的Go代码

代码格式
  • 使用 gofmt 自动格式化代码,保证所有的 Go 代码与官方推荐格式保持一致。
  • 提升可读性,风格一致的代码更容易维护、需要更少的学习成本、团队合作成本,同时可以降低 Review 成本。
  • 使用goimports自动格式化代码,并进行依赖包管理:自动增删依赖的包引用、将依赖包按字母序排序并分类。
注释
  • 注释应该解释代码作用 公共符号始终要注释
  • 包中声明的每个公共的符号:变量、常量、函数以及结构体都需要添加注释。
  • 任何既不明显也不简短的公共功能必须予以注释。
  • 无论长度或复杂程度如何,对库中的任何函数都必须进行注释。
  • 有一个例外,不需要注释实现接口的方法。具体不要像下面这样做:
// Read implements the is.Reader interface
func (r *FileReader) Read(buf []byte) (int, error)
  • 注释应该解释代码如何做的
  • 注释应该解释代码实现的原因
    • 适合解释代码的外部因素
    • 提供额外上下文
  • 注释应该解释代码什么情况会出错
    • 适合解释代码的限制条件 包中声明的每个公共的符号:变量、常量、函数以及结构都需要添加注释 代码是最好的注释 注释应该提供代码未表达出的上下文信息
命名规范
  • 简介胜于冗长
  • 缩略词全大写,但当其位于变量开头不需要导出时,使用全小写
    • 例如使用ServerHTTP而不是ServeHttp
    • 使用XMLHTTPRequest而不是xmlHTTPRequest
  • 变量距离其被使用的地方越远,则需要携带越多的上下文信息
    • 全局变量在其名字中需要更多的上下文信息,使得在不同地方可以轻易辨认出其含义
  • package
    • 只由小写字母组成。不包含大写字母和下划线等字符。
    • 简短并包含一定的上下文信息。例如schema、task等。
    • 不要与标准库同名。例如不要使用sync或者strings。
  • 以下规则尽量满足,以标准库包名为例。
    • 不使用常用变量名作为包名。例如使用bufio而不是buf。
    • 使用单数而不是负数。例如使用encoding而不是encodings。
    • 谨慎地使用缩写。例如使用fmt在不破坏上下文的情况下比format更加简短。
  • 核心目标是降低阅读理解代码的成本。
  • 重点考虑上下文信息,设计简洁清晰的名称。
控制流程
  • 避免嵌套,保持正常流程清晰。
  • 尽量保持正常代码路径为最小缩进。
    • 优先处理错误情况/特殊情况,尽早返回或继续循环来减少嵌套。
    • 最常见的正常流程的路径被嵌套在两个 if 条件内。
    • 成功的退出条件是 return nil,必须仔细匹配大括号来发现。
    • 函数最后一行返回一个错误,需要追溯到匹配的左括号,才能了解何时会触发错误。
    • 如果后续正常流程需要增加一步操作,调用新的函数,则又会增加一层嵌套。
错误和异常处理
  • 简单错误
    • 简单的错误指的是仅出现一次的错误,且在其他地方不需要捕获该错误。
    • 优先使用 errors.New 来创建匿名变量来直接表示简单错误。
    • 如果有格式化的需求,使用fmt.Errorf。
  • 错误的Wrap和Unwrap
    • 错误的Wrap实际上是提供了一个error嵌套另一个error的能力,从而生成一个error的跟踪链。
    • 在fmt.Errorf中使用:%w关键字来将一个错误关联至错误链中。
  • 错误判定
    • 判定一个错误是否为特定错误,使用errors.Is。
    • 不同于使用 ==,使用该方法可以判定错误链上的所有错误是否含有特定的错误。
data, err = lockedfile.Read(targ)
if errors.Is(err, fs.ErrNotExist) {
    return []byte{}, nil
}
return data, err
  • panic
    • 不建议在业务代码中使用panic。
    • 调用函数不包含recover会造成程序崩溃。
    • 若问题可以被屏蔽或解决,建议使用error代替panic。
    • 在程序启动阶段发生不可逆转的错误时,可以在init或main函数中使用panic。
  • recover
    • recover只能在被defer的函数中使用。
    • 嵌套无法生效。
    • 只能在当前的goroutine生效。
    • defer的语句时后进先出。
    • 如果需要更多的上下文信息,可以recover后在log中记录当前的调用栈。

1.2 编码规范

  • 使用驼峰命名法来命名变量和函数
  • 文件名应该是小写字母,单词之间用下划线连接
  • 每个文件应该只定义一个包
  • 使用 gofmt 来格式化代码
  • 使用 goimports 来维护导入路径
  • 使用 golint 来检查代码质量
  • 使用 go test 来编写单元测试
  • 使用注释来解释代码的目的和用途
  • 不要在包级别的变量和函数上使用缩写
  • 使用 const 关键字来定义常量
  • 使用大写字母来定义公开的变量和函数