送给学Go或者转Go同学的一套编码规范

webff · · 3172 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

![code.png](https://static.golangjob.cn/221209/2ba85e00eef5827a123742382a71e8d6.png) 有没有 xdm 是从别的语言转 Go 的,比如 Java 、php 等,有兄弟在刚开始学的时候疑惑怎么能写出来优秀的代码。最近在项目中也 codereview 了不少 Go 语言的代码,有必要总结下代码规范,算是一个笔记记录了。 > 说在前面,这只是我们团队的一套规范而已。 今天我们聊一下 Go 的编码规范,大概分为几大模块,如注包/变量/常量命名、基本语法、函数、错误处理、心得等。 ### 1. 代码风格 #### 1.1 代码格式 - 代码必须用 gofmt 进行格式化,goland 可以配置,可以自行搜索一下配置 - 我们编写的代码每行应该不超过 120 个字符,超出部分用换行解决。 - 单个文件最大行数最大不超过 800 行. - 单个函数最大行数不超过 80 行。 - import 规范 - 不要使用相对路径引入包,例如 `import ../util/net` - 在导入包时,多个相同包名冲突时,必须使用导入别名 ``` // bad "github.com/google/uuid" // good uuid "github.com/google/uuid" ``` - 导入的包建议分组,引用匿名包建议用一个新的分组,并加上注释方便后面小伙伴阅读 ``` import ( // Go 标准库 "fmt" //第三方包 "github.com/jinzhu/gorm" "github.com/google/uuid" "github.com/go-redis/redis/v8" // 匿名包 /import mysql driver _"github.com/jinzhu/gorm/dialects/mysql" // 内部包 slice "xxx.local/pkg/v1/goslice" meta "xxx.local/pkg/v1/meta" gomap "xxx.local/pkg/v2/gomap" ) ``` #### 1.2 声明、初始化和定义 - 一个函数需要使用多个变量时,可以在函数最开头处使用 var 声明。在函数外部声明的变量不能使用 :=,会踩坑,不知道的可以评论区留言(要评论不易呀)! ``` var ( port = 8081 metricServerPort = 2001 ) ``` - 在初始化结构体用 &struct 代替 new(struct),确保与结构体初始化一致,初始化结构体时换行。 ``` // bad stu := new(S) stu.Name = "张三" // good stu := &S{ Name:"李四" } ``` - 使用 make 在声明 map、array 等应该指定容器的容量,从而达到预先分配内容。 ``` users := make(map[int]string, 10) tags := make([]int, 0, 10) ``` - 使用标准 var 关键字事,不要指定类型,除非它与表达式的类型不同。 ``` // bad var _f string F() func F() string { return "hello world!" } // good var _f F() func F() string { return "hello world!" } ``` #### 1.3 error 处理 - 若函数返回 error, 必须对 error 进行处理,如果业务允许可以用 _ 接受忽略。对应 defer 可以不用显式进行处理。 ``` // bad func InitConfig() error { ... } InitConfig() // good func InitConfig() error { ... } err := InitConfig() if err != nil { ... } // or _ := InitConfig() ``` - error 作为返回值时必须作为最后一个参数返回 ``` // bad func InitConfig() (error,int) { ... } // good func InitConfig() (int, error) { ... } ``` - 错误需要单独处理,尽量不要与其他的逻辑耦合在一起。 ``` // bad res, err := InitConfig() if err != nil || res != nil { return err } // good res, err := InitConfig() if err != nil { return err } if res != nil { return fmt.Errorf("invalid result") } ``` #### 1.4 panic处理 - 业务代码中禁止抛出 panic 错误。 - panic 只允许出现在在服务启动之前,如读取配置、链接存储(redis、mysql 等)。 - 业务代码中建议用 error 而不是 panic 来传递。 #### 1.5 单元测试 - 每个重要的函数都要编写测试用例,合并代码要自动化运行一下所有的 test。 - 文件命名 xxx_test.go。 - 函数命名建议使用 Test函数名。 ### 2. 命名规范 在每个语言中,命名规范在代码规范中非常重要,一个统一的、精确的命名不仅仅可以提高代码的可读性,也可以让人觉的这个同志真的会呀。牛! #### 2.1 包命名规范 - 包名必须与目录名一致(这和其他 php、Java 还是有一点不太一样的),尽量采取有意义、简短的包名,不要与 go 的标准库名称一样。 - 包名小写,没有下划线,可以使用中划线隔开,使用多级目录来划分目录。 - 包名不要出现复数命名。 - 包名命名尽量简单一目了然,ge:user、log。 #### 2.2 文件命名规范 - 文件名要见名思义,尽量简而短 - 文件名小写,组合词用下划线分割 #### 2.3 函数命名规范 - 与 php、Java 一样,必须遵循驼峰规范,Go 语言中需要根据访问的控制决定大驼峰还是小驼峰。 - 单元测试的函数用大驼峰,TestFunc。 #### 2.4 结构体命名规范 - 与 php、Java 一样,必须遵循驼峰规范,Go 语言中需要根据访问的控制决定大驼峰还是小驼峰。 - 避免使用 info 、data 这种无意义的名称。 - 命名使用名词而非动词。 - 结构体在声明和初始化的时候需要换行,eg: ``` type Student struct{ Name string Age uint8 } student := Student{ Name: "张三", Age: 18, } ``` #### 2.5 变量命名规范 - 和 php、Java 一样,必须遵循驼峰规范,Go 语言中需要根据访问的控制决定大驼峰还是小驼峰。 - 若变量为私有时,可以使用小写命名。 - 局部变量可以简写,eg:i 表示 index。 - 若变量代表 bool 值,则可以使用 Is 、Can、Has 前缀命名,eg: ``` var isExit bool var canReturn bool ``` #### 2.6 常量命名规范 - 必须遵循驼峰规范,Go 语言中需要根据访问的控制决定大驼峰还是小驼峰。 - 若代表枚举值,需要先创建。 ``` type Code int const ( ErrNotFound Code = iota ErrFatal ) ``` ### 3. 类型 #### 3.1 字符串 好像学过的语言中,都是从字符串开始说起的。就像写代码第一行都是从 Hello World!一样!同意的点赞哈。 - 字符串判空值 ``` // bad if s == "" { ... } // good if len(s) == 0 { ... } ``` - 字符串去除前后子串。 ``` // bad var s1 "hello world" var s2 "hello" var s3 strings.TrimPrefix(s1, s2) // good var s1 "hello world" var s2 "hello" var s3 string if strings.HasPrefix(s1, s2){ s3 = s1[len(s2):] } ``` #### 3.2 切片 slice - 声明 slice。 ``` // bad s := []string{} s := make([]string, 10) // good var s []string s := make([]string, 0, 10) ``` - 非空判断。 ``` //bad if len(slice) >0 { ... } // good if slice != nil && len(slice) > 0 { ... } ``` - slice copy。 ``` // bad var b1,b2 []byte for i, v := range b1 { b2[i] = v } for i := range b1 { b2[i] = b1[i] } // good copy(b2,b1) ``` - slice 新增。 ``` // bad var a,b []int for _, v := range a { b = append(b,v) } // good var a, b []int b := append(b, a...) ``` #### 3.4 结构体 struct - 初始化需要多行。 ``` type Student struct{ Name string Age uint8 } student := Student{ Name: "张三", Age: 18, } ``` ### 4. 控制语句 #### 4.1 if - if 可以用局部变量的方式初始化。 ``` if err := InitConfig; err != nil { return err } ``` #### 4.2 for - 不允许在 for 中使用 defer, defer 只在函数结束时才会执行。 ``` // bad for file := range files { fd, err := os.Open(file) if err != nil { return err } defer fd.close() } // good for file := range files{ func() { fd,err := os.open(file) if err!=nil { return err } defer fd.close() }() } ``` #### 4.3 range - 如果不需要 key 直接用 _ 忽略,value 也一样。 ``` for _, v := range students { ... } for i, _ := range students { ... } for i, v := range students { ... } ``` **注:** 若操作指针时请注意不能直接用 s := v。想知道可以评论区告诉我哦! #### 4.4 switch - 和其他语言不一样,必须要有 defalt ``` switch type { case 1: fmt.Println("type = 1") break case 2: fmt.Println("type = 2") break default : fmt.Println("unKnown type") } ``` #### 4.5 goto - 业务中不允许使用 goto。 - 框架和公共工具也不允许使用 goto。 ### 5. 函数 - 传参和返回的变量小写字母。 - 传入参数时slice、map、interface、chan 禁止传递指针类型。 - 采用值传递,不用指针传值。 - 入参个数不能超出 5 个,超过的可以用 struct 传值。 #### 5.1 函数参数 - 返回值超出 1 个时,需要用变量名返回。 - 多个返回值可以用 struct 传。 #### 5.2 defer - 当操作资源、或者事物需要提交回滚时,可以在创建开始下方就使用 defer 释放资源。 - 创建资源后判断 error,非 error 情况后在用 defer 释放。 #### 5.3 代码嵌套 - 为了代码可读性,为了世界和平,尽量别用太多的嵌套,因为真的很难有人类能看懂。 ### 6. 日常使用感悟 - 能不用全局变量就不用,可以用参数传值的方式,这样可以大大降低耦合,更有利于单元测试。 - 衣服开发中,在函数间多用 context 传递上下文,在请求开始时可以生成一个 request_id,便于链路、日志追踪。 #### 6.1 提高性能 - 在业务开发中,尽量使用 strconv 来替代 fmt。 - 我们在使用 string 字符串类型时,当修改的场景较多,尽量在使用时用 []byte 来替代。因为每次对 string 的修改都需要重新在申请内存。 #### 6.2 避免踩坑 - append 要小心自动扩容的情况,最好在申明时分配好容量,避免扩容所带来的性能上的损耗以及分配新的内存地址。若不能确定容量,应选择一个比较大一点的值。 - 并发场景下,map 非线程安全,需要加锁。还有一种评论区告诉我吧。 - interface 在编译期间无法被检查,使用上会出现 panic,需要注意 ### 7. 总结 本篇很讲了 Go 语言的编码规范,当时想说的,规范是大家预定的东西,每个公司、团队都会有不一样的规范,只要大家一起遵循就好啦。你可以根据自己团队的需求,定一套属于自己团队的项目规范。如果想小伙伴一起遵循,可以借助一些工具来保障执行度。 讲了很多,虽然很基础,希望对于刚刚转 Go 语言,或者刚学习 Go 语言的同学有帮助吧。今天就到这里了。希望得到大家的一键三连。感谢! 欢迎关注微信【程序员祝融】,原文链接:https://mp.weixin.qq.com/s/lfjP9DEia2WL4UabxsDq0w ![微信.png](https://static.golangjob.cn/221209/6b82ffc1bbb877d55631b73171f3534b.png)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3172 次点击  ∙  1 赞  
加入收藏 微博
1 回复  |  直到 2023-01-12 18:09:24
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传