(四)高质量编程|青训营笔记

102 阅读3分钟

1 高质量编程

1.1 注释规范

核心就是要提供额外信息辅助理解

不需要注释实现接口的方法

思考:一般C++/python会提供输入输出,函数功能的说明,且注释比较结构化,Go的看起来像一段文字。

1.2 命名规范

核心就是能提供信息,且彼此不重复不冲突

  • 变量

· 缩略词:一般全大写,位于开头且不需要导出时全小写

· 距离使用的地方越远,携带的信息越多,如全局变量

· 函数参数最好能直接表示含义

· 误区

· 长命名的局部变量并不优于单字母

  • 函数

· 不需要携带包名的上下文信息,这样子每部分命名都能体现相应的信息

· 函数的返回类型与包名一致时,可以省略返回类型信息而不导致歧义;

不一致时,函数名需携带类型信息

示例:选Serve即可,调用时一般是http.Serve

· 只由小写字母组成,不包含大写字母和下划线

· 简短但包含信息

· 不与标准库同名

· 不使用常用变量名,如bufio而不是buf

· 不使用复数

1.3 控制流程

  • 避免嵌套,保证流程清晰 -> 应该理解为减少分支

如果两个分支都包含return,则可以去除冗余的else

if 条件{
  ...
  return 
}
//else{
return
  • 尽量保持正常路径代码为最小缩进 -> 理解为减少层次嵌套

优先处理错误异常情况,减少嵌套

1.4 错误和异常处理

1.4.1 简单错误

仅出现一次,在其他地方不需要捕获

一般创建:errors.New("msg")

需要格式化:fmt.Errorf

1.4.2 复杂错误(Go1.13引入)

  • 错误链:处理方式是wrap和unwrap

wrap:一个错误嵌套另一个错误,形成错误的跟踪链

fit.Errorf中使用:%w关键字将该错误加入到错误链

  • errors.Is

判定范围为整条链,不同于==只能判定单一错误

data, err = lockedfile.Read(targ)
if errors.Is(err, fs.ErrNotExist) {
  return []byte{}, nil
}
return data, err
  • errors.As

    获取特定种类的错误

    与Is的区别:取出特定错误的命令

    if _, err := os.Open("non-existing"); err != nil{
      var pathError *fs.pathError
      if errors.As(err, &pathError) {
        //here pathError with explicit error path
        fmt.Println("Failed at path:", pathError.Path)
      } else {
        fmt.Println(err)
      }
    }
    

1.4.3 panic和recover

  • panic

    • 不建议在业务代码中使用
    • 调用函数不包含recover会造成程序崩溃
    • 如果是可以被屏蔽或解决的问题,建议使用error代替
    • 建议使用时机:在程序启动阶段发生不可逆转错误,在maininit中使用panic
  • recover

    • 只能在被defer的函数中使用

    • 嵌套无法生效

    • 只在当前goroutine中生效

    • defer的执行是后进先出的机制

      协程遇到panic时,遍历本协程的defer链表,并执行defer。在执行defer过程中,遇到recover则停止panic,返回recover处继续往下执行。如果没有遇到recover,遍历完本协程的defer链表后,向stderr抛出panic信息。从执行顺序上来看,实际上是按照先进后出的顺序执行defer

    package main
    ​
    import "fmt"func main() {
      defer_call()
    }
    func defer_call() {
      defer func() {
        fmt.Println("one")
      }()
      defer func() {
        fmt.Println("two")
      }()
      defer func() {
        fmt.Println("three")
      }()
    ​
      panic("触发异常")
    }
    ​
    /*output is 
    three
    two
    one
    panic: 触发异常
    */
    
    func main() {
      if true {
        defer fmt.Println("1")
      } else {
        defer fmt.Println("2")
      }
      defer fmt.Println("3")
    }
    ​
    /*
    3
    1
    */
    

    需要更多的上下文信息,可以在recover后在log中记录当前的调用栈

    func (t *treeFS) Open(name string) (f fs.File, err Error) {
      defer func() {
        if e := recover(); e != nil {
          f = nil
          err = fmt.Errorf("gifts panic: %v\n%s", e, debug.Stack())
        }
      }()
      //...
    }
    

    总结recover生效范围:当前goroutine的被defer的函数中生效

1.4.5 小结

error简明扼要的提供错误信息,方便定位

panic用于真正异常的情况