[ 后端与 Go | 青训营笔记 ]
这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
课内:Go 高质量编程
高质量编程的定义和原则,分享了代码格式、注释、命名规范、控制流程、错误和异常处理五方面的常见编码规范
重点
- 高质量编程简介及编码规范;
- 性能优化指南;
- 性能优化分析工具。
细节
高质量编程
简介
编写的代码正确可靠、简洁清晰
- 各种边界条件;
- 异常情况处理,稳定性保证;
- 易读易维护。
原则
- 简单性:逻辑清晰,消除多余的复杂性;
- 可读性:编写可维护代码的第一步是确保代码可读;
- 生产力:团队整体的工作效率,风格统一。
编码规范
- 代码格式
- 注释
- 命名规范
- 控制流程
- 错误和异常处理
代码格式
- gofmt 自动格式化代码
注释 - 公共符号始终要注释
- 包中声明的每个公共的符号:变量、常量、函数和结构都需要注释;
- 任何不明显不简短的功能必须注释;
- 库中的任何函数都必须注释。
注释应该做到的:解释作用、制作过程、实现原因(提供额外的上下文)、说明什么情况下会出错。
命名规范
- variable
-
简洁胜于冗长;
-
缩略词全大写,但位于开头且不需要导出时使用全小写;
- 如 ServeHTTP 而不是 ServeHttp;
- 如 XMLHTTPRequest 或 xmlHTTPRequest;
-
变量距离被使用位置越远,越需要携带更多的上下文信息。
- function
- 函数名与包名成对出现;
- 函数名要简短;
- 在函数名中加入返回类型。
- package
- 只由小写字母组成,不包括下划线;
- 简短,包含一定上下文信息;
- 不要与标准库同名;
- 不使用采用变量名作为包名;
- 使用单数形式;
- 谨慎使用缩写。
控制流程
- 避免嵌套,保证正常流程清晰;
- 尽量保证正常代码路径为最小缩进;
- 优先处理错误、特殊情况,尽早返回或继续来减少嵌套;
- 小结
- 处理逻辑走直线,避免复杂嵌套分支;
- 正常代码流程沿屏幕向下移动;
- 提升可维护性和可读性;
- 故障大多出现在复杂的条件和循环语句中。
错误与异常处理
- 简单错误:仅出现一次的错误,且其他地方不需要捕捉该错误;
- 直接 errors.New 来创建匿名变量表示错误;
- 使用 fmt.Errof 来满足格式化需求.
- 错误的 Wrap 和 Unwrap
- 错误的 Wrap 实际上提供了一个 error 嵌套另一个 error 的能力,从而生成一个error 的跟踪链;
- 在 fmt.Errorf 中使用 %w 关键字将一个错误关联至错误链中。
- 错误判定
- 判断一个错误是否为特定错误,使用 errors.Is;
- 可以判断错误链在是否存在特定的错误;
- 获取特定错误,使用 errors.As;
性能优化建议
简介
- 性能优化的前提是正确可靠、简洁清晰;
- 性能优化是综合评估,时间效率与空间效率可能矛盾;
- 根据语言特性提出建议。
如何使用
- Go 语言提供了基准性能测试的 benchmark 工具
$ go test -bench=. -benchmem
slice 预内存分配
- 尽可能在使用 make() 初始化切片的时候提供容量信息;
func PreAlloc(size int) {
data := make([]int ,size)
for i := 1;i <= size;i++ {
data = append()
}
}
- 每次 slice 申请内存都会复用原有数组;
- 如果要重复使用切片的一部分,最好新建一个切片,使用 copy 复制所需内容。避免大内存未释放。
// 在使用返回的切片时,依然会使用到原有切片
func GetLastSlice(origin []int) []int {
return origin[len(origin) - 2 :]
}
// 创建新切片,不会使用到原有切片
func PreGetLateSlice(origin []int) []int {
var re = make([]int ,2)
copy(re ,origin[len])
}
map 预分配内存
- 与 slice 同理,减少内存拷贝和 Rehash 的消耗。
使用 strings.Builder
- 字符串操作使用 strings.Builder 效率更高;
func Add(n int ,s string) string {
var Sa = := ""
for i := 1;i <= n;i++ {
sa += s
}
return sa
}
func PreAdd(n int ,s string) string {
var builder strings.Builder
for i := 1;i <= n;i++ {
builder.WriteString(s)
}
return builder.String()
}
- Go 中 string 的大小是固定的,每次使用 + 号,都需要再次申请一个更大的 string 来容纳相加的双方;
- string.Builder 不需要额外申请空间。
使用空结构体
- 空结构体 struct {} 实例不占用内存空间;
func EmptyStruct(n int) {
var m = make(map[int]struct{}) // 空结构体
for i := 1;i <= n;i++ {
m[i] = struct{}{}
}
}
- 如上可以实现用 map 实现 set,只需要 key ,不需要 val;
atomic 包
性能调优
简介
性能调优原则
- 依靠数据而不是猜测;
- 定位最大瓶颈而不是细枝末节;
- 不要过早优化;
- 不要过度优化。
pprof工具使用
- 只有在程序运行的时候才能进入 debug;
程序正常运行时的 CPU 占用;
总结
了解到编程原则,了解部分工具了解,对于代码优化有一定的了解。