这是我参与「第五届青训营 」笔记创作活动的第3天
简介
高质量代码:正确可靠、简介清晰
· 各种边界条件是否考虑完备
· 异常情况处理,稳定性保证
· 易读易维护
简单性
消除多余的复杂性,以简单清晰的逻辑编写代码
不理解的代码无法修复改进
可读性
编写可维护代码的第一步是确保代码可读
生产力
团队整体工作效率非常重要
编码规范
代码格式
推荐使用gofmt自动格式化代码
Go语言官方提供的工具
注释
注释应该解释代码的作用
注释应该解释代码如何做的
注释应该解释代码实现的原因
注释应该解释代码上面情况会出错
Good code has lots of comments,bad code requires lots of comments.
公共符号始终要注释
注释应该提供代码未表达出的上下文信息
命名规范
核心:降低阅读理解代码的成本
变量
简洁胜于冗长
缩略词全大写
函数名
函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的
函数名尽量简短
// http包中创建服务的函数如何命名更好
func Serve(...) error
func ServeHTTp(...) error
// 第一种更好,因为在http包中,可以不携带http的信息
包名
由小写字母组成,不包含大写字母和下划线等字符
简短并包含一定的上下文信息
不与标准库同名,例如不用sync、strings
一下规则尽量满足:
不使用常用变量名作为包名,例如使用bufio,而不是 buf
使用单数而不是复数
谨慎使用缩写,例如使用fmt在不破坏上下文的情况下比format更加简短
控制流程
避免嵌套
例如两个分支都包含return,可以去掉冗余的else
尽量保持正常代码路径为最小缩进
优先处理错误/特殊情况,尽早返回或继续循环来减少嵌套
错误和异常处理
简单错误
仅出现一次,且在其他地方不需要捕获该错误
优先使用errors.New来创建匿名变量来直接表示简单报错
如有格式化需求,使用fmt.Errorf
return errors.New("stopped after 10 redirects")
错误的Wrap和Unwrap
错误的wrap实际上就是提供一个error嵌套另一个error的能力,从而生成一个error的跟踪链
在fmt.Errorf中使用:%w关键字来将一个错误关联至错误链中
return fmt.Errorf("reading srcfiles list: %w", err)
错误判断
判断错误是否为特定错误,使用errors.Is(is)
不同于==,使用==无法判断
if errors.Is(err, fs.ErrNotExist) {
...
}
存在某种错误使用errors.As
panic
不建议在业务代码中使用panic
调用函数不包含recover会造成程序崩溃
若问题可以被屏蔽或解决,建议使用error代替panic
recover
与panic对应
recover只能在被defer的函数中使用
嵌套无法生效
只在当前goroutine中生效
defer的语句是后进先出
评估性能
Go语言提供了支持基准性能测试的工具-Benchmark
slice 预分配内存
尽可能在使用make()初始化切片时提佛那个容量信息
切片本质是一个数组片段的描述
包含一个数组指针,长度和容量(不改变内存分配情况下的最大长度)
map预分配内存
不断向map中添加元素会触发map扩容
strings.Builder
拼接字符串使用+性能最差,bytes.Buffer和strings.Buffer差不多,但strings.Buffer更快
分析:
字符串在Go语言中是不可变类型,占用内存大小是固定的
使用+每次都会重新分配内存
strings.Bulider,bytes.Buffer底层都是[]byte数组
不需要内存拼接时都分配内存
空结构体
空结构体实际不占剧任何的内存空间
可以作为占位符使用,节省资源
m := make(map[int]struct{})
使用场景
实现set,用map来替代
因为set只需要用到map的键,不需要用到值
使用空结构体比bool更节省空间
atomic包
在维护公共变量时,使用atomic包比加锁的效率优秀
type atomicCounter struct {
i int32
}
func AtomicAddOne(c *atomicCounter) {
atomic.AddInt32(&c.i, 1)
}
// 加锁
type mutexCounter struct {
i int32
m sync.Mutex
}
func MutexAddOne(c *mutexCounter) {
c.m.Lock()
c.i++
c.m.Unlock()
}
锁的实现通过操作系统来实现,属于系统调用
atomic操作是通过硬件实现的,效率比锁高
sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量
性能调优
原则
要依靠数据而不是猜测
要定位最大瓶颈而不是细枝末节
不要过早优化
不要过度优化
性能分析工具pprof
在什么地方耗费多少cup和内存