高质量编程
1.编程简介
高质量:正确可靠,简洁清晰的代码
边界条件完备性
异常情况处理,是否稳定
可维护性
2.编码规范
编程原则
- 简单性(无冗余代码)
- 可读性(便于维护和交流)
- 生产力(团队工作效率)
代码格式
工具:
- gofmt官方工具,自动格式化Go语言代码位官方统一风格
- goimports实际等于gofmt加上依赖包管理,自动增删依赖的包引用,将依赖包按字母排序并分类
注释:
-
解释代码作用
-
解释代码如何做的
-
解释代码实现的原因
-
解释代码在什么情况会报错
小结
代码是最好的注释
注释应该提供代码未表达出的上下文信息
命名规范
变量:
- 尽量简洁
- 缩略词全大写,但在变量开头且不需要到出时,全小写 如:用 ServeHTTP 而不用 ServeHttp,用 XMLHTTPRequest 或 xmlHTTPRequest
函数名:
- 尽量简短
- 函数名不携带包名的上下文信息,
- 名为foo的包某个函数返回类型Foo,可省略类型信息而防止产生歧义
- 名为foo的包某个函数返回类型T (T≠Foo),可在函数名中加入类型信息
包(package):
- 只由小写字母组成,不包含大写字母和下划线等字符
- 简短且包含一定的上下文信息
- 不要和标准库同名,防止出错
小结:
- 核心是降低阅读代码的成本
- 结合上下文代码进行参考,来命名
控制流程
- 避免分支嵌套,删除多余的else
- 尽量保持代码路径为最小缩进(观赏性,可阅读性高)
小结
- 线性原理,处理逻辑尽量直线,减少复杂的嵌套
- 可维护性和可读性
- bug大多出现在复杂的条件和循环语句中(找bug可侧重在逻辑语句中找)
错误和异常处理
-
简单错误
errors.New // 创建匿名变量表示简单错误 fmt.Errorf // 格式化输出 -
复杂错误
嵌套链,可在
fmt.Errorf中使用%w关键字将错误进行关联到错误链中errors.Is:判定错误是否为特定错误errors.As:在错误链上获取特定种类的错误 -
panic -
调用函数不包括
recover会造成程序崩溃 -
如果问题可以被屏蔽或解决,建议使用
error代替panic -
程序启动阶段发生不可逆转的错误时,可以在
init或main中使用panic -
recover- 只能在
defer中使用 - 嵌套中无法生效
- 只在当前的
goroutine生效 defer的语句是后进先出(队列)- 在
log中记录信息
- 只能在
小结:
error:尽可能的提供简明的上下文信息链,方便定位问题panic:用于真正的异常recover:只在当前goroutine的defer函数中生效
3.性能优化建议
评估工具:benchmark支持基准性能测试
go test -bench=. -benchmem
使用slice预分配内存,尽可能使用make()初始化切片时提供容量信息
切片:
- 切片本质是数组片段
- 包括数组指针
- 片段的长度
- 片段的容量
- 切片操作并不复制切片指向的元素
- 创建一个新的切片会复用原来切片的底层数组
大内存未释放:
- 在已有切片基础上创建切片,不会创建新的底层数组
- 例
- 原切片较大,代码在原切片基础上新建小切片
- 原底层数组在内存中有引用,得不到释放
- 可用
copy代替re-slice
map预分配内存
- 不断向
map中添加元素会触发map的扩容 - 提前分配好空间可以减少内存拷贝和
Rehash的消耗 - 可根据实际需求提前预估好需要的空间
字符串处理:
- 字符串在
Go语言中是不可变类型,占用内存大小是固定的 - 使用
+每次都会重新分配内存 strings.Builder,bytes.buffer底层都是[]byte数组- 内存扩容策略,不需要每次拼接都重新分配内存
空间结构体:
- 空结构体
struct{}实例不占据任何的内存空间 - 可作为各场景下的占位符使用
- 节省资源
- 空结构体本身具备很强的语义,可以不赋值,仅作为占位符
使用atomic包
小结:
- 避免常见的性能陷阱可保证大部分的程序性能
- 不要一味的追求程序的性能
- 越高级的性能优化手段越容易出现问题
- 要在正确可靠,简洁清晰的前提下去提升程序性能