高质量编程和代码规范 | 青训营笔记

127 阅读4分钟

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

代码规范

代码格式化工具

gofmt官方自带的格式化工具

goimports在gofmt基础上增加了对依赖包的管理

注释的作用

  • 解释代码作用
  • 解释代码是如何实现的
  • 解释代码为什么要实现
  • 解释代码出错的原因

公共符号始终需要注释,注释应当提供代码没有的信息,代码本身需要有自解释性。

命名规范

  • var 变量名:

    • 缩略词全大写,位于变量开头且不需要导出的时候使用全小写,例如:xmlHTTPRequest,ServeHTTP
    • 变量距离使用的距离越远,变量的名字所带的信息就应该越多
  • func 函数:

    • 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现
    • 函数名尽量简短
    • 当名为foo的包返回某个类型Foo时,可以省略类型信息
    • 名为foo的包返回类型T时,可以在函数名中添加类型信息
  • package 包:

    • 只有小写字母组成,不包含大写字母和下划线
    • 短小精悍
    • 不和标准库同名
    • 不使用常用变量名
    • 用单数而不是复数
    • 谨慎使用缩写

控制流程

  • 避免嵌套
  • 尽量保证正常代码缩进为最小缩进

错误和异常处理

  • 简单错误

    • 使用errors.New创建匿名变量来优先处理错误
    • 如果有格式化需求,使用fmt.Errorf输出错误信息
  • 错误的wrap和unwrap

    • 错误可以行成一个链,使用fmt.Errorf,里面%w可以指定连接到哪一个错误上
    • errors.unwrap可以把错误从错误链上取下来
  • 错误判定

    • errors.is不用于使用==,可以判定错误在错误链上是否存在该错误

    image.png

    • 在错误链上获取特定类型的错误使用errors.As

    image.png

  • panic

    • 不建议在业务代码中使用panic
    • 调用函数不包括recover会导致程序崩溃
    • 如果问题可以被解决建议使用error代替panic
    • 特例:当程序启动阶段发生不可逆转的错误,可以在init或者main函数中panic
  • recover

    • 只能在被defer的函数中使用
    • 嵌套无法生效
    • 只在当前goroutine生效
    • def语句后进先出

性能测试

  • benchmark
    相关数据的含义:

image.png

slice优化

//数据结构
type slice struct{
    array unsafe.Pointer//数组指针
    len int//长度
    cap int//容量
}

切片尾接示意图:

image.png

  • 通过切片截断,创建新的切片会复用之前使用的切片,内存不会释放
  • 切片操作并不复制切片指向的元素
  • 大切片新建小切片,内存未释放,示例:

image.png

测试结果如下:

image.png

因为创建的新切片会复用之前的切片,所以直接用切片操作内存不会释放,使用copy会释放之前的切片内存,内存占用减少

map优化

  • 预分配空间,减少内存拷贝和rehash消耗的时间

字符串优化

  • 三种字符串拼接方式:+,string.builder,bytes.buffer.最快的是string.builder,内存占用最少的是bytes.buffer.性能最差的是+

    下图第一行是+,第二行是string.builder,第三行是bytes.buffer

    image.png

分析:

  • 字符串在GO语言中是不可变类型,每次占用的内存大小是固定的
  • 使用+会重新分配内存
  • string.builder和bytes.buffer底层是[]bytes类型,采用内存扩容的策略,并不一定每次拼接都需要扩容操作.

builder 和 buffer的区别:

image.png

image.png

builder.string()直接将底层的[]byte类型转换成字符串类型返回,buffer.string()创建了一个新的内存空间存放字符串,分配了一次空间

使用builder.grow()方法可以提前分配[]byte的大小,buffer.grow()类似,提前分配可以减少运行时间,分配次数和内存消耗.

buffer.grow(): image.png builder.grow(): image.png buffer比builder多分配一次,因为buffer.string()分配了新的内存空间存放字符串: image.png

空结构体

空结构体不占任何空间,可以当作占位符

atomic包

  • 原子操作一个变量,可以使用atomic包,不用自己写一个mutex结构体加锁解锁;atomic包性能更好

  • 如果需要保护一段逻辑代码,可以使用sync.mutex.locksync.mutex.unlock;sync.mutex的加锁解锁是操作系统的系统调用,atomic操作是通过硬件实现的

  • 对于非数值操作,可以使用atomic.value,可以承载一个interface{}