1.基本类型偏执
有时候你会看到某个类里有很多基本类型字段,但是你隐约地能区分出某些字段好像应该在一起,如手机号和带区号的电话号码、描述某个日期范围的开始日期和结束日期,它们应该有一个真实的载体(如联系方式类和时间范围类),而不应零散放在一个大类型中。
通用场景
- 复杂的执行/错误信息,需要定义结构体保存。
- 出现状态/类型等字眼时,需要使用枚举。
- 时间类型尽量使用内置定义,如 time.Second,不要使用 int。
结构体
- 一个文件中出现多个结构体时,需要注意观察是否有重复的成员。
- 一个结构体中,成员较多,且多个成员有明显关联关系,需要封装新的结构体。
- 意义不明的成员变量,应该定义类型描述作用。
2.单一职责
包&文件
- 需要判断当前文件是否应该归属于当前包,主要以职责进行判断。
- 导出的函数/变量的职责必须与包&文件职责高度一致。
- 除了包的主逻辑文件中内容允许导出,包内的辅助函数都应该是非导出的。
函数
- 一个函数只负责一个职责。
- 配置文件的读取,和对象初始化应该分开,不要让对象自己根据配置文件初始化,保证构造函数足够简单
- 解析、校验、计算的逻辑应该进行分离
- 读、写、计算的逻辑应该进行分离
- rpc、db 相关操作需要独立封装
- 一个函数内不应该混杂多个实现细节,需要将独立的逻辑封装成函数。
- 一次循环尽量只做一件事,不用担心多次循环。
- 同一层级的逻辑细节不要拆分。
3.goroutine
- 启动的 goroutine 最好有 recover。
因为其他 goroutine 是无法捕当前 goroutine 抛出的异常。如果启动的 goroutine 没有 recover,很容易发生 panic 导致整个进程退出。
- 遇到 goroutine一定要梳理清楚 goroutine 的退出机制,防止泄漏。
- 如果要开启多个线程执行一组动作,并且要等待全部完成后继续后续逻辑,考虑使用 errgroup.Group。
4.应用服务
- 应用服务建议有 README.md 说明文档,介绍服务功能、使用方法、部署时的限制与要求、基础环境依赖等
- 应用服务必须要有接口测试
5.常用工具
Go 本身在代码规范方面做了很多努力,很多限制都是语法要求,例如左大括号不换行,引用的包或者定义的变量不使用会报错。此外 Go 还是提供了很多好用的工具帮助我们进行代码的规范。
- gofmt ,大部分的格式问题可以通过 gofmt 解决, gofmt 自动格式化代码,保证所有的 go 代码与官方推荐的格式保持一致,于是所有格式有关问题,都以 gofmt 的结果为准。
- goimports ,此工具在 gofmt 的基础上增加了自动删除和引入包。
- go vet ,vet 工具可以帮我们静态分析我们的源码存在的各种问题,例如多余的代码,提前 return 的逻辑, struct 的 tag 是否符合标准等。编译前先执行代码静态分析。
- golint ,类似 javascript 中的 jslint 的工具,主要功能就是检测代码中不规范的地方。