代码整洁之有意义的命名!

名副其实

一个好的变量、函数或类的名称应该已经几乎答复了所有的大问题

它应该告诉你,这个名称所代表的内容,为什么会存在,做了什么事情,应该如何用等

如果一个名称需要注释来补充才能让大家明白其真正含义,那其实就不算是名副其实

举个栗子:

以下的这句代码里的d就不算是个好命名

  • 名称d什么都没说,它没引起我们对时间消逝的感觉,更别说单位是天了
1
2
3
4
5
6
7
int d; // elapsed time in days||经过了几天时间
// 我们应该选择这样的指明了计量对象和计量单位的名称:

int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

避免造成误导

我们应该避免留下隐藏代码本意的错误线索,也应该避免使用与本意相悖的词

例如,别用accountList来指一组账号

  • 除非它真的是List类型,用accountGroup、bunchOfAccounts,或者直接用accounts,都是更好的选择

尽量提防长得太像的名称:

  • 想区分XYZControllerForEfficientHandlingOfStringsXYZControllerForEfficientStorageOfStrings

    • 会花费我们太多的时间
  • 因为这两个词,实在太相似了

以同样的方式拼写出同样的概念才是信息,拼写前后不一致就是误导

尽量做有意义的区分

尽量避免使用数字系列命名(a1、a2…….aN)

  • 这样的名称纯属误导,因为很多时候完全没有提供正确的信息,没有提供导向作者意图的线索

废话是另一种没有意义的区分:

  • 如果我们有一个Product类,还有一个ProductInfo或ProductData类,那么他们的名称虽然不同,但意思却无区别
  • 这里的Info、Data就像a、an和the一样,是意义含混的废话

注意,只要体现出有意义的区分,使用a、the这样的前缀就没错

  • 例如,将a用在域内变量,把the用于函数参数

尽量使用读得出来的名称

我们要使用读得出来的名称

如果名称读不出来,讨论的时候就会不方便且很尴尬,甚至让旁人觉得很蠢

  • 例如,变量名称是beeceearrthreecee,讨论的时候读起来简直像没吃药

尽量使用可搜索的名称

单字母和数字常量有个问题,就是很难再一大篇文字中找出来

  • MAX_CLASSED_PER_STUDENT很容易,但想找数字7,就很麻烦

同样,字母e也不是个便于搜索的好变量名。因为作为英文中最常用的字母,在每个程序、每段代码中都有可能出现

名称长短应与其作用域大小相对应,若变量或常量可能在代码中多处使用,应赋予其以便于搜索的名称

举个栗子,比较如下两段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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好找得多

  • 而列表中也只剩下了体现我们意图的名称

取名不要绕弯子

我们取名的时候要避免思维映射,不应当让读者在脑中把你的名称翻译为他们熟知的名称

  • 也就是说取名不要绕弯子,而是要直白,直截了当

在多数情况下,单字母不是个好的命名选择,除非是在作用域小、没有名称冲突的地方,比如循环

  • 循环计数器自然有可能被命名为i,j或k(最好别用字母l),这是因为传统上我们惯用单字母名称做循环计数器

程序员通常都是聪明人,聪明人有时会借助脑筋急转弯炫耀其聪明

而聪明程序员和专业程序员之间的区别在于,专业程序员了解,明确就是王道

专业的程序员善用其能,能编写出其他人能理解的代码

类名尽量用名词

类名尽量用名词或名词短语,比如Customer,WikiPage,Account 或 AddressParser

类名最好不要是动词

方法名尽量用动词

方法名尽量用动词或动词短语

  • 比如postPayment,deletePage或者save

属性访问器、修改器和断言应该根据其value来命名,并根据标准加上get、set和is前缀

举个栗子,这里的getName、setName等命名都很OK

1
2
3
string name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted())...

而重载构造器时,使用描述了参数的静态工厂方法名

1
Complex fulcrumPoint =Complex.FromRealNumber(666.0);

通常好于:

1
Complex fulcrumPoint = new Complex(666.0);

我们也可以考虑将相应的构造器设置为private,强制使用这种命名手段

每个概念对应一词,并一以贯之

我们需给每个概念选一个词,并且一以贯之

  • 例如,使用fetch、retrieve和get来给在多个类中的同种方法命名,你怎么记得住哪个类中是哪个方法呢?

同样,在同一堆代码中混用controller、manager,driver,就会令人困惑

DeviceManager和Protocol-Controller之间有何根本区别?

为什么不全用controller或者manager?他们都是Driver吗?

  • 这就会让读者以为这两个对象是不同的类型,也分属不同的类

所以,对于那些会用到你代码的程序员,一以贯之的命名法简直就是天降福音

通俗易懂

我们应尽力写出易于理解的变量名,把代码写得让别人能一目了然,而不必让人去非常费力地去揣摩其含义

我们想要那种大众化的作者尽责地写清楚的通俗易懂的畅销书风格

  • 而不是那种学者学院风的晦涩论文写作风格

尽情使用解决方案领域专业术语

记住,只有程序员才会读你写的代码

  • 所以,尽管去用那些计算机科学(Computer Science,CS)领域的专业术语、算法名、模式名、数学术语

对于熟悉访问者(Visitor)模式的程序来说,名称AccountVisitor富有意义

给技术性的事物取个恰如其分的技术性名称,通常就是最靠谱的做法

添加有意义的语境

很少有名称是可以自我说明的

  • 所以,我们需要用有良好命名的类,函数或名称空间来放置名称,给读者提供语境

若没能提供放置的地方,还可以给名称添加前缀

举个栗子,假如我们有名为firstName、lastName、street、houseNumber、city、state和zipcode的变量

  • 当他们搁一块儿的时候,的确是构成了一个地址

  • 不过,假如只是在某个方法中看到一个孤零零的state呢?

  • 我们会推断这个变量是地址的一部分吗?

我们可以添加前缀addrFirstName、addrLastName、addrState等,以此提供语境

  • 至少,读者可以知道这些变量是某个更大变量的一部分

当然,更好的方案是创建名为Address的类

这样,即便是编译器也会知道这些变量是隶属于某个更大的概念了

  • 另外,只要短名称足够好,对含义的表达足够清除,就要比长名称更合适

添加有意义的语境甚好,别给名称添加不必要的语境

避免使用编码

使用编码 是一个不好的选择,把类型或者作用域添加的变量名中,无疑给变量名添加了额外的信息,增加了解码的负担

1
string m_name;

应该避免这样的命名方式,下面python语言为例:

1
2
m_list;
arr_list;