变量名的力量(P259)
1.选择好变量名的注意事项
一个好的变量名是可读的、易记的和恰如其分的。
1)最重要的命名注意事项
为变量命名时最重要的考虑事项是,该名字要完全、准确地描述出该变量所代表的事物。获得好名字的一种实用技巧就是用文字表达变量所代表的是什么。通常,对变量的描述就是最佳的变量名。名字应该尽可能的明确。像x、temp、i这些名字都泛泛得可以用于多种目的,它们并没有像应该的那样提供足够信息,因此通常都是命名上的败笔。
2)以问题为导向
一个好记的名字反映的通常都是问题,而不是解决方案。
3)最适当的名字长度
太长 numberOfPeopleOnTheUsOlympicTeam
numberOfSeatsInTheStadium
maximumNumberOfPointsInModernOlympics
太短 n,np,ntm
n,ms,nsisd
m,mp,max,points
正好 numTeamMembers,teamMemberCount
numSeatsInStadium,seatCount
teamPointsMax,pointsRecord
4)作用域对变量名的影响
当你把一个变量名取得很短的时候,如i,这一长度本身就对该变量做出了一些说明——该变量代表的是一个临时的数据,它的作用域非常有限。
- 对位于全局命名空间中的名字加以限定词
在那些不支持命名空间或者包的语言里,可以为全局可见的类加上带有子系统特征的前缀。用户接口部分的雇员类可能命名为uiEmployee,数据库部分的雇员类可能命名为dbEmployee,这样做能把全局命名空间的命名冲突降到最低。
5)变量名中的计算值限定词
如果你要用类似于Total、Sum、Average、Max、Min、Record、String、Pointer这样的限定词来修改某个名字,那么请记住把限定词加到名字的最后。
6)变量名中的常用对仗词
- begin/end
- first/last
- locked/unlocked
- min/max
- next/previous
- old/new
- opened/closed
- visible/invisible
- source/target
- source/destination
- up/down
2.为特定类型的数据命名
1)为循环下标命名
对于一般的循环,可以用i、j和k这种约定俗成的名字:
但是对于长循环、嵌套循环,就很容易忘记i的意义,这么处理可以提高可读性:for( i = firstItem; i < lastItem; i++){ data[ i] = 0; }
for( teamIndex = 0; teamIndex < teamCount; teamIndex++){ for( eventIndex = 0; eventIndex < eventCount[teamIndex]; eventIndex++){ score[ teamIndex][ eventIndex] = 0; } }
2)为状态变量命名
为状态变量取一个比flag更好的名字。下面例子中标记的命名都很差:像statusFlag = 0x80这样的语句是反映不出这段代码能做什么的,除非是自己亲自写的,或者有文档告诉你。下面是作用相同但更为清晰的代码:if( flag) ... if( statusFlag & 0x0F) ... if( printFlag == 16) ... if( computeFlag == 0) ... flag = 0x1; statusFlag = 0x80; printFlag = 16; computeFlag = 0;
if( dataReady) ... if( characterType & PRINTABLE_CHAR) ... if( reportType == ReportType_Annual) ... if( recalcNeeded == false) ... dataReady = true; characterType = CONTROL_CHARACTER; reportType = ReportType_Annual; recalcNeeded = false;
如果你发现自己需要猜测某段代码的含义的时候,就该考虑为变量重新命名。
3)为临时变量命名
通常,临时变量是一个信号,表面程序员还没有完全把问题弄清楚。而且,由于这些变量被正式地赋予了一种“临时”状态,因此程序员会倾向于比其他变量更为随意地对待这些变量,从而增加了出错的可能。警惕“临时”变量:这样储存一个变量是很不错的,特别是当这一结果还会被随后两次用到的时候。但是名字temp却丝毫也没有反应该变量的功能。下面的例子显示了一种更好的做法:temp = sqrt( b^2 - 4*a*c); root[0] = ( -b + temp ) / ( 2 * a ); root[1] = ( -b + temp ) / ( 2 * a );
discriminant = sqrt( b^2 - 4*a*c); root[0] = ( -b + discriminant ) / ( 2 * a ); root[1] = ( -b + discriminant ) / ( 2 * a );
4)为布尔变量命名
下面是一些格外有用的布尔变量名:
- done
- error
- found
- success或ok
这样命名的重点在于给布尔变量赋予隐含“真/假”含义的名字,对于类似status这样的名字,你什么也说不出来。
5)为枚举类型命名
在使用枚举类型的时候,可以通过使用组前缀,如Color_等来明确表示该类型的成员都同属于一个组Public Enum Color Color_Red Color_Green Color_Blue End Enum Public Enum Planet Planet_Earth Planet_Mars Planet_Venus End Enum Public Enum Month Month_January Month_February ... Month_December End Enum
6)为常量命名
在具名常量时,应该根据该常量所表示的含义,而不是该常量所具有的数值为该抽象事物命名。比如FIVE = 6.0就显得太可笑了。
3.命名规则的力量
1)为什么要有规则
关键之处在于,采用任何一项规则都要好于没有规则。规则可能是武断的。命名规则的威力并非来源于你所采取的某个特定规则,而是来源与以下事实:规则的存在为你的代码增加了结构,减少了你需要考虑的事情。
2)何时采用命名规则
- 当多个程序员合作开发一个项目时
- 当你计划把一个程序转交给另一位程序员来修改和维护的时候
- 当你所在组织中的其他程序员评估你写的程序的时候
- ...
3)正式程度
对于微小的、用完即弃的项目而言,实施严格的规则可能就太没必要了。对于多人协作的大型项目而言,无论是在开始阶段还是贯穿整个程序的生命周期,正式规则都是成为提高可读性的必不可少的辅助手段。
4.非正式命名规则
- 区分变量名和子程序名字
本书所采用的命名规则要求变量名和对象名以小写字母开始,子程序以大写字母开始:variableName对RoutineName()。 - 区分类和对象
//方案一:通过大写字母开头区分类型和变量 Widget widget; LongerWidget longerWidget; //方案二:通过全部大写区分类型和变量 WIDGET widget; LONGERWIDGET longerWidget; //方案三:通过给类型加“t_”前缀区分类型和变量 t_Widget Widget; t_LongerWidget LongerWidget; //方案四:通过给变量加“a”前缀区分类型和变量 Widget aWidget; LongerWidget aLongerWidget; //方案五:通过对变量采用更明确的名字区分类型和变量 Widget employeeWidget; LongerWidget fullEmployeeWidget;
- 标识全局变量
在全局变量之前加上g_前缀,比如g_RunningTotal - 标识成员变量
在成员变量之前加上m_前缀 - 标识类型声明
C++的惯用方法是把类型名全部大写——例如COLOR和MENU。不过这样可会增加与命名预处理常量发生混淆的可能。为了避免这种情况,可以为类型名增加t_前缀,如t_Color和t_Menu。 - 标识具名常量
在VB里,具名常量可能是一个函数的返回值,可以给常量增加c_前缀区分。
在C++和Java里则全部用大写,以及有可能的话用下划线来分隔单词,如:RECS_MAX - 标识枚举类型的元素
- 在不能保证输入参数只读的语言里标识只读参数
如const,final,nonmodifiable等前缀 - 格式化命名以提高可读性
用大小写和下划线“_”分隔字符
1)与语言相关的命名规则的指导原则(P275)
- C的命名规则
- C++的命名规则
- Java的规则
- Visual Basic的命名规则
2)混合语言编程的注意事项
在混合语言环境中编程时,可以对命名规则作出优化以提高整体的一致性和可读性——即使意味着优化后的规则会与其中某种语言所用的规则相冲突。
5.标准前缀
1)用户自定义类型(UDT)缩写
UDT缩写 含义 ch 字符(Character,这里指的字符不是指C++中的字符,而是指文字处理程序可能用于表示一份文档中的字符的数据类型) doc 文档(Document) pa 段落(Paragraph) scr 屏幕区域(Screen region) sel 选中范围(Selection) wn 窗体(window)
当使用UDT时,应该像这样数据声明:
CH chCursorPosition
SCR scrUserWorkspace
DOC docActive
...
2)语义前缀
语义前缀比UDT更进一步,它描述了变量或者对象是如何使用的。
语义前缀 含义 c 数量(Count,如记录、字符或者其他东西的个数) first 数组中需要处理的第一个元素 g 全局变量 i 数组的下标 last 数组中需要处理的最后一个元素 lim 数组中需要处理的元素的上限(表示的是一个数组中并不存在的上界,即last+1) m 类一级的变量 max 数组或者其他种类列表中绝对的最后一个元素,反映的是数组本身 min 同上 p 指针(pointer)
一组段落的下标可以命名为iPa;cPa是相应的计数值,段落的总数量。
3)标准前缀的优点
除了具备命名规则所能提供的一般意义上的优点外,标准前缀还为你带来了另外一些好处。由于很多名字都已经标准化了,因此你在一个程序或者类内需要记忆的名字更少了。
6.创建具备可读性的短名字
从某种程度上说,要求使用短变量名是早期计算的遗留物。而在现代语言如C++、Java和VB里面,实际上你可以创建任何长度的名字,几乎没有任何理由去缩短具有丰富含义的名字。1)缩写的一般指导原则
其中的一些原则彼此冲突,所以不要试图同时应用所有的原则。
- 使用标准的缩写(列在字典中的那些常见缩写)
- 去掉所有非前置元音(computer变成cmptr,screen变成scrn,temp变成tmp)
- 去掉虚词and,or,the等
- 去除无用的后缀——ing,ed等
- 使用每个单词的第一个或前几个字母
- ...
2)语音缩写
有些人倡导基于单词的发音而不是拼写来创建缩写,于是skating就变成了sk8ing,before变成了b4,execute变成了xqt。太难猜,不提倡。
3)有关缩写的评论
- 不要用从每个单词删除一个字符的方式来缩写
- 缩写要一致
- 创建你能读出来的名字
- 避免使用容易看错或者读错的字符组合
- 使用辞典来解决命名冲突
- 在代码里用缩写对照表解释极短的名字的含义
- 在一份项目级的“标准缩写”文档中说明所有的缩写
本原则中体现出来的核心问题,是方便编写代码的同时方便阅读代码两种理念之间的差异。上面的方法很明显会带来代码编写时的麻烦,但是程序员们在整个项目生命周期里会把更多的时间花在阅读代码而不是编写代码之上。这种方法提高了阅读代码的方便性。名字对于代码读者的意义比对作者更重要。
7.应该避免的名字
- 避免使用令人误解的名字或缩写
- 避免使用具有相似含义的名字
- 避免使用具有不同含义但却有相似名字的变量
- 避免使用发音相近的名字
- 避免在名字中使用数字
- 避免在名字中拼错单词
- 避免实用英语中常常拼错的单词
- 不要仅靠大小写来区分变量名
- 避免使用多种自然语言
- 避免使用标准类型、变量和子程序的名字
- 不要使用与变量含义完全无关的名字
- 避免在名字中包含易混淆的字符