高质量编程|青训营笔记

57 阅读3分钟

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

今天的主要内容是如何编写更简洁清晰的代码,以及go常用的优化手段及工具

高质量代码

一、 简介
    什么是高质量?
       编写的代码能够达到正确可靠,简洁清晰的目标
    编程的原则
        简单性:消除多余的复杂性,以简单清晰的逻辑编写代码
        可读性:注意代码能够让他人轻松读懂
        生产力:团队的整体工作效率非常重要
二、编码规范
    1. 注释:公共符号始终要注释,但注释接口的方法可以不用
        1.1 代码格式:推荐gofmt自动格式化代码(goimports = gofmt + 依赖包)
        1.2 注释的作用
            -    注释应该解释代码作用
            -    注释应该解释代码如何做的
            -    注释应该解释代代码实现的原因
            -    注释应该解释代码什么情况会出错
    2. 命名规范
        2.1 变量命名简洁胜于冗长(for循环中,使用i效果远胜于index)
        2.2 函数名不应该携带包名的上下文信息,也不应该有歧义
        2.3 包名只由小写字母组成,简短且不与标准库冲突
    3. 控制流程
        3.1 避免嵌套,保持正常流程清晰            
            //bad
            if foo{
              return x
            }else{
              return nil
            }

            //good
            if foo{
              return x
            } 
            return nil
        3.2 尽量保持正常代码路径为最小缩进
            //bad
            func OneFunc() error{
                err := doSomething()
                if err != nil{
                    err := doAnotherThing()
                    if err == nil{
                        return nil
                    }
                    return err
                }
                return err
            }
            
            //good
            func OneFunc() error{
                if err := doSomething(); err != nil{
                    return err
                }
                if err := doAnothrtThing(); err != nil{
                    return err
                }
                return nil;
             }
        
    4. 错误和异常处理
        4.1 简单错误(仅出现一次,且在其他地方不需要捕获该错误)
            优先使用error.New 来创建匿名变量来直接表示简单错误,如果有格式化的需求,使用fmt.Errorf
        4.2 错误的Wrap 和 Unwrap
            错误的wrap实际上是提供了一个error嵌套另一个error的能力,从而生成一个error的跟踪链,在fmt.Errof中使用:%w 关键字来将一个错误关联至错误链中
                list,_,err:= c.GetBytes(cache.Subkey(a.actionID,"srcfiles"))
                if err != nil{
                    return fmt.Errof("reading srcfiles list: %w",err)
                }
        4.3 错误判断
            判断一个错误是否为特定错误,使用 errors.ls
                data,err = lockedfile.Read(targ)
                if errors.Is(err, fs.ErrNotExist){
                    return []byte{},nil
                }
                return data,err
        4.4 panic
            不建议在业务代码中使用panic,若问题可以被解决或屏蔽,建议使用error
        4.5 recover
            只能在被defer 的函数中使用,

性能优化指南

    性能优化的前提是满足正确可靠,简洁清晰等质量因素
    性能优化是综合评估,有时候时间效率和空间效率可能对立
    1. Benchmark
        go语言提供的性能测试工具
        输出结果包括:
            测试函数名-GOMAXPROCS
            执行次数
            每次执行花费时间
            每次执行申请多大的内存
            每次执行申请几次内存
     2. slice 预分配内存
         若创建切片时在已有切片,则不会创建新的底层数组,如果原切片较大,原底层数组在内存中存有引用,将会得不到释放,因此可以使用copy代替re-slice
     3. map 预分配内存
         不断向map中添加元素的操作会触发map的扩容,提前分配好空间就可以减少内存拷贝和Rehash的消耗。
     4. 字符串处理
         建议使用Stings.Builder,相较于“+”和bytes.Buffer性能较好
         buffer转化为字符串重新申请了一块空间
         Builder直接将底层[]byte转化成了字符串类型返回
     5. 空结构体
         实际不占据任何内存空间,可作为占位符使用
     6. atomic包
         atomic操作硬件来实现,效率比锁高