Go 学习笔记(83)— 编码规范及常用开发技巧
UBER 开源的 Go 语言开发规范
1. 规范指南
1.1 包名
package
的命名应该遵循如下原则:
- 只由小写字母组成。不包含大写字母和下划线等字符;
- 简短并包含一定的上下文信息。例如
time
、list
、http
等; - 不能是含义模糊的常用名,或者与标准库同名。例如不能使用
util
或者strings
; - 包名能够作为路径的
base name
,如果包名字符串过长则尽量使用目录来分割。例如应该使用encoding/base64
而不是encoding_base64
或者encodingbase64
; - 包名与目录保持一致,尽量有意义,简短且简洁能代表该包的主要功能,不和标准库冲突,防止导入包时需要重命名, 全小写,不要有下划线,包名不用复数形式,例如
net/url
,而不是net/urls
;
以下规则按照先后顺序尽量满足:
- 不使用常用变量名作为包名。例如使用
bufio
而不是buf
; - 使用单数而不是复数。例如使用
encoding
而不是encodings
; - 谨慎地使用缩写。例如使用
fmt
在不破坏上下文的情况下比format
更加简短,以及一些需要用多个单词表达上下文的命名可以使用缩写,例如使用strconv
而不是stringconversion
。
1.2 变量命名
变量的命名应该遵循如下原则:
对于可导出的变量使用
MixedCaps
,对于内部使用的变量使用mixedCaps
;缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写。例如使用
ServeHTTP
而不是ServeHttp
,以及使用XMLHTTPRequest
或者xmlHTTPRequest
;简洁胜于冗长。例如在循环中,使用
i
代替sliceIndex
;变量距离其被使用的地方越远,则需要携带越多的上下文信息。例如全局变量在其名字中需要更多的上下文信息,使得在不同地方可以轻易辨认出其含义;
错误变量名一般以
Err
或者err
开头,后面使用驼峰命名法;和结构体类似,变量名称一般遵循驼峰法,首字母根据访问控制原则大写或者小写,但遇到特有名词时,需要遵循以下规则:
- 如果变量为私有,且特有名词为首个单词,则使用小写,如
apiClient
- 其它情况都应当使用该名词原有的写法,如
APIClient
、repoID
、UserID
- 错误示例:
UrlArray
,应该写成urlArray
或者URLArray
- 如果变量为私有,且特有名词为首个单词,则使用小写,如
若变量类型为
bool
类型,则名称应以Has
、Is
、Can
或Allow
开头
var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool
1.2 变量初始化
所以我们更青睐下面这样的形式:
var (a = 13b = int32(17)f = float32(3.14)
)
而不是下面这种看起来不一致的声明形式:
var (a = 13b int32 = 17f float32 = 3.14
)
1.3 常量命名
常量命名常量均需使用全部大写字母组成,并使用下划线分词
const APP_VER = "1.0"
1.3 函数名
函数的命名应该遵循如下原则:
- 对于可导出的函数使用
MixedCaps
,对于内部使用的函数使用mixedCaps
; - 函数名不携带包名的上下文信息。例如使用
http.Server
而不是http.HTTPServer
,因为包名和函数名总是成对出现的。 - 函数名尽量简短:
- 当名为
foo
的包某个函数返回类型Foo
时,往往可以省略类型信息而不导致歧义。例如使用time.Now()
以及time.Parse()
,两者返回的都是time.Time
类型; - 当名为
foo
的包某个函数返回类型T
时(T
并不是Foo
),可以在函数名中加入类型信息。例如使用time.ParseDuration()
返回的是time.Duration
类型; - 当名为
foo
的包某个函数返回类型Foo
,且Foo
是其所有方法的入口时,可以使用New()
来命名而不导致歧义。例如使用list.New()
返回的是*list.List
类型。
- 当名为
1.4 结构体
struct
声明和初始化格式采用多行: 定义如下:
type User struct{Username stringEmail string
}
初始化如下:
u := User{Username: "wohu",Email: "wohu@gmail.com",
}
1.5 接收器名
接收器的命名应该遵循如下原则:
- 不要使用面向对象编程中的常用名。例如不要使用
self
、this
、me
等; - 一般使用 1 到 2 个字母的缩写代表其原来的类型。例如类型为
Client
,可以使用c
、cl
等; - 在每个此类型的方法中使用统一的缩写。例如在其中一个方法中使用了
c
代表了Client
,在其他的方法中也要使用c
而不能使用诸如cl
的命名;
recieved
是值类型还是指针类型到底是采用值类型还是指针类型主要参考如下原则:
func(w Win) Tally(playerPlayer)int //w不会有任何改变
func(w *Win) Tally(playerPlayer)int //w会改变数据
1.6 接口名
接口的命令应该遵循如下原则:
- 对于只有一个方法的
interface
,通常将其命名为方法名加上er
。例如Reader
和Writer
; interface
的方法不要占用一些惯用名,除非此方法具有同样的作用。例如Read
、Write
、Flush
、String
、ServeHTTP
;
1.7 参数传递
- 对于少量数据,不要传递指针
- 对于大量数据的
struct
可以考虑使用指针 - 传入参数是
map
,slice
,chan
,interface
,string
不要传递指针
1.8 源文件名
如果要在源文件的名字中使用多个单词,我们通常直接是将多个单词连接起来作为源文件名,而不是使用其他分隔符,比如下划线。
也就是说,我们通常使用 helloworld.go
作为文件名而不是 hello_world.go
。这是因为下划线这种分隔符,在 Go
源文件命名中有特殊作用。
1.9 其它
- 每个基础库都必须有实际可运行的例子, 基础库的接口都要有单元测试用例
- 不要在
for
循环里面使用defer
,defer
只有在函数退出时才会执行 panic
捕获只能到goroutine
最顶层,每个自己启动的goroutine
,必须在入口处就捕获panic
,并打印出详细的堆栈信息Go
的内置类型slice
、map
、chan
都是引用,初次使用前,都必须先用make
分配好对象,不然会有空指针异常- 使用
map
时需要注意:map
初次使用,必须用make
初始化;map
是引用,不用担心赋值内存拷贝;并发操作时,需要加锁;range
遍历时顺序不确定,不可依赖;不能使用slice
、map
和func
作为key
import
在多行的情况下,goimports
会自动帮你格式化,但是我们这里还是规范一下import
的一些规范,如果你在一个文件里面引入了一个package
,还是建议采用如下格式:
import ("fmt"
)
如果你的包引入了三种类型的包,标准库包,程序内部包,第三方包,建议采用如下方式进行组织你的包:
import ("encoding/json""strings""myproject/models""myproject/controller""myproject/utils""github.com/astaxie/beego""github.com/go-sql-driver/mysql"
)
有顺序的引入包,不同的类型采用空格分离,第一种是标准库,第二是项目包,第三是第三方包。
- 长句子打印或者调用,使用参数进行格式化分行 我们在调用
fmt.Sprint
或者log.Sprint
之类的函数时,有时候会遇到很长的句子,我们需要在参数调用处进行多行分割:
下面是错误的方式:
log.Printf(“A long format string: %s %d %d %s”, myStringParameter, len(a),expected.Size, defrobnicate(“Anotherlongstringparameter”,expected.Growth.Nanoseconds() /1e6))
应该是如下的方式:
log.Printf( “A long format string: %s %d %d %s”, myStringParameter,len(a),expected.Size,defrobnicate(“Anotherlongstringparameter”,expected.Growth.Nanoseconds()/1e6, ),
)
- 注意闭包的调用 在循环中调用函数或者
goroutine
方法,一定要采用显示的变量调用,不要在闭包函数里调用循环的参数
fori:=0;i<limit;i++{go func(){ DoSomething(i) }() //错误的做法go func(i int){ DoSomething(i) }(i)//正确的做法
}
2. 开发技巧
package
的名字和目录名一样,main
除外string
表示的是不可变的字符串变量,对string
的修改是比较重的操作,基本上都需要重新申请内存,如果没有特殊需要,需要修改时多使用[]byte
- 尽量使用
strings
库操作string
,这样做可以提高性能 append
要小心自动分配内存,append
返回的可能是新分配的地址- 如果要直接修改
map
的value
值,则value
只能是指针,否则要覆盖原来的值 map
在并发中需要加锁- 编译过程无法检查
interface{}
的转换,只有运行时检查,小心引起panic
- 使用
defer
,保证退出函数时释放资源 - 尽量少用全局变量,通过参数传递,使每个函数都是“无状态”的,这样减少耦合,也方便分工和单元测试
- 参数如果比较多,将相关参数定义成结构体传递
写完代码都必须格式化,保证代码优雅
gofmt -w main.go
,
gofmt
是Go
自带的格式化工具,用于整理代码格式、末尾加空格等。参考 https://pkg.go.dev/cmd/gofmt编译前先执行代码静态分析:
go vet pathxxx/
go vet
是 Go 自带的工具,用于检查程序是否正确。参考 https://pkg.go.dev/cmd/vet
竞态检测:
go build –race
(测试环境编译时加上-race
选项,生产环境必须去掉,因为race
限制最多goroutine
数量为 8192 个)每行长度约定:一行不要太长,超过请使用换行展示,尽量保持格式优雅;单个文件也不要太大,最好不要超过 500 行
多返回值最多返回三个,超过三个请使用
struct
在逻辑处理中禁用
panic
,除非你知道你在做什么错误处理的原则就是不能丢弃任何有返回
err
的调用,不要采用_
丢弃,必须全部处理。接收到错误,要么返回err
,要么实在不行就panic
,或者使用log
记录下来。 不要这样写:
if err != nil {// error handling} else {// normal code}
而应该是:
if err != nil {// error handlingreturn // or continue, etc.
}// normal code
3. 函数的健壮性
函数的健壮性设计包括很多方面,首先就有最基本的“三不要”原则
原则一:不要相信任何外部输入的参数。
函数的使用者可能是任何人,这些人在使用函数之前可能都没有阅读过任何手册或文档,他们会向函数传入你意想不到的参数。因此,为了保证函数的健壮性,函数需要对所有输入的参数进行合法性的检查。一旦发现问题,立即终止函数的执行,返回预设的错误值。
原则二:不要忽略任何一个错误。
在我们的函数实现中,也会调用标准库或第三方包提供的函数或方法。对于这些调用,我们不能假定它一定会成功,我们一定要显式地检查这些调用返回的错误值。一旦发现错误,要及时终止函数执行,防止错误继续传播。
原则三:不要假定异常不会发生。
这里,我们先要确定一个认知:异常不是错误。错误是可预期的,也是经常会发生的,我们有对应的公开错误码和错误处理预案,但异常却是少见的、意料之外的。通常意义上的异常,指的是硬件异常、操作系统异常、语言运行时异常,还有更大可能是代码中潜在 bug 导致的异常,比如代码中出现了以 0 作为分母,或者是数组越界访问等情况。虽然异常发生是“小众事件”,但是我们不能假定异常不会发生。所以,函数设计时,我们就需要根据函数的角色和使用场景,考虑是否要在函数内设置异常捕捉和恢复的环节。
最佳实践:
Effective Go : 高效 Go 编程,由 Golang 官方编写,里面包含了编写 Go 代码的一些建议,也可以理解为最佳实践。
Go Code Review Comments Golang 官方编写的 Go 最佳实践,作为 Effective Go 的补充。
Style guideline for Go packages 包含了如何组织 Go 包、如何命名 Go 包、如何写 Go 包文档的一些建议。
https://github.com/cristaloleg/go-advice/blob/master/README_ZH.md
https://github.com/cristaloleg/go-advice
Uber Go 语言编码规范
Go 学习笔记(83)— 编码规范及常用开发技巧相关推荐
- OceanBase 从0到1数据库内核实战教程学习笔记 - 3.OceanBase基础架构和开发技巧
这篇文章主要介绍王泽林老师分享的 <OceanBase 的基础架构和开发技巧>.如果您看过第一篇文章的对应视频,会发现整个系列主要分为 MiniOB 和 OceanBase 两个系列,本篇 ...
- 数据库学习笔记第三弹——MySQL常用的图形化管理辅助工具及相关问题(图文详解2022))
数据库学习笔记第三弹--MySQL常用的图形化管理辅助工具(图文详解2022) 文章目录 数据库学习笔记第三弹--MySQL常用的图形化管理辅助工具(图文详解2022) 1.MySQL常用的图形化管理 ...
- oracle常用数据统计,学习笔记:Oracle DBMS_STATS常用方法汇总 常用于收集统计oracle...
天萃荷净 Oracle数据库中DBMS_STATS常用方法(收集oracle数据库.索引.表等信息) –收集Oracle数据库信息命令 EXEC DBMS_STATS.gather_database_ ...
- 【白帽子学习笔记14】SQL注入常用语句
[白帽子学习笔记14]SQL注入常用语句 目前网站中使用的最多的数据库要算是 ACCESS.SQL Server(MSSQL).MySQL 这三个了,所以这里的手工注入,我就以他们三个数据库来分成三 ...
- 「学习笔记」黑马面面布局开发
「学习笔记」黑马面面布局开发 黑马面面布局开发 一.目的 1.1 技术方案 1.2 代码规范 1.2 目录规范 二.流程开发 2.1 蓝湖/摹客协作平台 2.2 适配方案 2.3 初始化文件 2.4 ...
- Halcon学习笔记(一):Qt+Halcon联合开发配置
Halcon学习笔记(1):Qt+Halcon联合开发配置 首先是新建一个QT项目qtest_hc 方法一: 1)QT项目文件 qtest_hc 添加库: #1.包含目录添加 INCLUDEPATH ...
- Kinect开发学习笔记之(三)Kinect开发环境配置
Kinect开发学习笔记之(三)Kinect开发环境配置 zouxy09@qq.com http://blog.csdn.net/zouxy09 我的Kinect开发平台是: Win7 x86 + V ...
- Kinect开发学习笔记之(二)Kinect开发学习资源整理
Kinect开发学习笔记之(二)Kinect开发学习资源整理 zouxy09@qq.com http://blog.csdn.net/zouxy09 刚刚接触Kinect,在网上狂搜资料,获得了很多有 ...
- PyTorch学习笔记(六):PyTorch进阶训练技巧
PyTorch实战:PyTorch进阶训练技巧 往期学习资料推荐: 1.Pytorch实战笔记_GoAI的博客-CSDN博客 2.Pytorch入门教程_GoAI的博客-CSDN博客 本系列目录: P ...
最新文章
- 计算机专业术语graphui,计算机专业术语英译
- 了解linux内核必读的5本书
- 插入顶部_轻巧的衣领插入技术
- 面试官:知道时间轮算法吗?在Netty和Kafka中如何应用的?
- grads 相关系数_基于小波变换的多聚焦图像融合算法
- poj 1298 The Hardest Problem Ever
- 零基础学习 Python 之文件
- python 复制文件_python 复制文件
- [渝粤教育] 广东-国家-开放大学 21秋期末考试中国近现代史纲要(A)10881k1 (2)
- Windows Live Messenger 2011,离线安装、多开、去广告……
- netstat php,netstat查看网络端口情况
- 禁用惠普服务器自动开机,惠普商用台式机如何在BIOS中设置通电自动开机
- 自媒体怎么做?5个操作步骤,普通人也可以做
- Chrome浏览器清除cookies方法
- 织梦php数据库修改密码,如何使用phpmyadmin修改织梦后台密码
- 系统集成资质培训 - 挣值分析题目分析
- 5G承载网,到底有哪些关键技术?
- word图文混排复制到CuteEditor图片不显示
- 2006年6月26日之足球不眠夜---意大利+黄健翔VS澳大利亚
- 单机版音乐播放器--ZTPlayer