可读代码编写炸鸡二 (下篇) - 命名的歧义
大家好,我是多选参数的一员 —— 大炮。
在上一篇炸鸡 可读代码编写炸鸡二(上篇) - 命名的长度 中,我们知道了:
由于代码命名添加信息后,存在 命名长度 和 命名歧义 这两个方面问题。
上一篇也提供了一些关于 命名长度 方面的一些建议。
所以接下来本篇炸鸡便提供一些关于 命名歧义 方面的一些建议。
同时不要忘记上一篇炸鸡中抛出的一个问题:
表达 商店数量上限 的常量命名可以是
MAX_SHOP_COUNT,那SHOP_COUNT_LIMIT合适吗?
带着这个问题,我们开始吧。
命名的歧义
命名的歧义是如何产生的?
由于命名需要词汇组织,那么 词汇的多义性 可能会导致命名产生歧义。
同时程序员中 约定俗成 的规则也可能使得命名出现歧义。
百闻不如一见,举些例子和针对其的解决方法,希望能给你一点启发。
filter()
这个 filter 方法,到底是过滤了什么。过滤了 2011 年以前的数据,还是过滤了 2011 年以后的数据?
如果需要这些数据,不妨使用 select()
如果想过滤掉这些数据,不妨使用 exclude()
所以用更具体的意思来表达功能,是更妥当的。
clip(text, length)
函数 clip 代表缩短的意思,将文本做一个缩短操作。
但是,这个方法在阅读者角度,就产生歧义。这个 clip 可能存在两种意思:
若函数本意是
若函数本意是
同时我们也可以看出,length 也充满了歧义,如果写成 max_length,可能就稍微清晰一些。
但是这个 max_length 是 text 的字符串长度,还是字节长度,还是包含单词个数?
很明显,先前文章 可读代码编写炸鸡一 提到的,加入更多的信息的一个办法 —— 加入单位。
max_chars
max_bytes
max_words
包含或者不包含
比如说,需要一个常量来规定购物车数量限制。
是要大于 10,还是大等于 10,才算超过限制?
亦或是小于 10,还是小等于 10,才算超过限制?
最好的办法就是加上 min/max 前缀。
如果换成 MAX_ITEM_IN_CART, 这个就一目了然是最大个数的限制。
还有一个类似的问题,便是区间问题。假设我们有一个函数,根据传入区间左右两点来生成一串序列。
那调用函数时,intRange(n, m),返回的序列存在四种情况:
(n, m)
[n, m]
(n, m]
[n, m)
所以 intRange(start, stop) 这样的函数原型,参数的命名会带来一定歧义。
所以如果是要两端都包含,也就是 [n, m] ,最好使用 first/last 作为命名。
那么如果是要表达 [n, m) 呢。那就是使用 begin/end。但是这两个太惯用了,例如 lua 就是用 end 来作为函数的范围结尾限定。可以试用 ending。
命名布尔值变量
关于命名布尔值变量产生的歧义,举一个 bool read_password 例子。
这个变量会出现两种意思。
是否需要读
password或者是
password已经是否被读了。
所以 read 这个词还是换了。如果要表达第一种意思,可以用 need_password。
第二种意思的话,就用 user_is_authenticated。
所以,善于使用 is, has, can, should, 都可使得布尔值命名传达的信息更加清晰,而且让人读了就知道,这是布尔值变量。
同时再举一个例子,这个例子我的项目组挺喜欢用的,就是使用带有 否定意味 的词语。
noSync = falsedisable_ssl = false
这样绕不绕?
所以 否定意味 的词语尽量不要加入布尔变量的命名。
我这么改造一下,意思就明朗多了,不需要绕来绕去九曲十八弯。
needSync = falseis_use_ssl = false。
约定俗成误导阅读者
get()
一般情况下,get 方法一般是被用于返回类的 轻量级 的内部成员,或者说,get 方法内部逻辑不复杂不繁重。
但是如果一个方法中存在大量的数据计算或者内存分配,只有一个 get ,就可能忽略了方法中大量的重的逻辑。
例如我们有一个 getCityInfo 的方法
由于 get 的存在,会让阅读者和使用者产生 这个操作很轻量 的错觉而泛滥使用,使得程序效率下降。
换成 computeXXX(), allocateXXX() 会更好一些。
我所在的项目中,主程倒是推荐我们使用 fetchxxx()。这样就能告诉阅读者,这是繁重的操作,谨慎使用。
list->size()
在链表实现代码中,常常有求链表长度的操作,不少人将其命名为 size。
这个 size,很容易会被当做是一个 属性 被返回,但是如果代码如下:
由上述代码可知,size 不仅是一个方法,还是一个 操作很繁重 的方法。
除了上文提到的利用 computeXXX()、 allocateXXX() 、fetchxxx() 修改命名,让阅读者知晓以外,还可以直接修改代码实现:
让 List 保存一个 _size 成员变量来记录链表长度。这样就不用每次都遍历链表来求长度了。
回顾开头的问题
表达 商店数量上限 的常量命名可以是
MAX_SHOP_COUNT,那SHOP_COUNT_LIMIT合适吗?
现在不知道你心中有答案了吗?
总结
好的命名要将歧义出现的可能降到最低。
filter,length这些其实都充满歧义,使用更加具体的意义命名。如果要有个表示上下限变量,
max/min前缀是个好选择。如果是表示 [n, m] 这个区间,用
first/last比较好。如果是表示 [n, m) 这个区间,用
begin/ending,end比较好。命名一个布尔值变量,善用
can, is, has等修饰。同时,否定意味的词,例如disable_ssl,noSync尽量不用。由于一些约定俗成的规则,阅读者还是容易对一个词产生惯性思维。例如
get(),List::size(),会传递一个 轻量操作 的错误信息。
版权声明: 本文为 InfoQ 作者【多选参数】的原创文章。
原文链接:【http://xie.infoq.cn/article/88b12e2561a638ec793bc1682】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。











评论