1.readable
else处理和happypath
避免else return
bad example
func absoluteNumber(x int)int{
if x >= 0{
return x
}else{
return -x
}
}
good example
func absoluteNumber(x int)int{
if x >= 0{
return x
}
return -x
}
保持happypath的缩进
bad example
func aFunc() error{
err := doSometing()
if err == nil{
err := doAnotherThing()
if err == nil{
return nil // happy path
}
return err
}
return err
}
good example
func aFunc() error{
if err := doSometing();err != nil{
return err
}
if err := doAnotherThing(); err != nil{
return err
}
return nil
}
去除没有必要的 else return
bad example
var a int
if flag {
a = 1
}else {
a = -1
}
good example
a := -1
if flag {
a = 1
}
init()
init 函数是什么
golang 中提前定义好的一个函数,写在 init 函数内部的代码块会被最提前执行
执行顺序:
- 初始化所有的 import 包
- 初始所有当前包中的变量声明
- 执行 init()
使用 init()的问题
- 可读性变差: 不知道 init 函数有提前处理的逻辑,在查看代码的时候容易忽略这一块内容
- 导入一个包的时候,包中的 init 函数有可能会产生一些不可预料的副作用
bad example
func init(){
c = buildClient()
}
// 编写测试代码,或者在开发环境执行测试用例的时候会直接跑这部分代码,如果这部分代码没有对其它环境进行适配,就存在报错的风险
good example
func InitClient(){
c = buildClient()
}
// server.go
func main(){
xxx.InitClient()
}
bad example
type MyVar struct{...}
var defaultVar MyVar
func init(){
defaultVar = MyVar{...}
}
// 变量 defaultVar 不好 mock
good example
var defaultVar createVar()
func createVar() MyVar{
return MyVar{...}
}
使用 init() 注意
- 不要有很强的依赖关系
- 不要操作访问全局变量 or 环境变量
- 避免依赖 init()函数之间的执行顺序
- 不要在 init()函数中使用 I/O 操作
注释
go 语言有两种注释风格
- /* */进行注释
- 通过 // 注释
// Bad example
printInfo("foo", true, true)
// good example
printInfo("foo", true/* isLocal*/, true/* done */)
- 每个包中都应该有一个包注释
- 小包情况下: 在package foo的 foo.go 中进行注释
- 大包情况下: 在 doc.go 中进行注释
- 关于每个导出的定义
- 避免一些显然的注释
注释原则: 是 what 而不是 how
2. robust 可靠性
panic
产生 panic 的两种情况
- 程序运行时爆出来的不可以简单继续的一个uncover 的 error
- 一个developer error 如空指针
处理 panic 的方式
- 使用 defer 方法进行 recover
- 在方法开始的时候定义 defer
- 多个 defer 执行是按照声明的逆序进行执行的
error
优雅处理 error return err --> return fmt.Errorf("context: %w", err) 通过 errors.Is(err, io.EOF) 来确定是否是 error
3. efficient
pointer
一个存储地址的变量
使用 pointer 的场景 Good
- 方法修改 参数/receiver的时候
- 有大量的数据, 通过 pointer 来避免值传递
- 其它方法都使用 pointer [代码一致性] Bad
- 通过 pointer 指向一个 interface
- 方法没有设定他的 参数/receiver的时候
- 需要传递的数据量很小
deprecation
单元测试
- 给deprecation的方法添加单元测试
- 在deprecation的方法中调用新的代码
进行 mark