代码质量随想录(三)名字好,误会少 推荐
2012-06-05 16:53
176 查看
写完前两篇之后,有点小倦怠,因为一方面要整理读书笔记,一方面还要结合自己的思路加以重新表述,颇费周张。不过前两日看到有小朋友过来赞我的文章,说对实际代码有所帮助,还是满欣慰的,本系列随想录的目的之一,就是要营造一个努力改良代码质量的思维环境。
要想让标识符的名称更易理解,就应该多考虑考虑此名称是否会被误读。
先看两个很容易误读的例子。
到底是要选出year小于等于2011的那部分对象,还是选出year大于2011的那部分呢?filter到底是排除(exclude),还是遴选(select)呢?我自己在日常编码中也爱用filter,多半由于习惯。现在自己思量,是得改正了。再看
clip方法有歧义:到底是去掉文本后的length个字符,还是从头开始截取最大length个字符呢?比如clip("Java",2);到底是"va"还是"Ja"?如果是前者应该叫removeLast,如果是后者则应叫truncate。而且length也有毛病,到底以什么为单位?字节?字符?还是词语?如果是字符,应该是truncate(Stringtext,intmaxCharCount)。
归纳起来说,以下几种情形应格外注重选取避免误解的名称。
1.以常量表示包含端点的上限或下限时,应分别用MAX与MIN做前缀。
例如CART_TOO_BIT_LIMIT=10到底是说购物车中最多放10件商品还是11件?抑或是9件?改为MAX_ITEMS_IN_CART=10则很清楚:最多10件。
2.在表达包含左右端点的区间时,应用first及last。
到底打印[2,3,4,5]还是[2,3,4,5,6]?如果是后者,应该是printIntegerInRange(intfirst,intlast)。
3.在表达包含左端点而不含右端点的区间时,应当使用begin与end。
英文中没有哪个常用词的字面意思能表示“区段内最后一个值的紧下一个值”这个意思,所以使用end只是约定成俗而已,并不精确。例如publicvoidprintEventsInRange(Stringbegin,Stringend),可以使用如下参数来调用:printEventsInRange("OCT1600:00","OCT1700:00"),这样的话,一般人都能理解右端点("OCT1700:00")不含在范围内。如果用publicvoidprintEventsInRange(Stringfirst,Stringlast),则是printEventsInRange("OCT1600:00","OCT1623:59")。
4.使用判断词来消除boolean变量的歧义。
为boolean变量起名时一定注意是否有歧义:
到底是当前需要读取密码,还是密码已经被读取过了?前者应是needPassword,后者应是userIsAuthenticated。
使用is、has、can、should等词汇来让boolean变量与方法的意图更加清晰,尤其是在那些不需要申明方法或函数返回类型的编程语言中。例如:spaceLeft()到底是返回剩下的空间大小,还是返回是否有剩余空间?根据是简单获取还是复杂计算,前者应命名为getLeftSpaceInPixel()或calcLeftSpacePx(),分别指示轻量级(get)和重量级(calculate或compute)的两种获取办法;而后者则应是hasSpaceLeft(),只说有没有剩余空间,不谈具体的量。
5.避免在boolean命名中使用否定形式。
例如:
不如下面这种命名方式清晰:
6.不要同约定成俗的命名方式相违逆。
例如getXXX()格式的方法一般有两个隐含意义:1.该操作为轻量级。2.该操作返回所在类的某个成员。
如下统计算数平均数的方法名称即为不宜:
getMean()并非轻量级操作,且不返回本类某个成员。不如叫它computeMean()更好,compute会引人联想该操作是不是稍为复杂一些,耗时一些。如果非要用getMean做名称的话,那么mean应被纳入缓存机制。例如:
[/code]
size()操作的时间复杂度为O(1)应是大多数人的共识,可是恰恰list的size()是时间复杂度为O(n)的操作,这导致整个函数的复杂度变为O(n2)。按理说size()应该叫为countSize()或countElements(),以体现其重量级运算的特质来,不过,为了和其余容器类相符合,还是叫成size了。所幸新版C++规范强制要求size操作的时间复杂度为O(1)了(ARC书的作者这么说的,我未查证。大家帮忙在C++11规范中查证此事。原有规范只是“建议”它应具有常数时间复杂度,并未强制)。
小翔以为,如果某个抽象接口定义了一个貌似轻量级的简单操作,如Collection的size(),则子类对象在实现时应该尽量降低时间复杂度。实在不能时甚至可以考虑抛出异常或对客户提出警告。根本的解决办法还是学习C++规范那样,给出一个建议的时间复杂度来。
7.在多个候选名称中取舍时应该仔细质询其可能带来的歧义。
例如有两份相似的服务器配置参数文件:
我们现在想通过某个机制复用整套参数,例如这样:
那么,这个“想要复用的配置文件id”,应该怎么起名呢?备选关键词有:template、reuse、copy和inherit。
template很模糊:“template:100”到底是说自己是一份名叫“100”模板,还是说使用一个名叫“100”的模板作为其基础参数?况且模板这个概念太过抽象,给人感觉需要以具体内容填充它。
“reuse:100”到底是说这份参数最多可以使用100次,还是说复用名为“100”的那份配置文件中的参数?
“copy:100”是第100份拷贝吗?还是说拷贝自编号为“100”的那套配置?后者不如叫copy_config_from更好。
“inherit:100”,inherit这个词,大多数程序员很熟悉,且与日常生活的“财产继承”概念可相比拟,所以引起的误解相对较少。可以扩充为inherit_config_from来更精确地阐明这个意思。
综上,copy_config_from或inherit_config_from应为最终中选名称。
总之,好的标识符名称可以尽量消除代码阅读者的误解,提高代码可读性与可维护性,亦能促进业务交流。所以应当仔细考究,尽量选取免于误会的名称,尤其是遇到“filter、length和limit”这些模棱两可的词语时。此外,区间与上下限含不含端点、boolean类型的标识符会不会引起误解、方法名称所隐含的意义是否符合常识,这些问题也应该在起名时反覆考量。
用了两篇文章才讲完给标识符起名的事情,可见其的确关乎代码质量的提升。下一篇我们谈谈代码的排版问题。
爱飞翔2012年6月4日
本文使用CreativeCommonsBY-NC-ND3.0协议(创作共用自由转载-保持署名-非商业使用-禁止衍生)发布。
本文网址:http://agilemobidev.com/eastarlee/code-quality/think_in_code_quality_3_good_name_zh_cn/
要想让标识符的名称更易理解,就应该多考虑考虑此名称是否会被误读。
先看两个很容易误读的例子。
Object[]results=Database.getAllObjects().filter("year<=2011");
到底是要选出year小于等于2011的那部分对象,还是选出year大于2011的那部分呢?filter到底是排除(exclude),还是遴选(select)呢?我自己在日常编码中也爱用filter,多半由于习惯。现在自己思量,是得改正了。再看
publicStringclip(Stringtext,intlength);//裁掉文本的末尾
clip方法有歧义:到底是去掉文本后的length个字符,还是从头开始截取最大length个字符呢?比如clip("Java",2);到底是"va"还是"Ja"?如果是前者应该叫removeLast,如果是后者则应叫truncate。而且length也有毛病,到底以什么为单位?字节?字符?还是词语?如果是字符,应该是truncate(Stringtext,intmaxCharCount)。
归纳起来说,以下几种情形应格外注重选取避免误解的名称。
1.以常量表示包含端点的上限或下限时,应分别用MAX与MIN做前缀。
例如CART_TOO_BIT_LIMIT=10到底是说购物车中最多放10件商品还是11件?抑或是9件?改为MAX_ITEMS_IN_CART=10则很清楚:最多10件。
2.在表达包含左右端点的区间时,应用first及last。
publicvoidprintIntegerInRange(intstart,intstop){...} ... printIntegerInRange(2,6);
到底打印[2,3,4,5]还是[2,3,4,5,6]?如果是后者,应该是printIntegerInRange(intfirst,intlast)。
3.在表达包含左端点而不含右端点的区间时,应当使用begin与end。
英文中没有哪个常用词的字面意思能表示“区段内最后一个值的紧下一个值”这个意思,所以使用end只是约定成俗而已,并不精确。例如publicvoidprintEventsInRange(Stringbegin,Stringend),可以使用如下参数来调用:printEventsInRange("OCT1600:00","OCT1700:00"),这样的话,一般人都能理解右端点("OCT1700:00")不含在范围内。如果用publicvoidprintEventsInRange(Stringfirst,Stringlast),则是printEventsInRange("OCT1600:00","OCT1623:59")。
4.使用判断词来消除boolean变量的歧义。
为boolean变量起名时一定注意是否有歧义:
boolreadPassword=true;
到底是当前需要读取密码,还是密码已经被读取过了?前者应是needPassword,后者应是userIsAuthenticated。
使用is、has、can、should等词汇来让boolean变量与方法的意图更加清晰,尤其是在那些不需要申明方法或函数返回类型的编程语言中。例如:spaceLeft()到底是返回剩下的空间大小,还是返回是否有剩余空间?根据是简单获取还是复杂计算,前者应命名为getLeftSpaceInPixel()或calcLeftSpacePx(),分别指示轻量级(get)和重量级(calculate或compute)的两种获取办法;而后者则应是hasSpaceLeft(),只说有没有剩余空间,不谈具体的量。
5.避免在boolean命名中使用否定形式。
例如:
booldisableSSL=false;
不如下面这种命名方式清晰:
booluseSSL=true;
6.不要同约定成俗的命名方式相违逆。
例如getXXX()格式的方法一般有两个隐含意义:1.该操作为轻量级。2.该操作返回所在类的某个成员。
如下统计算数平均数的方法名称即为不宜:
publicclassSampleCollector{
publicvoidadd(doublesample){...}
publicdoublegetMean(){
...//叠加所有采样值并返回“总和/样本数”
}
...
}
getMean()并非轻量级操作,且不返回本类某个成员。不如叫它computeMean()更好,compute会引人联想该操作是不是稍为复杂一些,耗时一些。如果非要用getMean做名称的话,那么mean应被纳入缓存机制。例如:
privatebooleanmeanCached;//计算完样本后置为true,样本改动时置为false
...
publicdoublegetMean(){
if(!meanCached){
...//叠加所有采样值
mean=sampleSum/sampleCount;
meanCached=true;
}
returnmean;
}[code]
[/code]
voidShrinkList(list<Node>&list,intmax_size){
while(list.size()>max_size){
FreeNode(list.back());
list.pop_back();
}
}
size()操作的时间复杂度为O(1)应是大多数人的共识,可是恰恰list的size()是时间复杂度为O(n)的操作,这导致整个函数的复杂度变为O(n2)。按理说size()应该叫为countSize()或countElements(),以体现其重量级运算的特质来,不过,为了和其余容器类相符合,还是叫成size了。所幸新版C++规范强制要求size操作的时间复杂度为O(1)了(ARC书的作者这么说的,我未查证。大家帮忙在C++11规范中查证此事。原有规范只是“建议”它应具有常数时间复杂度,并未强制)。
小翔以为,如果某个抽象接口定义了一个貌似轻量级的简单操作,如Collection的size(),则子类对象在实现时应该尽量降低时间复杂度。实在不能时甚至可以考虑抛出异常或对客户提出警告。根本的解决办法还是学习C++规范那样,给出一个建议的时间复杂度来。
7.在多个候选名称中取舍时应该仔细质询其可能带来的歧义。
例如有两份相似的服务器配置参数文件:
config_id:100
description:"increasefontsizeto14pt"
traffic_fraction:5%
...
config_id:101
description:"increasefontsizeto13pt"
[其余参数与前一份相同]
我们现在想通过某个机制复用整套参数,例如这样:
config_id:101
想要复用的配置文件id:100
[其余参数与前一份相同]
那么,这个“想要复用的配置文件id”,应该怎么起名呢?备选关键词有:template、reuse、copy和inherit。
template很模糊:“template:100”到底是说自己是一份名叫“100”模板,还是说使用一个名叫“100”的模板作为其基础参数?况且模板这个概念太过抽象,给人感觉需要以具体内容填充它。
“reuse:100”到底是说这份参数最多可以使用100次,还是说复用名为“100”的那份配置文件中的参数?
“copy:100”是第100份拷贝吗?还是说拷贝自编号为“100”的那套配置?后者不如叫copy_config_from更好。
“inherit:100”,inherit这个词,大多数程序员很熟悉,且与日常生活的“财产继承”概念可相比拟,所以引起的误解相对较少。可以扩充为inherit_config_from来更精确地阐明这个意思。
综上,copy_config_from或inherit_config_from应为最终中选名称。
总之,好的标识符名称可以尽量消除代码阅读者的误解,提高代码可读性与可维护性,亦能促进业务交流。所以应当仔细考究,尽量选取免于误会的名称,尤其是遇到“filter、length和limit”这些模棱两可的词语时。此外,区间与上下限含不含端点、boolean类型的标识符会不会引起误解、方法名称所隐含的意义是否符合常识,这些问题也应该在起名时反覆考量。
用了两篇文章才讲完给标识符起名的事情,可见其的确关乎代码质量的提升。下一篇我们谈谈代码的排版问题。
爱飞翔2012年6月4日
本文使用CreativeCommonsBY-NC-ND3.0协议(创作共用自由转载-保持署名-非商业使用-禁止衍生)发布。
本文网址:
相关文章推荐
- 代码质量随想录(三):名字好,误会少
- 代码质量随想录(四)排版,不只是为了漂亮 推荐
- 代码质量随想录(五)注得多不如注得巧 推荐
- Oracle算老几?敢用甲骨文这个名字自居? 推荐
- 推荐几组漂亮的按纽CSS(名字自己定义)
- 推荐美文——《念你们的名字》
- 【推荐】科研随想录
- 随想录(推荐《自制编译器》这本书)
- 如何获得数据库里所有表的名字 推荐
- 随想录(推荐『步步惊"芯" - 软核处理器内部设计分析』这本书)
- 内幕:我曾在赛迪网工作得很开心! 推荐
- ASP.NET技术内幕[强烈推荐]
- 超级推荐!!值得收藏的黑客系列书:《黑客攻防实战xx》系列图书简介,一共4本
- POJ推荐50题
- 推荐30个国外的精美字体下载网站
- 最老程序员创业札记:全文检索、数据挖掘、推荐引擎应用35
- 国外程序员推荐:每个程序员都应读的书
- 数据服务系统的定位 推荐
- 推荐免费下载华软源码430套大型企业管理源码,下载地址:http://www.hur.cn/tg/linkin.asp?linkid=205389 源码语言:PB/Delphi/VB/Java/.Ne