Go推荐命名规范

2021/11/17 实践总结规范

# Go命名规范

好的命名可以提高代码的可读性,特点:

  • 统一 : 容易猜想,约定俗成
  • 简短 :容易书写(Go尤为强调)
  • 准确 :准确表达意思

# 核心准则

声明的地方和使用的地方离得越远,名字就建议越详细,相对也会越长,同样上下文没提供有效的描述也是如此。

# 常见命名

# Camel命名

  • Go推荐驼峰命名方式,不建议使用下划线(包括常量,包名)

    Good

    userManger
    UserManager
    
    1
    2

    Bed

    user_manager
    
    1
  • 单词缩写默认全大写,或全小写

    Good

    userID
    baiduCDN
    id
    cdn
    
    1
    2
    3
    4

    Bed

    userId
    baiduCdn
    
    1
    2

# 项目名(仓库名)

  • 多个单词建议采用中划线分隔. 目前github中大多数项目都是使用中划线,不建议采用驼峰式分隔,不要使用下划线(kubernetes中的组件名称不允许使用下划线) 例如

    github.com/redis-go
    github.com/mattn/go-sqlite3
    github.com/gin-gonic
    
    1
    2
    3
  • 命名尽量在三个单词以内。

  • 命名可以是项目功能的描述;也可以是一个代号(如神话人物的名字,或者希腊语)

    (注:适合采用代号的项目有两种:公司的基础组件或者开源项目,一般这种项目都有详细的文档)

# Local变量

  • 保持简短。太长的命名有的时候反而使代码不简洁,影响阅读 索引:i (Good),index(Bad)

    reader:简写:r

    buffer:简写为:b

  • 避免冗余。命名不要和上下文重叠,例如:

    在user上下文中

    func UserCount() int
    
    1

    变量名count 会比userCount更简洁

    在map的上下文下可以简写:v,ok=m[k]

注:一般情况下不需要长命名,长命名出现的情况是func很长,变量很多(意味着你该重构了

Local变量 - 例子1

Bad

func RuneCount(buffer []byte) int {
    runeCount := 0
    for index := 0; index < len(buffer); {
        if buffer[index] < RuneSelf {
            index++
        } else {
            _, size := DecodeRune(buffer[index:])
            index += size
        }
        runeCount++
    }
    return runeCount
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Good

func RuneCount(b []byte) int {
    count := 0
    for i := 0; i < len(b); {
        if b[i] < RuneSelf {
            i++
        } else {
            _, n := DecodeRune(b[i:])
            i += n
        }
        count++
    }
    return count
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Local变量 - 例子2

Bad

func Read(buffer *Buffer, inBuffer []byte) (size int, err error){
        if buffer.empty() {
                buffer.Reset()
        }
        size = copy(
                inBuffer,
                buffer.buffer[buffer.offset:])
        buffer.offset += size
        return size, nil
}
1
2
3
4
5
6
7
8
9
10

Good

func Read(b *Buffer, p []byte) (n int, err error) {
        if b.empty() {
                b.Reset()
        }
        n = copy(p, b.buf[b.off:])
        b.off += n
        return n, nil
}
1
2
3
4
5
6
7
8

# Receiver命名

方法接收名称也是特殊的参数,一般用一个或两个字母(优先r)

例如:

func (b *Buffer) Read(p []byte) (n int, err error)
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
func (r Rectangle) Size() Point
1
2
3

注:

  • 同一个接收者的命名要保持全局唯一
  • 不会体现到文档里,指向性强
  • 避免是用"me", "this" or "self"

# func/method - 参数

和本地变量一样,但是名称还会作为文档,一般规则:

  • 如果类型具有描述特征,变量名可以简短,例如:

    func Escape(w io.Writer, s []byte)
    func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) 
    
    1
    2
  • 如果类型没有指向性,名字可以完整,例如:strings包:

    HasSuffix(s, suffix string) bool
    Map(mapping func(rune) rune, s string) string
    
    1
    2

# func/method - 返回值

  • 函数/方法可以对返回值定义变量名

  • 变量名仅作为文档,不能只是为了在方法体内少定义变量

  • 规则和参数类似,取决于类型是否具有描述性,例如:

    func Copy(dst Writer, src Reader) (written int64, err error)
    func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
    
    1
    2

# method - get/set

Go没对get/set特别支持,必要的时候可以自己定义 。Go对get有不同建议,认为不符合语言习惯,例如:

读取Persion获取FirstName

Bad:

p.GetFirstName()
1

Good:

p.FirstName()
1

# package导出命名

包对外导出变量、函数、类型等,在包的上下文中,避免冗余

Good

bytes.Buffer
strings.Reader	
1
2

Bad

bytes.ByteBuffer
strings.StringReader
1
2

# 接口命名

  • 如果接口只有一个方法,默认为方法名+er 来命名接口:例如:

    type Reader interface {
        Read(p []byte) (n int, err error)
    }
    type Purger interface {
      Purge(u PurgeURL) error
    }
    
    1
    2
    3
    4
    5
    6

    注:有的时候加er不一定是正确的单词,但是也还是会遵守

  • 如果接口有多个方法, 会以用一个能准备描述他的用途来命名 type ResponseWriter interface

# error的命名

  • 自定义error命名通常: “名称+Error” 作为结构体的名字,例如:

    type TypeError struct {
    	Errors []string
    }
    
    1
    2
    3
  • 变量时会用简写err + 名称

    ErrShortDst = errors.New("transform: short destination buffer")
    ErrShortSrc = errors.New("transform: short source buffer")
    ErrEndOfSpan = errors.New("transform: input and output are not identical")
    
    1
    2
    3

# 包命名

包名选择有意义的名称,尽量避免:util , common

bytes.Buffer
ioutil.ReadFile
strconv.Atoi
1
2
3

# import路径

  • 路径中的最后一段尽量和包名保持一致,例如:

    "net/http" // package http

    调用:http.File

  • 路径尽量流畅,避免类似这样:

    “github.com/goauth/oauth2”

    类库一般会把代码放在根目录:

    “github.com/oauth2”

  • 路径都是小写,尽量避免混合大小写

# 常用缩写

src = source
srv = server
arg = argument
conn = connect, connection
attr = attribute
abs = absolute
min = minimum
len = length
auth = authenticate
buf = buffer
ctl = control
ctx = context
str = string
msg = message
fmt = format
dest = destination
diff = difference
orig = original
recv = receive
ref = reference
repo = repository
util = utility
fmt = format
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

参考资料:

  • 常用命名缩写:https://github.com/BluntSporks/abbreviation
  • Package names:https://go.dev/blog/package-names
  • Effective Go:https://golang.org/doc/effective_go#names