1.0 高质量代码的命名法则
1.1名副其实
名副其实说起来貌似很简单,但真正做起来,似乎没那么容易。选个好名字要花一些时间,但其实选好名字之后省下来的时间,要比之前选名字时花掉的时间多得多。
我们一旦发现有更好的名称时,就应该换掉之前的旧名称,这样做读你代码的人(包括你自己),都会很开心。
一个好的变量、函数或类的名称应该已经几乎答复了所有的大问题。它应该告诉你,这个名称所代表的内容,为什么会存在,做了什么事情,应该如何用等。
如果一个名称需要注释来补充才能让大家明白其真正含义,那其实就不算是名副其实。(并不是说不需要注释,恰如其分的注释是程序员让自己代码锦上添花的好方法。)
以下的这句代码里的d就不算是个好命名。名称d什么都没说,它没引起我们对时间消逝的感觉,更别说单位是天了:
int d; // elapsed time in days||经过了几天时间
我们应该选择这样的指明了计量对象和计量单位的名称:
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
1.2避免造成误导
我们应该避免留下隐藏代码本意的错误线索,也应该避免使用与本意相悖的词。例如,别用accountList来指一组账号,除非它真的是List类型,用accountGroup、bunchOfAccounts,或者直接用accounts,都是更好的选择。
尽量提防长得太像的名称。想区分XYZControllerForEfficientHandlingOfStrings和XYZControllerForEfficientStorageOfStrings,会花费我们太多的时间。因为这两个词,实在太相似了。
以同样的方式拼写出同样的概念才是信息,拼写前后不一致就是误导。
1.3尽量做有意义的区分
- 尽量避免使用数字系列命名(a1、a2…….aN)。这样的名称纯属误导,因为很多时候完全没有提供正确的信息,没有提供导向作者意图的线索。
- 废话是另一种没有意义的区分。如果我们有一个Product类,还有一个ProductInfo或ProductData类,那么他们的名称虽然不同,但意思却无区别。这里的Info、Data就像a、an和the一样,是意义含混的废话。
注意,只要体现出有意义的区分,使用a、the这样的前缀就没错。例如,将a用在域内变量,把the用于函数参数。
1.4尽量使用读得出来的名称
我们要使用读得出来的名称。如果名称读不出来,讨论的时候就会不方便且很尴尬,甚至让旁人觉得很蠢。
例如,变量名称是beeceearrthreecee,讨论的时候读起来简直像没吃药。
1.5尽量使用可搜索的名称
单字母和数字常量有个问题,就是很难再一大篇文字中找出来。
找MAX_CLASSED_PER_STUDENT很容易,但想找数字7,就很麻烦。
同样,字母e也不是个便于搜索的好变量名。因为作为英文中最常用的字母,在每个程序、每段代码中都有可能出现。
名称长短应与其作用域大小相对应,若变量或常量可能在代码中多处使用,应赋予其以便于搜索的名称。
举个栗子,比较如下两段代码:
for (int j=0; j<34; j++)
{
s += (t[j]*4)/5;
}
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j < NUMBER_OF_TASKS; j++)
{
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
按整洁代码的要求来评判,第一段代码会让读者不知所云,第二段代码比第一段好太多。第二段代码中,sum并非特别有用的名称,但至少他搜索得到。采用能表达意图的名称,貌似拉长了函数代码,但要想想看,WORK_DAYS_PER_WEEK要比数字5好找得多,而列表中也只剩下了体现我们意图的名称。
1.6 取名不要绕弯子
我们取名的时候要避免思维映射,不应当让读者在脑中把你的名称翻译为他们熟知的名称,也就是说取名不要绕弯子,而是要直白,直截了当。
在多数情况下,单字母不是个好的命名选择,除非是在作用域小、没有名称冲突的地方,比如循环。循环计数器自然有可能被命名为i,j或k(最好别用字母l),这是因为传统上我们惯用单字母名称做循环计数器。
程序员通常都是聪明人,聪明人有时会借助脑筋急转弯炫耀其聪明。而聪明程序员和专业程序员之间的区别在于,专业程序员了解,明确就是王道。专业的程序员善用其能,能编写出其他人能理解的代码。
1.7 类名尽量用名词
类名尽量用名词或名词短语,比如Customer, WikiPage,Account, 或 AddressParser。
类名最好不要是动词。
1.8 方法名尽量用动词
方法名尽量用动词或动词短语。比如postPayment, deletePage, 或者save。
属性访问器、修改器和断言应该根据其value来命名,并根据标准加上get、set和is前缀。
举个栗子,这里的getName、setName等命名都很OK:
string name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted())...
而重载构造器时,使用描述了参数的静态工厂方法名。如:
Complex fulcrumPoint =Complex.FromRealNumber(666.0);
通常好于:
Complex fulcrumPoint = new Complex(666.0);
我们也可以考虑将相应的构造器设置为private,强制使用这种命名手段。
1.9 每个概念对应一词,并一以贯之
我们需给每个概念选一个词,并且一以贯之。
例如,使用fetch、retrieve和get来给在多个类中的同种方法命名,你怎么记得住哪个类中是哪个方法呢?
同样,在同一堆代码中混用controller、manager,driver,就会令人困惑。DeviceManager和Protocol-Controller之间有何根本区别?为什么不全用controller或者manager?他们都是Driver吗?这就会让读者以为这两个对象是不同的类型,也分属不同的类。
所以,对于那些会用到你代码的程序员,一以贯之的命名法简直就是天降福音。
1.10 通俗易懂
我们应尽力写出易于理解的变量名,把代码写得让别人能一目了然,而不必让人去非常费力地去揣摩其含义。我们想要那种大众化的作者尽责地写清楚的通俗易懂的畅销书风格,而不是那种学者学院风的晦涩论文写作风格。
1.11 尽情使用解决方案领域专业术语
记住,只有程序员才会读你写的代码。所以,尽管去用那些计算机科学(Computer Science,CS)领域的专业术语、算法名、模式名、数学术语。
对于熟悉访问者(Visitor)模式的程序来说,名称AccountVisitor富有意义。给技术性的事物取个恰如其分的技术性名称,通常就是最靠谱的做法。
1.12 添加有意义的语境
很少有名称是可以自我说明的。所以,我们需要用有良好命名的类,函数或名称空间来放置名称,给读者提供语境。若没能提供放置的地方,还可以给名称添加前缀。
举个栗子,假如我们有名为firstName、lastName、street、houseNumber、city、state和zipcode的变量。当他们搁一块儿的时候,的确是构成了一个地址。不过,假如只是在某个方法中看到一个孤零零的state呢?我们会推断这个变量是地址的一部分吗?
我们可以添加前缀addrFirstName、addrLastName、addrState等,以此提供语境。至少,读者可以知道这些变量是某个更大变量的一部分。当然,更好的方案是创建名为Address的类。这样,即便是编译器也会知道这些变量是隶属于某个更大的概念了。
另外,只要短名称足够好,对含义的表达足够清除,就要比长名称更合适。添加有意义的语境甚好,别给名称添加不必要的语境。
2.0总结
- 要名副其实:一个好的变量、函数或类的名称应该已经答复了所有的大问题。一个好名称可以大概告诉你这个名称所代表的内容,为什么会存在,做了什么事情,应该如何用等。
- 要避免误导:我们应该避免留下隐藏代码本意的错误线索,也应该避免使用与本意相悖的词。
- 尽量做有意义的区分:尽量避免使用数字系列命名(a1、a2…….aN)和没有意义的区分。
- 尽量使用读得出来的名称:如名称读不出来,讨论的时候会不方便且很尴尬。
- 尽量使用可搜索的名称:名称长短应与其作用域大小相对应,若变量或常量可能在代码中多处使用,应赋予其以便于搜索的名称。
- 取名不要绕弯子:取名要直白,要直截了当,明确就是王道。
- 类名尽量用名词:类名尽量用名词或名词短语,最好不要是动词。
- 方法名尽量用动词:方法名尽量用动词或动词短语。
- 每个概念对应一词,并一以贯之:对于那些会用到你代码的程序员,一以贯之的命名法简直就是天降福音。
- 通俗易懂:应尽力写出易于理解的变量名,要把代码写得让别人能一目了然,而不必让人去非常费力地去揣摩其含义。
- 尽情使用解决方案领域专业术语:尽管去用那些计算机科学领域的专业术语、算法名、模式名、数学术语。
- 要添加有意义的语境:需要用有良好命名的类,函数或名称空间来放置名称,给读者提供语境。若没能提供放置的地方,还可以给名称添加前缀。