针对 package 和其成员的命名,本节会提供一些建议,使其与 Go 特有惯例相符。
先说创建、命名 package 时的注意事项:
保持名字短小,但不能太过简单短,以至于晦涩难懂。
标准包中最常用的被命名为 bufio, bytes, flag, fmt, http, io, json, os, sort, sync, time 。
名字要做清晰描述,杜绝通用含糊。
举例来说,工具包不要命名为 util ,要使用更明确,同时不失简洁的名字:imageutil 或 ioutil 。
package 名字,避开常用的变量名。
否则你就是在强迫使用者使用 renaming imports ,就像 path 包
包名称采用单数形式。
只有少数例外,才会采用复数形式,一种以 bytes, errors, strings 为代表, 目的是避开 Go 内置类型名称,另一种以 go/types 为代表,目的是避开对应的 Go 保留关键字。
避免会引发误解的名字
举例来说,2.5 节中温度转换包,我们开始叫 temp ,但没多久,我们就意识到这名字很糟,因为 temp 一般用于表示 temporary 。我们又改为 temperature ,但也没过多久,我们发现它又长,又不能说明它的作用。最终使用的名字是 tempconv ,它既短小,又和 strconv 这样的名字一致。
现在说说包内部成员的命名。
因为编码时,引用另一个包中的成员,要使用全名,比如 fmt.Println,因此描述成员,等同于包的名称加上成员的名称。
你可能已经发现用于格式化的函数 Println ,其函数名中并不提及“格式化”,这是因为该含义由包名达成。
这就是说,当设计一个 package 时,不能只关注成员命名,要意识到全名是由 2 个部分组成的,重点关注它俩组合后的整体效果 ?
下面是一些典型例子:
bytes.Equal
flag.Int
http.Get
json.Marshal
该如何借鉴一些已有的优秀命名模式 ?以 strings 包提供的一组彼此独立的字符串操作函数为例
package strings
func Index(needle, haystack string) int
type Replacer struct{ /* ... */ }
func NewReplacer(oldnew ...string) *Replacer
type Reader struct{ /* ... */ }
func NewReader(s string) *Reader
字符串 string 不会在其中的任何一个函数名称中出现。而使用者是通过 strings.Index, strings.Replacer 实现这些函数的引用。
除此之外,还有一些包,我们称为 single-type packages 。
比如 html/template 或是 math/rand ,它们会暴露一个主要的类型以及该类型的各种方法,还有一个 New 函数用来创建类型的实例。
package rand // "math/rand"
type Rand struct{ /* ... */ }
func New(source Source) *Rand
这些名字引用时一定会导致同义反复(重复),比如 template.Template 或 rand.Rand , 这就是为什么这类包的名字要取得特别短的原因。
最后还有一类特殊的情况。
那就是类似 net/http 这样的包,包内含有一堆的成员名,但并没有特定结构,这通常是因为这些包完成的任务太过复杂,与此同时,包内也许会含有一沓子各种类型,以及数量更多的函数。
要注意的是,包中最重要的成员的名字,应该是最简单的,比如 Get, Post, Handle, Error, Client, Server 。