命名是代码规范中很重要的一部分,统一的命名规范有利于提高代码的可读性,仅仅通过好的命名便可获取足够有用的信息。

通用规则

  • 不要用宽泛、无意义的名字,如 util、helper、info、common 等。
  • 首字母缩略语要么全小写,要么全大写。
// Bad
Md5
Rsa

// Good
MD5
md5
RSA
rsa

例外,如果是与其他单词构成驼峰命名风格,请使用驼峰风格。

// Bad
GetMD5
GetRSAKey

// Good
GetMd5
GetRsaKey
  • 非缩略语应该使用驼峰命名。
  • 不要使用 2/4 来表达英文 to/for。

因为这样的命名风格不够清晰易读。代码的可读性和可维护性是非常重要的,应该尽量避免使用令人困惑的命名方式。

  • 如无必要,不要起和包相同的名字。

项目命名

  • 小写,如果有多个单词使用连字符分隔。
// Bad
goecharts
go_echarts
goEcharts
GoEcharts

// Good
go-echarts

包命名

  • 保持包名和目录名一致。
  • 尽量采取有意义、简短的包名,不要和标准库冲突。
  • 包名应该为小写单词,不要使用下划线或者混合大小写,使用多级目录来划分层级。
  • 简单明了的包命名,如 time、list、http。
  • 不用复数。如 net/url 而不是 net/urls。
  • 包名谨慎使用缩写。当缩写是程序员广泛熟知的词时,可以使用缩写。例如:
    • strconv (string conversion)
    • syscall (system call)
    • fmt (formatted I/O)
  • 不要使用大而全无意义的包名。

util、common、misc、global。package 名字应追求清晰且收敛,符合‘单一职责’原则。而不是像common一样,什么都能往里面放,越来越膨胀,让依赖关系变得复杂,不利于阅读、复用和重构。注意,xxx/utils/encryption 这样的包名是允许的。

  • 只有一个源文件的包,包名应该和文件名保持一致。
  • 不要轻易使用别名。

更多可参考 Package names - The Go BlogStyle guideline for Go packages

文件命名

  • 采用有意义简短的文件名。
  • 文件名应该采用小写,并且使用下划线分割各个单词。

函数命名

  • 函数名必须遵循驼峰式,首字母根据访问控制决定使用大写或小写。
  • 代码生成工具自动生成的代码可排除此规则(如协议生成文件 xxx.pb.go,gotests 工具自动生成文件 xxx_test.go 里面的下划线)。
  • 函数应该以动词开头。
// Bad
func panicLinesParsing(){}
func (f VerifyFlow) DataETL(ctx context.Context, datas []Data){}

// Good
func parsePanicLines(){}
func (f VerifyFlow) ETLData(ctx context.Context, datas []Data){}

结构体命名

  • 采用驼峰命名方式,首字母根据访问控制采用大写或者小写。
  • 结构体名应该是名词或名词短语,如 Customer、WikiPage、Account、AddressParser,它不应是动词。
  • 避免使用 Data、Info 这类意义太宽泛的结构体名。
  • 结构体的定义和初始化格式采用多行。
// User 多行定义。
type User struct {
    Name  string
    Email string
}

// 多行初始化。
u := User{
    UserName: "john",
    Email:    "john@example.com",
}

接口命名

  • 命名规则基本保持和结构体命名规则一致。
  • 单个函数的接口名以 er 作为后缀,如 Reader,Writer。
// Reader 字节数组读取接口。
type Reader interface {
    // Read 读取整个给定的字节数据并返回读取的长度
    Read(p []byte) (n int, err error)
}
  • 两个函数的接口名综合两个函数名。
  • 三个以上函数的接口名,类似于结构体名。
// Car 小汽车结构申明。
type Car interface {
    // Start ...
    Start([]byte)
    // Stop ...
    Stop() error
    // Recover ...
    Recover()
}

量命名

通用

  • 不应该以类型作为前后缀。
// map
filterHandlerMap -> opToHandler

// slice
uidSlice -> uids

// array
uidArray -> uids

// 二维切片或数组。
// 比如多个班级下的学生ID。
uidSliceSlice -> classesUids
  • 量名应该是名词,进行时和过去式可以做形容词,成为量名的一部分。
  • 尽量不要用拼音命名。
  • 遵循驼峰式,根据是否导出决定首字母大小写。
// 导出全局变量。
var AppVersion = "1.0.0"
// 未导出全局变量。
var appVersion = "1.0.0"

// 导出全局常量。
const AppVersion = "1.0.0"
// 未导出全局常量。
const appVersion = "1.0.0"
  • 若量类型为 bool 类型,则名称应以 Has,Is,Can 或 Allow 等单词开头。
  • 私有量和局部量规范一致,均以小写字母开头。
  • 作用域较小的名字(局部变量/函数参数),尽量使用简短的名字。

如 c 比 lineCount 要好,i 比 sliceIndex 要好。

// Bad
lineCount := getlineCount()
    for sliceIndex := range msgs {
}

// Good
c := getlineCount()
    for i := range msgs {
}
  • 作用域较大的名字(全局变量),不要使用缩写,要有明确的意义。

如 lineCount 要比 c 好,sliceIndex 要比 i 好。

// Bad
var c, i int

// Good
var lineCount, sliceIndex int
  • 全局量中不要包含格式化字符,因为违反就近原则。
// Bad
var (
    tGitHost     = "https://git.code.oa.com"
    mrCommitsUri = "/api/v3/projects/%s/merge_request/%s/commits"
)

// Good
func getMRCommitsUri() string {
    return fmt.Sprintf("/api/v3/projects/%s/merge_request/%s/commits", "foo", "bar")
}

常量命名

  • 如果是枚举类型的常量,需要先创建相应类型。
// Scheme 传输协议。
type Scheme string

// 传输协议。
const (
    HTTP Scheme = "http"     // HTTP 明文传输协议
    HTTPS Scheme = "https"     // HTTPS 加密传输协议
)

方法接收器命名

  • 推荐以类名第一个英文首字母的小写作为接收器的命名。
  • 接收器的名称在函数超过 20 行的时候不要用单字符。
  • 命名不能采用 me,this,self 这类易混淆名称。

错误命名

对于存储为全局变量的错误值,根据是否导出,使用前缀 Err 或 err。

var (
  // 导出以下两个错误,以便此包的用户可以将它们与errors.Is 进行匹配。
  ErrBrokenLink = errors.New("link is broken")
  ErrCouldNotOpen = errors.New("could not open")

  // 这个错误没有被导出,因为我们不想让它成为我们公共 API 的一部分。
  errNotFound = errors.New("not found")
)

对于自定义错误类型,请改用后缀 Error。

// 这个错误被导出,以便这个包的用户可以将它与 errors.As 匹配。
type NotFoundError struct {
  File string
}

func (e *NotFoundError) Error() string {
  return fmt.Sprintf("file %q not found", e.File)
}

// 这个错误没有被导出,因为我们不想让它成为公共 API 的一部分。
// 但我们仍然可以在的包内将其和 errors.As 一起使用。
type resolveError struct {
  Path string
}

func (e *resolveError) Error() string {
  return fmt.Sprintf("resolve %q", e.Path)
}

避免使用内置名称

Go language specification 概述了几个内置的,不应在 Go 项目中使用的标识符 predeclared identifiers

Types:
    bool byte complex64 complex128 error float32 float64
    int int8 int16 int32 int64 rune string
    uint uint8 uint16 uint32 uint64 uintptr

Constants:
    true false iota

Zero value:
    nil

Functions:
    append cap close complex copy delete imag len
    make new panic print println real recover

在使用预先分配的标识符时编译器不会报告错误,但是诸如go vet之类的工具会正确地指出这些和其他情况下的隐式问题。

// Bad
// 作用域内隐式覆盖 error interface
var error string

func handleErrorMessage(error string) {
    // 作用域隐藏内置 error
}

type Foo struct {
    // 虽然这些使用内置标识符的自定义字段可以编译通过,但对 error 或 string 字符串的搜索存在二义性
    error  error
    string string
}

func (f Foo) Error() error {
    // error 和 f.error 在视觉上是相似的
    return f.error
}

func (f Foo) String() string {
    // string and f.string 在视觉上是相似的
    return f.string
}

// Good
var errorMessage string

func handleErrorMessage(msg string) {
}

type Foo struct {
    // error 和 string 现在是明确的
    err error
    str string
}

func (f Foo) Error() error {
    return f.err
}

func (f Foo) String() string {
    return f.str
}
powered by Gitbook该文章修订时间: 2024-08-04 07:27:05

results matching ""

    No results matching ""