这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
高质量编程
编码规范
代码格式工具
- gofmt:GO官方的工具,自动格式化代码
- goimports:等于gofmt + 依赖包管理,自动增删依赖包的引用,并按照字母顺序排序
注释
- 应解释:代码作用、代码是如何做的、实现原因、出错情况
- 注释应该提供代码未表达出来的上下文信息
命名规范
变量命名
- 缩略词全部大写,当其位于变量开头且不需要导出时,使用全小写
- ServerHTTP、
ServerHttp - XMLHTTPRequest或者xmlHTTPRequest
- ServerHTTP、
- 变量距离其被使用的地方越远,则需要携带越多的上下文信息
函数命名
- 尽可能简短
- 不需要携带包名的上下文信息
- 当名为foo的包某个函数返回类型为Foo时,可以省略类型信息而不导致歧义
- 当名为foo的包某个函数返回类型为T(T!=Foo)时,可以在函数名中加入该类型信息
包命名
- 不能与标准库重名
- 只能由小写字母构成
- 包含一定的上下文信息
- 使用单数而不是复数
- 不使用常用变量作为包名:bufio、buf
- 谨慎使用缩写:fmt
控制流程
- 避免嵌套
- 尽量保证正常路径位最小缩进
-
- 有限处理错误与特殊情况,尽早返回
错误处理
简单错误
不需要捕获的错误
- 优先使用
errors.New()来创建匿名变量直接表示简单错误 - 格式化需求则使用
fmt.Errorf
错误链
- 将错误嵌套起来,Wrap和Unwrap实现error的跟踪链
- 在fmt.Errorf中使用%w将一个错误关联到错误链里
(GO1.13 errors中新增了errors.Wrap、errors.Is、errors.As、errors.Unwrap)
错误判断
- 使用
errors.Is判断错误链上所有错误中是否含有特定的错误 - 不要使用
==
- 使用
errors.As获取错误链上某种特殊的错误,并取出他的内容
panic
- 业务代码中不建议使用
- 在程序启动阶段发生不可逆转错误时,可以在init()或者main()中使用
性能调优
Benchmarks
函数格式:func BenchmarkXxx(*testing.B)
func BenchmarkRandInt(b *testing.B) {
for i := 0; i < b.N; i++ {
rand.Int()
}
}
基准测试函数必须运行目标代码b.N次。在基准测试执行期间,b.N会被调整,直到基准测试函数持续足够长的时间,可以可靠地计时为止
go test -bench=BenchmarkRandInt(测试的函数名)
go test -bench=. -benchmem
性能优化建议
内存预分配
slice预分配内存:make初始化的时候指定大小 2. 减少slice扩容产生的拷贝- map一样也需要预分配
字符串处理:三种方式
- 直接使用
“+” - 使用:
string.Builder - 使用:
bytes.Buffer
func Plus(n int, str string) string {
s := ""
for i := 0; i < n; i++ {
s += str
}
return s
}
func StrBuilder(n int, str string) string {
var builder strings.Builder
for i := 0; i < n; i++ {
builder.WriteString(str)
}
return builder.String()
}
func ByteBuffer(n int, str string) string {
buf := new(bytes.Buffer)
for i := 0; i < n; i++ {
buf.WriteString(str)
} return buf.String()
}
- 性能:
strings.Buffer > bytes.Buffer >> + - 字符串时不可变类型,占用的内存大小是固定的
- 每次使用+都会重新给字符串分配内存,大小为两个字符串长度之和
strings.Buffer 、bytes.Buffer底层都是[]byte数组,所以有扩容策略,并不需要每次都重新分配内存bytes.Buffer转化位字符串时重新申请了一块内存空间strings.Buffer直接将底层的[]byte转化成了string返回
空结构体
空结构体不占用内存空间,故而可用其传递信号
atomic包
- 锁的实现是通过操作系统来实现的,属于系统调用
- atomic操作是硬件实现,效率比锁高
sync.Mutex通常是用来保护一段代码的逻辑,而不是保护一个变量- 对于非数值操作,可以使用
atomic.Value,能承载一个interface{}