高质量编程 | 青训营笔记

64 阅读3分钟

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

内容源于青训营课堂视频以及一些go文档和自己的经验、理解,若有错误欢迎及时指出

1 简介

什么是高质量——编写的代码能达到正确可靠、简洁清晰的目标可称之为高质量代码

  • 边界条件是否考虑完备
  • 异常处理,保证稳定性
  • 易读易维护

编程原则

Dave Cheney

  • 简单性
    • 消除“多余的复杂性”,以简单清晰的逻辑编写代码
    • 难以理解的代码逻辑,排查问题时难以定位,不知道如何修复
  • 可读性
    • 代码是写给人看的,而不是机器
    • 编写可维护代码的第一步是确保代码可读
  • 生产力
    • 团队整体的工作效率非常重要

2 编码规范

  • 代码格式

    gofmt/goimports都是官方提供的工具

  • 注释

    • 解释代码作用:公共符号...
    • 解释代码如何做的:实现过程...
    • 解释代码实现的原因:解释代码的外部因素,提供额外的上下文信息...
    • 解释代码什么时候会出错:限制条件...

    公共符号始终要注释说明

  • 命名规范

    • 简洁胜于冗长
    • 1.1缩略词全大写,但位于变量开头且不需要导出时全小写
      • ServerHTTP而不是ServerHttp
      • XMLHTTPRequest或者xmlHTTPRequest
    • 1.2变量距离被使用的地方越远,则需要携带越多的信息
    • 2.1函数名不用携带包名的上下文信息,因为常常同时出现
      • http.Server而不是http.ServerHTTP
    • 3.1包名只由小写字母组成,不含大写字母和下划线等字符
    • 3.2简短并包含一定的上下文信息
    • 3.3不与标准库同名
  • 控制流程

    • 避免嵌套,保持清晰

    • 尽量保持正常代码路径为最小缩进

      • 优先处理错误/特殊情况,尽早返回或继续循环来减少嵌套

        将两层循环改写为线性的:

        if err := doSomething(); err != nil {
        	return err
        }
        if err := doAnotherThing(); err != nil {
        	return err
        }
        
    • 线性原则,处理逻辑尽量走直线,避免复杂的分支嵌套

  • 错误和异常处理

    • 简单错误

      • 指仅出现一次,且在其他地方不需要捕获
      • 优先使用errors.New()创建匿名变量直接表示
      • 格式化可使用fmt.Errorf
    • 错误的包装和解包装

      • 实际上是提供了error嵌套的能力,生成一个error跟踪链

      • fmt.Errorf中使用%w来将一个错误关联至一个错误链

        if err := doSomething(); err != nil {
        	return fmt.Errorf("test error warp: %w", err)
        }
        
    • 错误判定

      • 判定一个错误是否是特点错误,使用error.Is
        • 不同于使用==,使用该方法可以判定判定错误链上所有错误是否含有特定错误
      • 获取错误链上的特定种类错误,使用error.As
        • 与is的区别在于as会提取出调用链中指定类型的错误,并将错误赋值给变量,便于后续处理
      • panic
        • 业务代码中不建议使用
        • 调用函数不含recover会造成程序崩溃
        • 使用error代替panic
        • 当程序启动阶段发生不可逆转的错误时,可在init或main中使用panic
      • recover
        • 只能在被defer的函数中使用
        • 嵌套不生效
        • 只在当前gorouine中生效
        • defer语句是后进先出:形成了栈,后面的语句会依赖前面的资源,因此如果先前面的资源先释放了,后面的语句就没法执行了

3 性能优化建议

简介

  • 性能优化的前提是满足正确可靠、简洁清晰等质量因素
  • 性能优化是综合评估,时空效率可能对立
方式
  • Benchmark

    • 基准测试
  • slice

    • 预分配内存
    • 使用copy代替re-slice
  • map

    • 预分配内存
  • 字符串处理

    • 使用strings.Builder
    • 使用+性能最差
  • 空结构体

  • atomic包

    • 系统调用,硬件实现