关于变量命名
忘了出处,但记得去年评选的03年年度十大程序员最头疼的事,排名第一的是:命名。咱本身略有一点代码洁癖,经常纠结于名字该如何取,更麻烦的是,咱对于一些特定的字符和单词十分不愿意把它们用到命名里,与技术无关,所以我比正常程序员在命名上还多一点麻烦,就是经常头疼于如何选择一个非常贴切的同义词。不夸张的说,我写代码所用的时间中,起个满意的名字占用了至少百分之三十,简直令人发指。
不过,由于轻微的代码洁癖,我觉得自己的代码有时候就算是因为时间妥协了一点,但终究还是过得去的。。。,直到,我读了《变量名的力量》,才发现原来咱还有一些命名问题的箭扎在膝盖上,或者知其然不知其所以然。
首先是准备阶段,在将要有变量需要被命名之前,需要有一个命名的规则。命名是为了让读程序的人更方便,但是哪怕所有的命名都非常好,也不一定会对读程序有帮助,这是因为命名的方式用很多种,每种方式都有好的命名实践,而它们并不相同,甚至放在一起会引起混乱。这就好像在某个高中,你要组一个足球队,选择了所有你认为踢球踢的好的童鞋,但他们各自的风格都很独特,短时间内很难做到配合默契,以至于成绩无法达到你的预期。当然,你可以花时间想办法调教的看起来很顺畅,但是这需要大量的精力和时间去磨合。
统一的命名规则可以带来的好处:
1.要求你更多的按规矩行事;
2.有助于在项目中传递知识;
3.有助于你在新项目中更快的学习代码;
4.有助于减少名字增生,避免多一个概念用了很多同义词表示;
5.弥补编程语言的不足,当编程语言的某方面无法满足你的要求时,可以制定统一的应对规则,比如你无法区分局部、全局变量时,可以通过统一规定前缀等方式解决;
6.强调相关变量之间的关系,比如address和phone两个变量是看不出直接关系的,但是起名为personAddress和personPhone就不一样了。
这些当然都是好的命名规则才能做到的,但是,哪怕是再不好的命名规则都要比没有规则强,因为规则将约定好的事项抽象出来,减少了复杂度,哪怕是很少的复杂度。
命名规则当然也有它适用的时机:多人合作、可能出现交接、代码规模很大和使用了不常见的术语等,这一切情况其实都说明了一个,就是你的代码需要被阅读的情况,如果代码一定需要被阅读,那采取一个命名规则就是很好的做法。至于规则的细致程度,需要综合考虑成本等等,不过规则既然是保证代码可读的,只要能保证工作范围内,命名可以被清晰识别就可以了,不一定总是需要规定的非常细致。
指导原则(和书上可能会有不同,因为书上是不分语言的,而且不符合我的习惯):
变量名以小写开头,后续单词首字母大写,既驼峰命名法;
对象类型和对象实例之间的区别。当对象为普通的某种对象时,比如一个Widget的实例就是一个普通的widget对象时,起名为genericWidget没有意义,不如用小写开头的widget(前提是编程语言支持),这种方法缺点是“心理距离”太短。另外一种方法是给变量前加"a"表示一个,例如aWidget;至于特定对象实例,只要架上特定词就好了,比如weatherWidget。
全局变量加上“g_”作为前缀,以表示此变量是全局变量,需要慎重对待,因为你不知道都什么地方动它了。
成员变量使用“_”作为前缀,稍微想那么一个下划线的时间,注意一下它。
常量大家都知道的,习惯于全部大写,以“_”分隔每个单词。
区分输入的参数是否允许在方法执行过程中被更改,当然,最好是不要改,因为更改后就不能在表示它是输入的参数了,但它偏偏就是那个参数,可以在方法开头使用局部变量来替代它之后的执行。标识这个输入参数是否只读,可以使用Attribue,如SetWidget([ReadOnly]Widget aWidget)。
使用i和j作为整数下标。
类和接口的首字母大写。
除用于全大写的名字外,不用“_”作为名字中间的分隔符。
访问器使用Get和Set作为动词。
变量名前缀:能对名字进行进一步的标准化,使程序中所需记忆的名字更少了;使一些含义模糊的概念更具体;提示程序员注意一些特定变量,如全局变量。缺点是分散程序员注意,导致对变量名意义在一定程度上的忽视。基于以上,变量名前缀除特定情况下,尽量不使用。
缩写:除非确定变量名命名正确,且长度已经影响到阅读代码(据说10~16个字符最佳),否则不要缩短变量名。必须缩短时,确保不要改变变量的含义;保留引人注意的发音;缩写要保持一致,一个概念一旦缩写则全部用该缩写;保证缩写后可以读出来;避免缩写后容易与其他概念混淆;缩写后建立统一文档,避免同一概念有不同的缩写。另外,尽量衡量好缩减变量名和变量名过长所引起的可读性障碍之间的平衡。
比较好的缩写方法:
1.使用标准(字典中常见的)或约定俗成的缩写;
2.去掉虚词and、or、the等;
3.使用名字中每一个重要的单词,做多不超过三个;
4.去掉无用的后缀---ing、ed等
使用变量名时尽量避免的情况:
1.避免使用相似的名字,造成看错,或者理解错,在大量代码中区分一个字符的区别有时候并不容易;
2.避免使用相似含义的名字,同一段代码中有两个近义词很容易导致读代码时理解错误;
3.避免使用发音相似的词,当两个程序员讨论一处代码时,很容易分不清;
4.尽量不在名字中使用数字,以免在变量名只有数字不同时,分不清变量之间的区别;
5.不要用仅靠大小写区分不同变量;
6.一个项目中不要使用多种自然语言命名;
命名:
上面说了变量名命名时写法格式,下面来说下该选择什么样内容的名字。
命名时最重要的事项是,名字要完全、准确的描述出所代表的事物。对变量的描述一般就是最好的变量名。变量名选用变量所代表的事物在自然语言中的说法,或者也可以直接查百科全书或者字典什么的,当然一定要是大家普遍知道的概念,名字不要太长。例如,当前日期命名为currentDate或todaysDate都很好,但是date就不好,因为没有反映出当前的意思。命名通常都是反映问题,而不是解决方案,这一点不只是变量,同样适用于方法等。除非是在命名关于计算机的变量,否则不要体现出计算机的相关特征,比如input、output或bit等。当你发现你需要猜测某段代码的含义的时候,就该考虑为变量重新命名了。
命名时同样需要考虑下变量的作用域。比如一个非常见得的循环,下标就可以用“i”,因为它只作用在几条语句上,甚至于只存在于循环头部起循环控制作用,一目了然。较长的名字适用于很少用到或全局的变量,而较短的名字适用于局部或循环变量。当然,不一定“i”作为循环变量就是好的,因为阅读代码的人很可能会忽略它,而有时候它代表某个概念,比如recordCount表示阅读记录数。另外,嵌套循环通常使用的i、j、k十分容易出现下边串话的问题,使用一个有意义的变量名更容易避免这类问题。
表示计算结果的变量,比如加减乘除总量合计这些,书上建议把限定词放到名字的后面。因为通常变量名核心的概念不是这些,而主要含义最好放到前面。另外可以避免出现类似totalRevenue和revenueTotal这样的歧义。同时,同类概念的不同计算方式也可以通过名称轻易看出来,因为主要的含义在前面,方便阅读。
对仗词有助于提高可读性:begin/end;locked/unlocked;next/previous;old/new;opened/closed;visible/invisible;source/target;source/destination;up/down等。
状态变量不要使用诸如status这样的单词,而应该说明状态的真正含义,比如dataReady,一般情况下状态的值是特定的,可以配合枚举来使用。
需要注意临时变量,因为大多数变量都是临时性的,仅仅把其中几个称为临时可能表明你还没弄清它们的实际用途,不要用“临时”这样的名字命名它们,临时工除外。
布尔类型的变量,如done、success、ok等。给布尔变量起隐含“真/假”含义的名字,反例:status无法表达真或者假,可以改成statusOK。我比较喜欢在布尔类型前加上is作为前缀,isDone?的问题答案正常就是是或者否。这种方法优点是它能轻易表示出真假的含义,isStatus表达的是否是一个状态,当然这通常是用不上的,dataReady isStatus...。使用肯定的变量名,否定的变量名notDone不是很好阅读,not notDone更难阅读。
最后,为了作个快乐的标题党,扯一点方法名的事:
1.描述方法所做的所有事情,方法名字通常是方法最好的注释;
2.避免使用无意义、模糊或表达不清的动词,比如HandleCalculation(),很多时候如果名字模糊不清,说明方法本身没有写好;
3.不要仅通过数字来形成不同的方法名;
4.给函数命名时要对返回值有所描述,printer.IsReady();
5.给过程命名时使用语气强烈的动词加宾语形式,如PrintCheck(),当这一方法是支票对象的动作时,可以省略支票:check.Print();
6.统一命名规则,比如Get,Set;
至于对仗词。。。,show/hide;open/close;start/stop;get/put;create/destroy什么什么的。。。