有时候人们批评 Go 语言推荐短变量名的风格。正如 Rob Pike 所说,“ Go 程序员想要正确的长度的标识符”。 [1]
Andrew Gerrand 建议通过对某些事物使用更长的标识,向读者表明它们具有更高的重要性。
The greater the distance between a name’s declaration and its uses, the longer the name should be. (名字的声明与其使用之间的距离越大,名字应该越长。)
— Andrew Gerrand [2]
由此我们可以得出一些指导方针:
- 短变量名称在声明和上次使用之间的距离很短时效果很好。
- 长变量名称需要证明自己的合理性; 名称越长,需要提供的价值越高。冗长的名称与页面上的重量相比,信号量较小。
- 请勿在变量名称中包含类型名称。
- 常量应该描述它们持有的值,而不是该如何使用。
- 对于循环和分支使用单字母变量,参数和返回值使用单个字,函数和包级别声明使用多个单词
- 方法、接口和包使用单个词。
- 请记住,包的名称是调用者用来引用名称的一部分,因此要好好利用这一点。
我们来举个栗子:
type Person struct {
Name string
Age int
}
// AverageAge returns the average age of people.
func AverageAge(people []Person) int {
if len(people) == 0 {
return 0
}
var count, sum int
for _, p := range people {
sum += p.Age
count += 1
}
return sum / count
}
在此示例中,变量 p
的在第 10
行被声明并且也只在接下来的一行中被引用。 p
在执行函数期间存在时间很短。如果要了解 p
的作用只需阅读两行代码。
相比之下,people
在函数第 7
行参数中被声明。sum
和 count
也是如此,他们用了更长的名字。读者必须查看更多的行数来定位它们,因此他们名字更为独特。
我可以选择 s
替代 sum
以及 c
(或可能是 n
)替代 count
,但是这样做会将程序中的所有变量份量降低到同样的级别。我可以选择 p
来代替 people
,但是用什么来调用 for ... range
迭代变量。如果用 person
的话看起来很奇怪,因为循环迭代变量的生命时间很短,其名字的长度超出了它的值。
贴士:
与使用段落分解文档的方式一样用空行来分解函数。 在AverageAge
中,按顺序共有三个操作。 第一个是前提条件,检查people
是否为空,第二个是sum
和count
的累积,最后是平均值的计算。
上下文是关键
重要的是要意识到关于命名的大多数建议都是需要考虑上下文的。 我想说这是一个原则,而不是一个规则。
两个标识符 i
和 index
之间有什么区别。 我们不能断定一个就比另一个好,例如
for index := 0; index < len(s); index++ {
//
}
从根本上说,上面的代码更具有可读性
for i := 0; i < len(s); i++ {
//
}
我认为它不是,因为就此事而论, i
和 index
的范围很大可能上仅限于 for 循环的主体,后者的额外冗长性(指 index
)几乎没有增加对于程序的理解。
但是,哪些功能更具可读性?
func (s *SNMP) Fetch(oid []int, index int) (int, error)
或
func (s *SNMP) Fetch(o []int, i int) (int, error)
在此示例中,oid
是 SNMP
对象 ID
的缩写,因此将其缩短为 o
意味着程序员必须要将文档中常用符号转换为代码中较短的符号。 类似地将 index
替换成 i
,模糊了 i
所代表的含义,因为在 SNMP
消息中,每个 OID
的子值称为索引。
贴士: 在同一声明中长和短形式的参数不能混搭。