[代码大全读书笔记]如何定义一个好的变量名

在平常编程中最烦恼的就是给变量取名,一段好代码,既要能完美地运行,还要能较容易地维护。这就意味着须要让往后维护代码的人能很快地看懂你的代码,并且,在团队合做中,其余开发者也会常常阅读你那部分代码。若是你的代码中充满了a,b,c,a1,a2,a3...那简直就是一个噩梦。所以,好的变量名很是重要。程序员

选择好变量名的注意事项

变量和变量名本质上是同一件事物,所以,变量的好与坏就在很大程度上取决于它的命名的好与坏。编程

下面举一个糟糕命名的例子数组

$pp = ($cp > 1) ? ($cp - 1) : $cp;
$np = ($cp < $tp) ? ($cp + 1) : $tp;

$p = new P($pp, $cp, $np, $tp);

这段代码在作什么呢?也许能够大概知道是在计算一些信息,可是,计算的是什么信息呢?$p,$pp,np,$tp等等这些变量表明的是什么呢?讲真,若是没有任何注释,任何人都没法看不懂这段代码想表达的意思。若是写这段代码的人告诉你,这段代码是在计算分页信息,而后实例化一个分页类,那么你应该如何命名呢?app

下面是这段代码的另外一种写法,看起来更加清晰:

$prev_page_num = ($curr_page_num > 1) ? ($curr_page_num - 1) : $curr_page_num);
$next_page_num = ($next_page_num < $total_page_num) ? ($curr_page_num + 1) : $total_page_num);

$page = new Page($prev_page_num, $curr_page_num, $next_page_num, $total_page_num);

从上面两段代码能够看出,一个好的变量名在可读性和可维护性上是极其重要的。并且好的变量名是易记的。能够经过应用多条原则来实现这些目标。编程语言

最重要的命名注意事项

  • 名字要彻底、准确地描述出该变量所表明的事物学习

  • 用名字表达变量所表明的是什么,不包含晦涩的缩写,同时也没有歧义测试

下表给出一些变量名称的例子,其中有好的也有差的。编码

变量用途 好名字,好描述 坏名字,差描述
到期的支票累计额 runningTotal,checkTotal written,ct,checks,CHKTTL,x,x1,x2
高速列车的运行速度 velocity,trainVelocity,velocityInMph velt,v,tv,x,x1,x2,train
当前日期 currentDate,todaysDate cd,current,c,x,x1,x2,date
每页的行数 linesPerPage lpp,lines,l,x,x1,x2

currentDate和todaysDate都是很好的名字,由于它们都彻底并且准确地描述出了“当前日期”这一律念。指针

cd和c是很糟糕的命名,由于它们用了过短的缩写,并且又不具备描述性。调试

current也很糟,由于它并无告诉你是当前什么。

date看上去不错,但通过最后推敲它也只是个坏名字,由于这里所说的日期并非全部的日期都可,而只是特指当前日期,而date自己并未表达出这层含义。

x,x1和x2永远都是坏名字--传统上用x表明一个未知量,若是不但愿你的变量所表明的是一个未知量,那么请考虑取一个更好的名字吧。

名字应该尽量地明确。像x、temp、i这些名字都泛泛可得能够用于多种目的,它们并无像应该的那样提供足够信息,所以一般是命名上的败笔。

以问题为导向

一个好记的名字反映的一般都是问题,而不是解决方案。即,一个好名字一般表达的是什么(What),而不是怎么样(How)。一般来讲,若是一个名字反映了计算机的某些方面而不是问题自己,那么它反映的就是“How”而非“What”了。

好比,考虑下面这两个变量命名:inputRec和employeeData。inputRec是一个反映输入、记录这些计算概念的计算机术语。employeeData则直指问题领域,与计算机无关。
相似地,printerReady比bitFlag更能表达打印机的状态;在财务软件里,calcVar比sum来得更准确。

最适当的名字长度

经研究发现,变量名的平均长度在10到16个字符的时候,调试花的力气是最小的。平均名字长度在8到20隔字符的程序也几乎一样容易调试。这并不意味着你的变量名必定要在8到20个字符,它强调的是,若是你查看本身写的代码时发现了不少更短的名字,那么你就须要认真检查,确保这些名字含义足够清晰。

下面展现变量名太长、过短或恰好的示例:

太长 : numberOfPeopleOnTheUsOlympicTeam; numberOfSeatsInTheStadium; maximumNumberOfPointsInModernOlympics

过短 : n, np, ntm; n, ms, nsisd; m, mp, max, points

正好 : numTeamMembers, teamMemberCount; numSeatsInStadium, seatCount; teamPointsMax, pointsRecord

变量名中的计算值限定词

不少程序都有表示计算结果的变量:总额、平均值、最大值,等等。若是你要用相似Total、Sum、Average、Max、Min、Record、String、Pointer这样的限定词来修改某个名字,那么请记住把限定词加到名字的最后。

这种方法的优势:

  • 变量名中最重要的那部分,即为这一变量赋予主要含义的部分应当位于最前面,这样,这一部分就能够显得最为突出,并会被首先阅读到;

  • 避免了因为同时在程序中使用totalRevenue和revenueTotal而产生的歧义

  • 使用统一的编码规范能够提升可读性,简化维护工做。好比,revenueTotal、expenseTotal、revenueAverage、expenseAverage这组名字具备很是优雅的对称性。而totalRevenue、expenseTotal、revenueAverage、averageRevenue这组名字中则看不出什么规律来。

这条规则也有例外,那就是Num的限定词的位置已是约定俗成的。Num放在变量名的开始位置表明一个总数,好比:numCustomers表示员工的总数。Num放在变量名的结束位置表明一个下标:customerNum表示的是当前员工的序号。这样使用Num经常会带来麻烦,所以,最好的方法是避开这些问题,使用Count或者Total来表明总数,使用Index来指代某个特定的员工。这样,customerCount就表明员工的总数,customerIndex表明某个特定的员工。

变量名中的经常使用对仗词

对仗词要使用正确,否则会产生歧义。

经常使用对仗词以下:

  • begin/end

  • first/last

  • locked/unlocked

  • min/max

  • next/previous

  • old/new

  • opened/closed

  • visible/invisible

  • source/target

  • source/destination

  • up/down

为特定类型的数据命名

为变量命名,除了一般的考虑事项以外,为一些特定类型数据的命名还要求做出一些特殊的考虑。好比,循环变量、状态变量、临时变量等等。

为循环下标命名

在循环中,最多见的下标变量就是i,j,k,如:

for(i = 0; i < arrLen; i++) {
    // ...
}

若是循环下标变量只在循环内部使用,那么如此使用是没问题的,可是,若是该变量须要在循环以外使用,那么就应该为它取一个比i,j,k更有意义的名字。举个栗子,若是你从文件中读取记录,而且须要记下所读取记录的数量,那么相似于redcordCount这样的名字就更合适:

recordCount = 0;
while ( moreScores() ) {
    score[recordCount] = GetNextScore();
    recordCount++;
}

// using recordCount

另外一种状况就是嵌套循环,比较常犯的错误就是在想写j的时候写了i,想用i的时候却写了j。

若是你使用了多个嵌套的循环,那么就应该给循环变量赋予更长的名字以提升可读性:

for ( teamIndex = 0; teamIndex < teamCount; teamIndex++) {
    for ( eventIndex = 0; eventIndex < eventCount[teamIndex]; eventIndex++) {
        score[teamIndex][eventIndex] = 0;
    }
}

scoreteamIndex 比 scorei给出的信息更多。

注意:若是你必定要用i、j、k,那么不要把它们用于简单循环的循环下标以外的任何场合,避免形成误解。要想避免这种问题,最简单的方法就是使用更好的命名而不是i,j,k。

为状态变量命名

为状态变量取一个比flag更好的名字。

最好是把标记看做是状态变量。标记的名字中不该该含有flag,由于你从中丝毫看不出该标记是作什么的。

为清楚可见,标记应该使用枚举变量、具名常量,或用做具名常量的全局变量来对其赋值。

看看下面比较差的标记命名:

if ( flag ) ...
if ( statusFlag & 0x0F ) ...
if ( printFlag == 16 ) ...
if ( computeFlag == 0 ) ...

flag = 0x1;
statusFlag = 0x80;
printFlag = 16;
computeFlag = 0;

上面这段代码反映不出能作什么,若是没有文档,不知道statusFlag = 0x80的含义是什么。下面是做用相同但更为清晰的代码:

if ( dataReady ) ...
if ( characterType & PRINTABLE_CHAR ) ...
if ( reportType == ReportTyoe_Annual ) ...
if ( recalcNeeded == false ) ...

dataReady = true;
characterType = CONTRAL_CHARACTER;
reportType = ReportType_Annual;
recalNeeded = false;

这段代码更加清晰。并且说明你能够结合枚举类型和预约义的具名常量来使用这种方法。

若是你发现本身须要猜想某段代码的含义的时候,就该考虑为变量从新命名。代码应该尽量直接读懂。

为临时变量命名

临时变量经常使用于存储计算的中间结果,做为临时占位符,以及存储内部值。它们常被赋予temp,tmp,x或者其余一些模糊且缺少描述性的名字。一般,临时变量是一个信号,代表程序缘尚未彻底把问题弄清楚。并且,因为这些变量被正式地赋予了一种“临时”状态,所以程序员会倾向于比其余变量更为随意地对待这些变量,从而增长了出错的可能。

警戒临时变量

临时地保存一些变量是颇有必要的。但不管从哪一种角度看,程序中的大多数变量都是临时性的。把其中几个称为临时的,可能代表你尚未弄清它们的实际用途。看看下面的示例:

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 );

discriminant,判别式

为布尔变量命名

典型的布尔变量名:

  • done

  • error

  • found

  • success/ok

给布尔变量赋予隐含“真/假”含义的名字。像done和success同样,它们的值不是true就是false,表示某件事情完成了或者没有完成;成功或者失败。另外一方面,想status这样的名字倒是很糟的布尔变量名,由于它们没有明确的true或者false。status是true反映的是什么含义呢?表示某件事情拥有一个状态吗?然而,每件事情都有状态。true代表某件事情的状态是OK吗?或者说false代表没有任何错误吗?对于status,你什么都说不出。

为了更好的效果,能够把status命名为error或者statusOK。

有时,也能够在布尔变量名前加上Is。这样,变量名就成了一个问题:isDone?isError?isFound?用true或false回答问题也就为该变量给出了取值。优势是不能用于那些模糊不清的名字,好比:isStatus?毫无心义。缺点就是下降了简单逻辑表达式的可读性:if(isFound)的可读性要略差于if(Found)。

使用确定的布尔变量名。避免双重否认:not notFound。

为枚举类型命名

在使用枚举类型的时候,能够经过使用组前缀,如Color_,Planet_或者Month_来明确标识该类型的成员都同属于一个组。好比:

Public Enum Color
    Color_Red
    Color_Green
    Color_Blue
End Enum

Public Enum Planet
    Planet_Earth
    Planet_Mars
    Planet_Venus
End Enum

在有些编程语言里,枚举类型的处理很像类,枚举类型也老是被冠以枚举名字前缀,好比Color.Color_Red或者Planet.Planet_Earth。若是你正在使用这样的编程语言,那么重复上述前缀的意义就不大了,能够简化为Color.Red和Planet.Earth。

为常量命名

在具名常量时,应该根据该常量所表示的含义,而不是该常量所具备的数值为该抽象事物命名。好比FIVE是个很糟糕的常量名,CYCLES_NEEDED是个不错的名字。

命名规则的力量

不少程序员会抵制标准和约定(有时我也会这样),而且有很好的理由:有些标准和约定很是刻板而且低效--它们会毁坏创造性和程序质量。

为何要有规则

  • 要求你更多地按规矩行事。集中精力关注代码更重要的特征;

  • 有助于在项目之间传递知识;

  • 有助于在新项目中更快速地学习代码;

  • 有助于减小名字增生,在没有规则下,很容易给同一个对象起两个不一样的名字;

  • 弥补编程语言的不足之处;

  • 强调相关变量之间的关系。

关键是,采用任何一项规则都要好于没有规则。规则多是武断的。命名规则的威力并不是来源于你所采起的某个特定规则,而是来源于如下事实:规则的存在为你的代码增长告终构,减小了你须要考虑的事情。

什么时候采用命名规则

  • 多个程序员合做开发一个项目时

  • 计划把一个程序转交给另外一位程序员来修改和维护的时候

  • 你所在组织中的其余程序员评估你写的程序的时候

  • 当你写的程序规模过大,以至于你没法在脑海里同时了解事情的全貌,而必须分而治之的时候

  • 你写的程序生命期足够长,长到你可能会在把它搁置几个星期或几个月以后又从新启动有关该程序的工做时

  • 当在一个项目中存在一些不常见的术语,而且你但愿在编写代码阶段使用标准的术语或缩写的时候

非正式命名规则

尽管上面介绍了不少比较标准的命名规则,可是大多数项目采用的都是相对非正式的命名规则。

与语言无关的命名规则的指导原则

  • 区分变量名和子程序名字

  • 区分类和对象

  • 标识全局变量

  • 标识成员变量

  • 标识类型声明

  • 标识具名常量

  • 标识枚举类型的元素

  • 在不能保证输入参数只读的语言里标识只读参数

  • 格式化命名以提升可读性

尽可能不要混用上述方法,那样会使代码更难阅读。老老实实地坚持使用其中任意一种提升可读性的方法,你的代码质量必定会有所改善。

与语言相关的命名规则的指导原则

应该遵循你所用语言的命名规则。对于大多数语言,你均可以找到描述其风格原则的参考书,下面给出C的指导原则。

C的命名规则

  • c和ch是字符变量

  • i和j是整数下标

  • n表示某物的数量

  • p是指针

  • s是字符串

  • 预处理宏所有大写,一般包括typedef

  • 变量名和子程序名所有小写

  • 下划线用做分隔符,如:letters_in_lowercase

标准前缀

对具备通用含义的前缀标准化,为数据命名提供了一种简洁、一致而且可读性好的方法。

标准化的前缀由两部分组成:用户自定义类型(UDT)的缩写和语义前缀。

用户自定义类型缩写

UDT缩写能够标识被命名对象或变量的数据类型。UDT缩写一般不会表示任何由编程语言所提供的预置数据类型。下面列出一份UDT示例。

UDT缩写 含义
ch 字符(Character)
doc 文档(Document)
pa 段落(Paragraph)
scr 屏幕区域(Screen region)
sel 选中范围(Selection)
wn 窗体(Window)

可使用上表列出的UDT类型定义下面这样的数据声明:

CH chCursorPosition;
SCR srcUserWorkSpace;
DOC docActive;
PA firstPaActiveDocument;
PA lastPaActiveDocument;
WN wnMain;

语义前缀

语义前缀比UDT更进一步,它描述了变量或者对象是如何使用的。并且语义前缀不会根据项目的不一样而不一样,对于不一样的项目均是标准的。下面列出一组标准的语义前缀。

语义前缀 含义
c 数量(count)
first 数组中须要处理的第一个元素
g 全局变量
i 数组的下标
last 数组中须要处理的最后一个元素,与first对应
lim 数组中须要处理的元素的上限,一般,lim等于last+1
m 类一级的变量
max 数组或其余种类的列表中绝对的最后一个元素
min 数组或其余种类的列表中绝对的第一个元素
p 指针(pointer)

标准前缀的优劣

  • 能更为精确地描述一些含义比较模糊的名字

  • 使名字变得更加紧凑

缺陷:程序员在使用前缀的同时忽略给变量其有意义的名字。

建立具有可读性的短名字

若是环境真的要求你建立简短的名字,请注意有些缩短名字的方法要好于其余的方法。

缩写的通常指导原则

下面列出几项用于建立缩写的指导原则。其中一些原则彼此冲突,因此不要试图同时应用全部的原则。

  • 使用标准的缩写

  • 去掉全部非前置元音(computer => cmptr, screen => scrn, apple => appl, interger => intgr)

  • 去掉虚词and,or,the等

  • 使用每一个单词的第一个或前几个字母

  • 统一在每一个单词的第1、第二或者第三个字母后截断

  • 保留每一个单词的第一个和最后一个字母

  • 使用名字中的每个重要单词,最多不超过三个

  • 去掉无用的后缀--ing,ed等

  • 保留每一个音节中最引人注意的发音

  • 确保不要改变变量的含义

  • 反复使用上述技术,直到把每一个变量名的长度缩减到了8-20个字符。

语音缩写

有些人倡导基于单词的发音而不是拼写来建立缩写,好比skating => sk8ing, before => b4...可是不提倡这么作。

有关缩写的评论

下面是一些可以用来避免犯错的规则

  • 不要用从每一个单词中删除一个字符的方式来缩写,要么删除不止一个字符,要么就把单词拼写完整

  • 缩写要一致,好比:要么所有使用Num,要么全用No,不要两个都用

  • 建立你能读出来到的名字,好比:用xPos而不用xPstn。能够借助电话来测试--若是你没法在电话中向他人读出你的代码,就请从新给变量起一个更清晰的名字吧

  • 避免使用容易看错或者读错的字符组合,好比ENDB和BEND,为了表示B的结尾,能够用一种好的分隔技术来命名:b_end/BEnd

  • 使用辞典来解决命名冲突,使用近义词来解决命名冲突

  • 在代码里用缩写对照表解释极短的名字的含义

应该避免的名字

  • 避免使用使人误解的名字或缩写,好比将"Fig and Almond Season"缩写为FALSE

  • 避免使用具备类似含义的名字

  • 避免使用具备不一样含义但却有类似名字的变量

  • 避免使用发音相近的名字

  • 避免在名字中使用数字

  • 避免在名字中拼错单词

  • 避免使用英语中经常拼错的单词

  • 不要仅靠大小写来区分变量名

  • 避免使用多种天然语言

  • 避免使用标准类型、变量和子程序的名字

  • 不要使用与变量含义彻底无关的名字

  • 避免在名字中包含易混淆的字符,好比1(数字1)和l(字母l),0(数字0)和O(字母O)

总结

好的变量名是提升程序可读性的一项关键要素。代码阅读的次数远远多于编写的次数,确保代码中所取的名字更侧重于阅读方便而不是编写方便。选择一种规则,并坚持遵循该规则。

原创文章,文笔有限,才疏学浅,文中如有不正之处,万望告知。

若是本文对你有帮助,请点下推荐吧,谢谢^_^

相关文章
相关标签/搜索