cover_image

10.6 包和命名

张加强 小强会武术
2021年09月03日 07:06

10.6 包和命名

针对 package 和其成员的命名,本节会提供一些建议,使其与 Go 特有惯例相符。

1. package 的命名

先说创建、命名 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 这样的名字一致。

2. package 内部成员的命名

现在说说包内部成员的命名。

因为编码时,引用另一个包中的成员,要使用全名,比如 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 实现这些函数的引用。

3. 其他情况下的命名

除此之外,还有一些包,我们称为 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 。

继续滑动看下一个
小强会武术
向上滑动看下一个