字节青训营笔记---day3 (1)

77 阅读3分钟

课程目录

  1. 高质量编程
  2. 性能调优实战

高质量编程

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

需要考虑

  • 边界条件问题
  • 异常情况处理问题
  • 易读易维护

编程原则

  1. 简单性:消除多余的复杂性,否则难以维护和改进

  2. 可读性:详细注释+清晰逻辑

  3. 生产力:团队合作的高效

编码规范

  1. 代码格式 (gofmt 格式化代码 ; goimports 格式化代码 + 依赖管理)

  2. 注释

  • 解释代码作用
  • 解释代码如何实现的
  • 解释代码实现的原因
  • 解释代码的特殊情况

Good code has lots of comments, bad code requires lots of comments

代码是最好的注释, 逻辑清晰的代码本身对阅读者来说就是一种注释

注释应该提供代码中未提到的上下文信息

  1. 包中声明的每个公共的符号:变量、常量、函数以及结构都需要添加注释
  2. 任何既不明显也不简短的公共功能必须予以注释
  3. 无论长度或复杂程度如何,对库中的任何函数都必须进行注释
  4. 不需要注释实现接口的方法,因为对理解代码没什么帮助(follow)
// read implements the io.Reader interface
func (r *FileReader) Read(buf []byte) (int, err)
  1. 命名规范
  • 简洁胜于冗长

  • 缩略词全大写(ServeHTTP), 但当其位于变量开头并且不需要导出时,使用全小写(xmlHTTPRequest)

  • 变量距离其被使用的地方越远,则需要携带越多的上下文信息 (全局变量在其名字中需要更多的上下文信息, 使得在不同的地方可以轻易辨认出其含义)

  • function 命名规范

image.png

规范示例: 在 http 包中创建服务函数, 应该使用 Serve 而不是 ServeHTTP (和包中重复)

  • 包名命名规范

image.png

好的命名规范,可以降低阅读理解代码的成本,重点考虑上下文信息,设计简洁清晰的名称

  1. 控制流程

image.png

  1. 错误和异常处理

image.png

  • errors.Is 判断错误链中某种错误是否存在
data, err := lockedfile.Read(targ)
if errors.Is(err, fs.ErrNotExit) { ... }
  • errors.As 检索错误链中是否存在某种错误并进行相应处理
if _, err := os.Open("non-existing); err != nil {
      var pathError *fs.PathError
      if errors.As(err, &pathError) {
             fmt.Println("failed at path:", pathError.Path)
      } else {
           fmt.Println(err)
      }
}

image.png

  • panic 异常处理
func main() {
    // .. 
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    
    client, err := sarama.NewConsumerGroup(strings.Split(brokers, ","), group, config)
    
    if err != nil {
        log.Panicf("error creating consumer group: %v", err)
    }
    ...
    
}

// Panicf is equivalent to Printf() + panic()
func Panicf(format string, v ...interface{}) {
    s := fmt.Sprintf(format, v...)
    std.Output(2, s)
    
    panic(s)
}
  • recover 在 defer 函数中捕获 panic 异常,防止程序崩溃
func (s *ss) Token(skipSpace bool, f func(rune) bool) (tok []byte, err error) {
    defer func() {
        if e := recover(); e != nil {
            if se, ok := e.(scanError); ok {
                err = se.err
            } else {
                panic(e)
            }
        }
    }()
    // 在程序 panic 退出前,将 panic 捕获,使程序正常返回退出
    ...
}

func (t *treeFS) Open(name string) (f fs.File, err error) {
    defer func() {
        if e := recover(); e != nil {
            f = nil
            // 在 log 中记录当前调用栈
            err = fmt.Errorf("gitfs panic: %v\n%s, e, debug.Stack())
        }
    }()
    
    //....
}

image.png