这是我参与「第五届青训营 」伴学笔记创作活动的第 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操作硬件来实现,效率比锁高