读《代码大全2》笔记:变量的力量

变量名的力量

11.1选择好变量名的注意事项

       你可不能像给狗取名字那样给变量命名----仅仅因为它很可爱或者听上去不错。狗和狗的名字不一样,它们是不同的东西,变量和变量名就本质而言却是同一事物。这样一来,变量的好与坏就在很大程度上取决于它的命名的好坏。在给变量命名的时候需要小心谨慎。

 

最重要的命名注意事项

       为变量命名时最重要的考虑事项是,该名字要完全,准确地描述出该变量所代表的事物。获得的好名字的一种实用技巧就是用文字表达所代表的是什么。通常,对变量的描述就是最佳的变量名。这种名字很容易阅读,因为其中并不包含晦涩的缩写,同时也没有歧义。因为它是对该事物的完整描述,因此不会和其它事物混淆。另外,由于这一名字与所表达的概念相似,因此也容易记忆。

      

       名字应该尽可能地明确

以问题为向导

       一个好记的名字反映的通常都是问题,而不是解决方案。一个好名字通常表达的是“什么”(what),而不是“如何”(how)。一般而言,如果一个名字反映了计算机的某方面而不是问题本身,那么它反映的就是“how”而非“what”了。请避免选取这样的名字,而应该在名字中反映出问题本身。

 

最适当的名字长度

       太短的名字无法传达足够的信息。诸如x1和x2这样的名字所存在的问题是,即使你知道了x代表什么,你也无法获知x1和x2之间的关系。太长的名字很难写,同时也会使程序的视觉结构变得模糊不清。

Gorla、Benander 和Benander发现,当变量名的平均长度在10到16个字符的时候,调试程序需要花费的力气使最小的(1990)。平均名字长度在8到20个字符的程序也几乎同样容易调试。这项原则并不意味着你应该尽可能把变量名控制在9到15或者10到16 个字符长。它更强调的是,如果你查看自己写的代码时发现了很多更短的名字,那么你需要认真检查,确保这些名字的含义足够清晰。

你可能几经通过Goldilocks-and-the-Three-Bears(金发姑娘喻三只小熊的经典通话,予以权衡比较 )的方法理解了如何为变量命名,正如表 11-2所示。

表11-2 变更量名太长、太短或刚好合适的示例

 

太长:

numberOfPeopleOnTheUsOlympicTeam

numberOfSeatsInTheStadium

maximunNumberOfPointsInModernOlympics

太短:

n,np,ntm

N,ms,nsisd

M,mp,max,points

正好:

numTeamMembers,teamMemberCount

numSeatInStadium,seatCount

teamPointsMax,pointsRecord

 

变量名对作用域的影响

当你把一个变更名取得很短的时候,如i,这一长度本身就对该变量做出了一些说明——也就是说,该变量代表的是一个临时的数据,它的作用域非常有限。

阅读该变量的程序员应该也会明白,这一数值只会用于几行代码之内。当你把变量名命名为 i的时候,你就是在表示,“这是一个普通的循环技术七或者数组下标,在这几行代码之外它没有任何作用。”

较长的名字适用于到的变量或者全局便变量,而较短的名字则适用于局部变量或者循环变量(Shneiderman 1980)。不过,短的变量常常会带来一些麻烦,因此,作为一项防御式编程策略,一些细心的程序员会避免使用短的变量名。

   对位于全局命名空间中的名字加以限定词    如果你在全局中定义了一些变量(具名常量,类名等),那么请考虑你是否需要采用某种方式对全局命名空间进行划分,并避免产生命名冲突。

  在那些不支持命名空间或者包的语言里,你同样也可以使用命名规则来划分全局命名空间。其中一项规则要求为全局可见的类加上带有子系统特征的前缀。用户接口部分的雇员类可能命名为uiEmployee,数据库部分的雇员类可能命名为dbEmployee,这样做能把全局命名空间的命名冲突降到最低。

变量名中的计算值限定词

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

  这种方法具有很多优点。首先,变量名中最重要的那部分,即为这一变量赋予主要含义的部分应当位于最前面,这样,这一部分就可以显得最为突出,并会被首先阅读到。其次,采纳这一规则,你将避免由于同时在程序中使用totalRevenue和revenueTotal而产生的歧义。这些名字在语义上示等价的,上述规则可以避免将它们当作不同的东西使用。还有,类似revenueTotal(总收入)、expenseTotal(总支出)、revenueAverage(评价收入)、expenseAverage(平均支出)这组名字的变量具有非常优雅的对称性。一致性可以提高可读性,简化维护工作。

   把计算的量放在名字最后的这条规则也有例外,那就是Num限定词的位置已经是约定俗成的。Num放在变量的开始位置代表一个总数:numCustomers表示的是员工的总数。Num放在变量名的结束位置代表一个下标:customerNum表示的是当前员工的序号。通过numCustomers最后代表复数的s也能够看出这两种应用之间的区别。然而,由于这样使用Num常常会带来麻烦,因此可能最好的办法是避开这些问题,用Count或者Total来代表员工的总数,用Index来指代某个特定的员工。这样,customerCount就代表员工的总数,customerIndex代表某个特定的员工。

变量名中的常用对仗词

对仗词的使用要准确。通过应用命名规则来提高对仗词使用的一致性,从而提高其可读性。比如像begin/end这样的一组用词非常容易理解和记忆。而那些与常用语言相去甚远的词则通常很难记忆,有时甚至会产生歧义。下面是一些常用的对仗词:

l begin/end

l first/last

l locked/unlocked

l min/max

l next/previous

l old/new

l opened/closed

l visible/invisible

l source/target

l source/destination

l up/down

 

11.2 为特定类型的数据命名

  在为数据命名的时候,除了通常的考虑事项外,为一些特定类型的命名还要求做出一些特殊的考虑。

为循环下标命名

循环是一种极为常见的计算机编程特征,微循环中的变量进行命名的原则也由此应运而生。i,j和k这些名字都是约定俗成的:

 

Java示例:简单的循环变量名

For (i=firstItem;i<lastItem;i++){

Data[i]=0;

}

如果一个变量要在循环之外使用,那么就应该为它取一个比i、j或者k更有意义的名字。举个例子,如果你在从文件中读取记录,并且需要记下所读取记录的数量,那么类似于recordCount这样的名字就很合适:

Java示例:描述性较好的循环变量名

recordCount=0;

while(moreScores()){

score[recordCount]=GetNextScore();

recordCount++;

}

 

//lines using recordCount

如果循环不是只有几行,那么读者会很容易忘记i本来具有的含义,因此你最好给循环下标换一个更有意义的名字。由于代码会经常修改、扩充,或者复制到其他程序中去,因此,很多有经验的程学员索性不使用类似于i这样的名字。

导致循环变长的常见原因之一是出现循环的嵌套使用。如果你使用了多个嵌套的循环,那么就应该给循环变量赋予更长的名字以提高可读性:

Java示例:嵌套循环中的好循环变量名

for(teamIndex=0; teamIndex<teamCount;teamIndex++){

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

  score[teamIndex][eventIndex]=0;

}

}

谨慎地为循环下标变量命名可以避免产生下标串话(indexcorss-talk)的常见问题:想用j的时候写了i,想用i的时候却写了j。同时,这也使得数据访问变得更加清晰:score[teamIndex][eventIndex]要比score[i][j]给出的信息更多。

如果你一定要用i、j和k,那么不要把它们用于简单循环的循环下标之外的任何场合——这种传统已经太深入人心了,一旦违背该原则,将这些变量用于其他用途就可能造成误解。要想避免出现这样的问题,最简单的方法就是想出一个比i、j和k更具描述性的名字来。

为状态变量命名

状态变量用于描述你的程序的状态。下面给出它的命名原则。

为状态变量取一个比flag更好的名字  最好是把标记(flag)看作状态变量。标记的名字中不应该含有flag,应为你从中丝毫看不出该标记是做什么的。为了清楚起见,标记应该用枚举类型、具名常量,或用作具名常量的全局变量来对其赋值,而且其值应该与上面这些量做比较。

       如果你发现自己需要猜测某段代码的含义的时候,就该考虑为变量重新命名。猜测谋杀案中谁是神秘凶手时可行的,但你没有必要去猜测代码。你应该能直接读懂它们。

 

为临时变量命名

       临时变量用于储存计算中的中间结果,作为临时占位符,以及存储内务管理(housekeeping)值。它们常被赋予temp、x或者其他一些模糊且缺乏描述性的名字。通常,临时变量时一个信号,表明程序员海没有完全把问题弄清楚。而且,由于这些变量被正式地赋予了一种“临时”状态,因此程序员会倾向于比其他变量更为随意地对待这些变量,从而增加了出错地可能。

       警惕“临时”变量  临时性地保存一些值常常是很有必要的。但无论从哪种角度看,你程序中的大多数变量都是临时性的。把其中几个称为临时的,可能表明你还没有弄清楚它们的实际用途。

      

为布尔变量命名

下面是为布尔变量命名时要遵循的几条原则。

谨记典型的布尔变量名  下面时一些格外有用的布尔变量名。

l done  用done表示某件事情已经完成。这一变量可用于表示循环结束或者一些其他的操作已完成。在事情完成之前把done设为false,在事情完成之后把它设为true。

l error 用error表示有错误发生。在错误发生之前把变量值设为false,在错误已经发生时把它设为true。

l found  用found来表明某个值已经找到了。在还没有找到该值的时候把found设为false,一旦找到该值就把found设为true。在一个数组中查找某个值,在文件中搜寻某员工的ID,在一叠支票中寻找某张特定金额的支票等等的时候,都可以用found。

l success或ok 用success或ok来表明一项操作是否称工。在操作失败的时候把该变量设为false,在操作成功的时候把其设为true。如果可以,请用一个更具体的名字来代替success,以便更具体的描述成功的含义。如果完成处理就表明这个程序执行成功,那么或许你应该用processingComplete来取而代之。如果找到某个值就是程序执行成功,那么你也许应该换用found。

 

给布尔变量赋予隐含“真/假”含义的名字  像done和success这样的名字时很不错的布尔变量名,因为其状态要么是true,要么是false;某件事情完成了或者没有完成;成功或者失败。另一方面,像status和sourceFile这样的名字却是很糟的布尔变量名,因为它们没有明确的true或者false。status是true反映的是什么含义?它表明某件事情拥有一个状态吗?对于status这样的名字,你什么也说不出来。

       为了取得更好的效果,应该把status替换为类似于error或者statusOK这样的名字,同时把sourceFile替换为sourceFileAvailabl、sourceFileFound,或者其他能体现该变量所代表含义的名字。

       有些程序员喜欢在它们写的布尔变量名前加上Is。这样,变量名就编程了一个问题:isdone? isError?isFound? isProcessingComplete? 用true或false回答问题也就为该变量给出了取值。这种方法的优点之一是它不能用于那些模糊不清的名字。isStatus? 这毫无意义。它的缺点之一是降低了简单逻辑表达式的可读性:if ( isFound )的可读性要略差于if( found )。

       使用肯定的布尔变量名  否定的名字如notFound、notdone以及notSuccessful等较难阅读,特别是如果它们被求反:

if not notfound

       这样的名字应该替换为found、done或者processingComplete,然后再用适当的运算符求反。如果你找到了想找到的结果,那么就可以用found而不必写双重否定的not notFound了。

为枚举类型命名

       在使用枚举类型的时候,可以通过使用组前缀,如Color_,Planet_或者Month_来明确表示该类型的成员都同属于一个组。

为常量命名

在具名常量时,应该根据常量所表示的含义,而不是该常量所具有的数据为该抽象事物命名。FIVE是个很糟的常量名(不论他说代表的值是不是5.0)。CYCLES_NEEDED是个不错的名字。CYCLES_NEEDED可以等于5.0或6.0。而FIVE=6.0就太可笑了。出于同样的原因,BAKERS_DOZEN就是个很糟的常量名;而DONUTS_MAX则很不错。

11.3命名规则的力量

有些程序员会抵制标准和约定(convention,规则)——并且有很好的理由:有些标准和约定非常刻板并且低效——他们会毁坏创造性和程序质量。这真让人感到遗憾,因为有效的标准是你所能掌握的最强大的工具之一。

为什么要有规则

命名规则可以带来以下好处:

l 要求你更多的按规矩行事。通过做一项全局决策而不是做许多局部决策,你可以集中精力关注代码更重要的特征。

l 有助于项目之间传递知识。名字的相似性能让你更容易更自信的理解那些不熟悉的变量原本应该是做什么的。

l 有助于在新项目中更快速的学习代码。你无需了解Anita写的代码是这样的,Julia写的代码是那样的,以及Kristin的代码又是另一个样子,而只需面对一组更加一致的代码。

l 有助于减少名字增生(name proliferation)。在没有名字规则的情况下,会很容易得给同一个对象起两个不同的名字。例如,你可能会把总点数即称为pointTotal,也称为totalPoints。在写代码的时候可能并不会让你感到迷惑,但是它却会让一位日后阅读这段代码的新程序员赶到及其困惑。

l 弥补编程语言的不足之处。你可以用规则来仿效具命常量和枚举类型。规则可以根据局部数据、类数据以及全局数据的不同而有所差别,并且可以包含编译器不直接提供的类型信息。

l 强调相关变量之间的关系。如果你使用对象,则编译器会自动照料它们。如果你用的编程语言不支持对象,你可以用命名规则予以补充。诸如address、phone以及name这样的名字并不能表明这些变量是否相关。但employeeAddress、employeePhone和employeeName就会毫无疑问的表明这些变量是相互相关的。编程的命名规则可以对你所用的编程语言的不足之处做出弥补

关键之处在于,采用任何一项规则都要好于没有规则。规则可能是武断的。命名规则的威力并非来源于你所采取的某个特定规则,而是来源于以下事实:规则的存在为你的代码增加了结构,减少了你需要考虑的事情。

何时采用命名规则

没有金科玉律表明何时应该建立命名规则,但是在下列情况下会则是很有价值的。

l 当多个程序员合作开发一个项目时。

l 当你计划把一个程序转交给另一位程序员来修改和维护的时候(这几乎总是会发生)

l 当你所在组织中的其他程序员评估你写的程序的时候

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

l 当你写的程序生命期足够长,长到你可能会把它搁置几个星期或几个月之后又重新启动有关该程序的工作的时

l 当在一个项目中存在一些不常见的术语,并且你希望在编写代码阶段使用标准的术语或者缩写的时候

你一定会因使用了某种命名的规则而受益。上述诸多注意事项将会帮助你解决在一个特定项目中按照何种程度来制定规则里所使用的规则的范围。

正式程度

不同规则所要求的正式程度也会有所不同。一个非正式的规则可能会像“使用有意义的名字”这样简单。通常,你所需的正式程度取决于为同一程序而工作的人员数量、程序的规模,以及程序预期的生命期。对于微笑的、用完即弃的程序而言,实施严格的规则可能就太没有必要了。

对于多人写作的大型项目而言,无论是在开始阶段还是贯穿整个程序的生命周期,正是规则都是成为提高可读性的必不可少的辅助手段。

11.4非正式命名规则

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

下面给出用于创建一种与语言无关的命名规则的指导原则

区分变量和子程序名字  本书所采用命名规则要求变量名和对象以小写字母开始,子程序名字以大写字母开始:variableName对RoutineName().

区分类和对象  类名字与对象名字——或者类型与该类型的变量——之间的关系会比较棘手。有很多标准的方案可用,如下例所示:

        方案1:通过大写字母开头区分类型和变量

        Widget widget;

        LongerWidget  longWidget

      

        方案2:通过全部大写区分类型和变量

        WIDGET widget;

        LONGERWidget longerWidget;

 

        方案3:通过给类型加“t_”前缀区分类型和变量

        t_WidgetWidget;

        t_LongetWidget aLongerWidget

       

方案4:通过给变量加“a”前缀区分类型和变量

       Widget aWidget

       LongerWidget aLongetWidget;

 

        方案5:通过对变量采用更明确的名字区分类型和变量

        Widget employeeWidget

        LongetWidget fullEmmplyeeWidget

 

每一种方案都有其优点和不足。第一种方案是在大小写敏感语言如C++和JAVA里常用的规则,但是有些程序对公依靠大写区分名字感到不在舒服。的确,创建两个只有第一个字母大小写不同的名字所能提供的“心理距离”太短了,二者之间的视觉差异也太小。

   在多语言混合编程的环境中,如果任一种语言是大小写不敏感的,则将无法一直使用第一种命名方案。以Microsoft Visual Basic为例,Dimwidget as Widget将会引发一处语法错误,因为widget 和Widget会被当做同一个标识符看待。

   第二和方案使类型名和变量名之间的差异更加鲜明。然而,由于历史原因,在C++和JAVA里面全部字母只用于表示常量,同时这种方案也会与第一种方案面临混合语言环境的问题。

   第三种方案可用于所有语言,但是很多程序员人审美的角度出发并不喜欢增加前缀。

   第四种方案有时会用于第三种方案的备选项,但是它存在的问题是需要改变类的每个实例的名字,而不是仅仅修改类名。

   第五种方案要求基于每个变量的实际情况做出更多的考虑。在大多数情况下,要求程序员为每个变量想出一个特别的名字会有助于提高代码的可读性。但是有时候,一个widget确实就是一个普通的widget,在这种情况下你会发现自已会想出一些并不鲜明的名字,如genericWidget,它的可读性比较差。

   简而言之,每一种可选方案都不是十全十美的。本书代码采用的是第五种方案,因为当不要求代码的阅读者熟悉一种不太直观的命名规则时,这种规则做是最容易理解的。

标识全局变量   有一种编程问题常见,那就是滥用全局变量。假如你在所有的全局变量名之前加上g_前缀,那么程序员在读到变量g_RunningTotal之后就会明白这是个全局变量,并且予以相应对待。

标识成员变量   要根据名字识别出变量是类的数据成员。即明确表示该变量既不是局部变量,也不是全局变量。比如说,你可以有m_前缀标识类的成员变量,以表明它是成员数据。

标识类型声明   为类型建立命名规则有两个好处:首先它能够明确表明一个名字是类型名,其次能够避免类型名与变量名冲突。为了满足这些要求,增加前缀或后缀是不错的方法。C++的惯用方法是把类型名全部大写——例如COLOR和MENU。(这一规则适用于typedet和struct,不适用于类名)。但是这样就会增加与命名处理常量发生混淆的可能。为了避免这样的麻烦,你可以为类型名增加t_前缀,如t_Color和t_Menu.

标识具名常量  你需要对具名常量加以标识,以便明确在为一个具名常量赋值时你用的是另一个变量的值(该值可能变化),还是一个具名常量。在Visual Basic里还会有另外的可能,那就是该值可能是一个函数的返回值。VisualBasic不要求在调用函数时给函数加括号,与之相反,在C++里即使函数没有参数也要使用括号给变量命名的方法之一是给变量增加 c_前缀。这会让你写出类似 c_RecesMax 或者c_LinesPerPageMax这样的名字来。 C++和Java里的规则是全部用大写,以及如果有可能,用下画线来分割单词,例如RECSMAX或者RECS_MAX以及LINESPERPAGEMAX或者LINES_PER_PAGE_MAX。

标识枚举类型的元素  与具名常量相同,枚举类型的元素需要加以标识——以便表明该名字表示的是枚举类型,而不是一个变量、具名常量或者函数。标准方法如下:全部用大写,或者类型名增加e_前缀或E_前缀,同时为该类型的成员名增加基于特定类型的前缀,如Color_或者Planet_.

在不能保证输入参数只读的语言里标识只读参数  有时输入参数会被意外修改。在C++ 和 Visual Basic这样的语言里,你必须明确表明是否希望把一个修改后的值返回给调用的方子程序。在C++ 里分别用*、&和const指明,在Visual Basic里分别用ByRef 和 ByVal指明。

在其他语言里,如果你修改了输入变量的值,那么无论你是否愿意,他的新值都会被返回。特别是当你传递对象的时候。举例来说,在Java里所有对象都是“按值(by value)”传递的,因此当你把一个对象传递给一个子程序的时候,该对象的内容就可以被调用子程序修改(Arnold,Gosling,Holmes2000).

在这些语言里,如果你制定了为输入参数增加一个const前缀(或者 final、nonmodifiable等)的命名规则,那么当你看到const前缀出现在赋值符号左边的时候,就会知道出现错误。如果你看到constMAX.SetNewMax(…),就会知道这里有大漏洞,一位const前缀表明了该变量是不应该不修改的。

格式化命名以提高可读性  有两种方法可以提高可读性,那就是用大小写和分隔符来分隔单词。例如,GYMNSTICSPOINTTOTAL就要比gymnasticsPointTotal 或者gymnastics_point_total难读的多。C++、Java、Visual Basic和其他的编程语言允许混合使用大小写字符。另外,C++、Java、Visual Basic和其他的编程语言也允许使用下画线(_)作为分隔符。

尽量不要混用上述方法,那样会使代码难以读懂。如果你老老实实地坚持使用任意一种提高可读性的方法,你的代码质量一定会有所改善。人们曾经就诸如变量名的第一个字母是不是应该大写(TotalPoints 对totalPoint)的做法价值展开了非常激烈的讨论,但是只要你和你的团队在使用上保持一致,那么大写小写就没有太大区别。

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

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

C Conventions

C 的命名规则

有很多命名规则特别适用于C语言:

l c 和ch是字符变量

l i和 j 是整数下标

l n 表示某物的数量

l p 表示指针

l s 是字符串

l 预处理红全部大写(ALL_CAPS)。.这通常也包括typedef.

l 变量名和子程序名全部小写

l 下画线(_)用作分隔符:letter_in_lowercase 要比 lettersinlowercase 更具可读性

这些都属于一般性的,UNIX 风格或者Linux风格的C编程规则,C编程规则在不同的环境下也会有所差异。开发MicrosoftWindows 应用的C程序员倾向于采用匈牙利命名法,并在变量名中使用大小写,这是因为Macintosh 工具箱和操作子程序最初是为支持Pascal接口而设计的。

 

C++  Conventions

C++的命名规则

以下是围绕C++ 编程形成的命名规则。

l i和 j 是整数下标

l p 是指针

l 常量、typedef和预处理宏全部大写(ALL_CAPS)

l 类和其他类型的名字混合大小写(MixedUpperAndLowerCase()).

l 变量名和函数名中的第一个单词小写,后续单词的首字母大写——例如,variableOrRoutineName。

l 不把下画线用作名字的分隔符,除非用于全部大写的名字以及特定的前缀中(如用于标识全局变量的前缀)。

与C编程相比,上述规则还远没有形成标准,并且不同的环境也会形成不同的具体规则。

 

Java Conventions

Java 的规则

与C 和 C++不同,Java 语言的风格从一开始就创建好了。

l i和j是整数下标。

l 常量全部大写(ALL_CAPS)并用下画线分隔。

l 类名和接口名的每一个单词的首字母均大写,包括第一个单词——例如,ClassOrinterfaceName。

l 变量名和方法名中的第一个单词的首字母小写,后续单词的首字母大写——例如,variableOrRoutineName。

l 除用于全部大写的名字之外,不使用下画线作为名字中的分隔符。

l 访问器子程序使用get  和 set 前缀。

 

 

Visual Basic Conventions

Visual Basic 的命名规则

Visual Basic还没有固定的规则。

混合语言编程的注意事项

在混合语言环境中编程时,可以对命名规则(以及格式规则、文档规则等)做出优化以提高整体的一致性和可读性——即使这意味着优化后的规则会与其中某些语言所用的规则相冲突。

命名规则示例

    上述的标准规则容易使我们忽略前几页里谈论过的有关命名的若干重要事项——包括变量的作用域(私有的,类的或者全局的)、类名、对象名、子程序名和变量名之间的差异等。

在命名规则的指导原则长度超过了几面之后,看上去就显得非常复杂。然而,它们没有必要变得如此复杂,你也可以按照实际应用来加以应用。变量名包含了以下三类信息:

l 变量的内容(它代表什么)

l 数据的种类(具名变量,简单变量,用户自定义类型或者类)

l 变量的作用域(私用的,类的,包的或者全局的作用域)

根据上述指导原则,表11-3,表1-4和表11-5给出了C,C++,Java和VB的命名规则。这些特殊规则并非是强制性的,但是它们能帮你了解一份非正式的命名规则应包含哪些内容。

 

表11-3 C++和Java命名规则示例

实体                          描       述          

ClassName                 类名混合使用大小写,首字母大写            

TypeName                 类型定义,包括枚举类型和typedef,混合使用大小写,首字母大写

EnumeratedTypes           除遵循上述规则外,枚举类型总以复数形式表示

localVariable               局部变量混合使用大小写,首字母小写。其名字应该与底层数据类型无关,而且应该反映该变量所代表的事物

routineParameter            子程序参数的格式与局部变量相同

RoutineName()             子程序名混合使用大小写(第7.3节已经讨论过什么是好的子程序名)

M_ClassVariable            对类的多个子程序可见(且只对该类可见)的成员变量名用m_前缀

g_GlobalVariable            全局变量用g_前缀

CONSTANT                具名常量全部大写

MACRO                   宏全部大写

Base_EnumeratedType        枚举类型名用能够反映其基础类型的,单数形式的前缀—例如:Color_Red,Color_Blue

 

表11-4 C的命名规则示例

实体                         描    述 

TypeName                类型名混合使用大小写,首字母大写

GlobalRoutineName()       公用子程序名混合使用大小写

f_FileRoutineName()        单一模块(文件)私用的子程序名用f_前缀

LocalVariable             局部变量混合使用大小写,其名字应该和底层数据类型无关,而且应该反映该变量所代表的事物

RoutineParameter          子程序参数的格式与局部变量相同

f_FileStaticVariable         模块(文件)变量名用f_前缀

G_GLOBAL_GlobalVariable  全局变量名以G_前缀和一个能反映定义该变量的模块(文件的,全部大写的名字开始-----例如,G_SCREEN_Dimensions

LOCAL_CONSTANT      单一子程序或者模块(文件)私用的具名常量全部大写—例如,ROWS_MAX

G_GLOBALCONSTANT    全局具名常量名全部大写,并且以G_前缀和一个能反映定义该具名模块(文件)的,全部大写的名字开始,如G_SCREEN_ROWS_MAX

LOCALMACRO()          单一子程序或者 模块(文件)私用 的宏定义全部大写

G_GLOBAL_MACRO()     全局宏定义全部大写,并且G_以前缀和一个能反映定义该宏的模块(文件)的全部大写名字开始----例如,G_SCREEN_LOCATION()

 

由于Visual Basic对大小写不敏感,因此需要采取一些特殊的规则来区分类型名和变量名。请见表11-5

 

表11-5 Visual Basic 的命名规则示例

实体                    描        述

C_ClassName         类名混合使用大小写,首字母大写,并且加C-前缀

T_TypeName         类型定义,包括枚举类型和typedef,混合使用大小写,首字母大写,并且加T-前缀

T_EnumeratedTypes    除遵循上述规则外,枚举类型总以复数形式表示

localVariable          局部变量混合使用大小写,首字母小写。其名字应该与底层数据类型无关,而且应该反映该变量所代表的事物

routineParameter       子程序参数的格式与局部变量相同

RoutineName()        子程序名混合使用大小写(第7.3节已经讨论过什么是好的子程名)

M_ClassVariable       只在一个类范围内对该类的多个子程序可见的成员变量名以前缀

g_GlobalVariable       全局变量用g_前缀

CONSTANT           具名常量全部大写

Base_EnumeratedType   枚举类型名用能够反映其基础类型的,单数形式的前缀—例如:Color_Red,Color_Blue

 

11.5标准前缀

   对具有通用含义的前缀标准化,为数据命名提供了一种简洁,一致并且可读性好的方法。有关标准前缀最广为人知的方案就是匈牙得命名法,该方案有一组用于指导变量和子程序命名(而不是指导如何给匈牙利人取名!)的详细原则组成,并且曾经一度被广泛用于Microsoft Windows编程。尽管目前匈牙利命名法已经不再得到广泛应用,但是使用简洁准确的缩写词的基本命名标准理念却仍然具有价值。

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

User-Defined Type Abbreviations

用户自定义类型缩写

UDT缩写可以标识被命名对像或变量的数据类型。UDT缩写可以被用于表示像窗体,屏幕区域以及字体一类的实体。UDT缩写通常不会表示任何由编程语言所提供的预置数据类型。

UDT用很短的编码描述,这此编码描述是为特定的程序创建的,并且经过标准化以在程序中使用。这些编码有助于用户理解其所代表的实体,如用wn代表社会分配,scr代表屏幕区域。表11-6列出了一份UDT示例。你可能会在开发文字处理程序的时候用到它们。

 

表11-6 用于文字处理程序的UDT示例。

UDT缩写                   含义

ch             字符(Character,这里的字符不是指C++中的字符,而是指文字处理程序可能用于表示一份文档中的字符的数据类型)

doc            文档(Document)

pa             段落(Paragraph)

scr             屏幕区域(Screen region)

Sel            选中范围(Selection)

Wn            窗体(window)

 

当你使用UDT的时候,你还要按与UDT同样的缩写去定义编程语言的数据类型。这样,如果你有表11-6所列出的那些UDT,你就会看到下面这样的数据声明:

CH       chCursorPosition;

SCR      scrUseWrkspace;

DOC     docActive;

PA       firstPaActiveDocument;

PA       lastPaActiveDocument;

WN      wnMain;

同样,这些例子是与文字处理程序相关的。要把它们 用于你自己的项目,你需要为环境中最常用的那些UDT创建UDT缩写。

语义前缀

     语义前缀比UDT更进一步,它描述了变量或者对像是如何使用的。语义前缀与UDT不同,后者会根据项目的不同而不同,而前者在某种程度上对于不同的项目均是标准的。表11-7列出了一组标准的语义前缀。

 

表11-7 语义前缀

语义前缀

含义

c

数量(Count,如记录,字符或者其它东西的个数)

first

  数组中需要处理的第一个元素。first与min类似,但它是相对于当前操作而不是数组本身的

g

全局变量(global variable)

i

数组的下标((index into an array)

Las

t数组中需要处理的最后一个元素。last与first相对应

lim

数组中需要处理的元素上限。lim 不是一个合法的下标。它与last都是与first相对应的概念。不用之处是lim 表示的是一个数组中并不存在的上界;而last表示的是最终的、合法的元素。通常,lim 等于 last+1

m

类一级的变量

max

数组或其他种类的列表中绝对的最后一个元素。max 反映的是数组本身,而不是针对数组的操作

min

数组或其他种类的列表中绝对的第一个元素

p

指针(pointer)

 

   语义前缀可以全用小写,也可以混合使用大小写。还可以根据需要与UDT和其他语义前缀混合使用。例如,文挡中的第一段应该命名为 pa ,以表明它是个段落 , 还要加上first 以强调它是第一个段落:即firstPa 。一组段落的下标可以命名为iPa;cPa是相应的计数值,段落的总数量;firstPaActiveDocument和lastPaActiveDocument表示当前活动文档中的第一个和最后一个段落。

标准化前缀的优点

除了具备命名规则所能提供的一般意义上的优点外,标准前缀还为你带来了另外一些好处。由于很多名字已经标准化了。因此你在一个程序或者类内需要记忆的名字更少了。

标准前缀能够更为精确的描叙一些比较模糊的字。min、first、last、和max之间的严格区别就显得格外有用。

标准化的前缀使名字更加紧凑。例如,你可以用cpa 而不是totalParagraphs表示段落总数。你可以用ipa 表示一个段落数组的下标,而不是用indexParagraphs 或者paragraphsIndex。

最后,在你用的编译器不能检查你所用的抽象数据类型是,标准前缀能帮你准确的对类型作出判断:paReformat = docReformat 很可能不对,因为pa 和 doc 是不同的DUT。

标准前缀的主要缺陷是程序员在使用前缀的同时忽略给变量起有意义的名字。如果ipa已经能非常明确的表示一个段落数组的下标,那么程序员就不会主动的去想类似于ipaActiveDocument这样有意义的名字。为了提高可读性,应该停下来为数组下标起一个具有描叙性的名字。

11.6  创建具备可读性的短名字

从某种程度上说,要求使用短变量名是早期计算的遗留物。早期语言,如汇编、一般的basic和fortran 都把变量的名字长度限制在2到8个字符,并要求程序员创建简短的名字。早期的计算科学更多的同数学联系在一起,并且大量使用求和及其它等式中的i、j和k等符号。而在现代语言如 c++、java、和visual basic里面,实际上你可以创建任何长度的名字;几乎任何理由去缩短具有丰富含义的名字。

如果环境真的要求你创建简短的名字,请注意有些缩短名字的方法要好于其它的方法。你可以通过消除冗余的单词,使用简短的同意词以及使用诸多缩写策略中的任意一种来创建更好的短变量名。熟悉多种缩写技巧会很有用,因为没有哪种方法能使用于所有的情况。

缩写的一般指导原则

下面是几项用于创建缩写的指导原则。其中的一些原则彼此冲突,所以不要试图同时应用所有的原则。

l 使用标准 的缩写(列在字典种的那些常见缩写)

l 去掉所有非前置元音(computer变成cmptr,screen变成scrn,apple变成appl,

l integer变成intgr。)

l 去掉虚词and,or,the等

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

l 统一的在每个单词的第一、第二或者第三(选择最合适的一个)字母后截断。

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

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

l 去掉无用的后缀——ing,ed等

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

l 确保不要改变变量的含义。

l 反复使用上述技术,直到你把每个变量的名字都缩减到了8到20个字符,或者达到你所用的编程语言对变量名的限制字符数。

 

语音缩写

有些人倡导基于单词发音而不是拼写来创建缩写。于是skating就变成了sk8ing,highlight变成了hilite,before变成了b4,execute变成了xqt,诸如此类。这样做很象是要人去猜出个性化汽车牌照的意思,我不提倡这么做。作为一项练习,请猜猜下面这些名字各表示什么意思:

ILV2SK8    XMEQWK  S2DTM8O NXTC  TRMN8R

有关缩写的评论

在创建缩写的时候,会有很多的陷阱在等你。下面是一些能够用来避免犯错的规则。

不要从每个单词删除一个字符的方式来缩写 。 键入一个字符算不上什么额外的工作,而节省一个字符带来的便利却很难抵消由此造成的可读性损失。这就像日历中的“Jun”和“Jul”。你只有在非常着急的情况下猜有必要把June 写成“Jun”。对于大多数删除一个字母的做法而言,你很难回忆起自己是不是删了一个字母。所以,要么删除不止一个字符,要么就把单词拼写完整。

缩写要一致 。 应该一直用相同的缩写词。例如,要么全用Num,要么全用No,不要两个都用。与此类似,不要在一些名字里缩写某个单词而洽谈名字里不缩写。比如,不要在有些地方使用完整的单词Number,同时在其他地方使用Num缩写。

创建出你能读出来的名字 。 用xPos而不用xPstn,用needsCompu而不用ndsCmptg。此处可以借助电话来测试——如果你无法在电话中向其他人读出你的代码,就重新给变量起一个更清晰的名字。(Kernighan and Plauger 1987)

避免使用容易看错或容易读错的字符组合 。 为了表示B的结尾,ENDB要比BEND更好。如果你使用了一种更好的分隔技术,那么就不需要这一条原则,因为B-END、BEND 或者b-end都不会被读错。

使用辞典来解决命名冲突   创建短名字会带来的一项麻烦就是命名冲突——缩写后的名字相同。例如,如果命名长度被限制为3个字符,并且你需要在程序中的同一段代码中使用fired和full revenue disbursal,你可能会不经意的把缩写都写成了fird。

避免冲突的一种简单方法是使用含义相同的不同单词,这样一来,有一部词典就显得很方便。在本例中,可以用dissmissed来代替fired,以及用complete revenue disbursal来代替full revenue disbursal。这样,3个字母的缩写就分别变成了dsm和crd,从而消除了命名冲突。

在代码里用缩写对照表解释极短的名字含义   当编程语言只允许用非常短的名字的时候,增正加一张缩写对照表来为用户提示更多的变量含义。把该表格作为注释加到一段代码的开始。下面是一个例子:

Fortran示例:良好的名字对照表

Translation Table

 

Variable   Meaning

---------    -----------

XPOS     X-Coordinate  Position (in meters)

YPOS     Y-Coordinate  Position (in meters)

NDSCMP  Needs Computing  (=0 if no computationis needed;

=1 if computation is needed);

PTGTTL   Point Grand Total

PTVLMX  Piont Value Maximum

PSCRMX  Possible Score Maximum

你可能会认为这种方法已经过时了,但是在2003年中期,我与一些客户合作,该客户有上万用RPG语言写成的.变量名被限制在六个字符以内的代码。这些要求极短变量名的问题仍然时不时的出现。

   在一份项目级的“标准缩写”文档中说明所有的缩写  代码中的缩写会带来两种常见风险。

l 代码的读者可能不理解这些缩写。

l 其他程序员可能会用多个缩写来代替相同的词,从而产生不必要的混乱。

为了同时解决这两个潜在的问题,你可以创建一份“标准缩写”文档来记录项目中用到的全部编码缩写。这份文档既可以事文字处理程序的文档,也可以是电子表格文档。在很多的大项目里,它还可以是一个数据库。这份文档应签入(check  in)到版本控制系统里,当任何人于任何时间在代码里创建了一种新的缩写是把它签出()来修改。文档中的词意奥应该按照完整的单词排序,而不是按照缩写排序。

这看上去可能显得非常麻烦,但是除了开始的一额外工作,它事实上是创建了一重在项目中有效地使用缩写的机制。通过对所有用到的缩写加以说明,就解决了上面两种描述的常见危险的第一种。程序员如果不费力把标准缩写文档从版本控制系统中check out,输入新的缩写并把它check in回去,就是不能创建一个新的缩写。这是件好事,它表明,只有当一个缩写在代码中应用非常广泛,程序员不惜花上很多精力来为它编写缩写文档时,这一缩写才应当被创建。

这种方法通过降低程序员创建多余的缩写的可能性,;从而解决了第二种风险。想创建缩写的程序员会把文档check out 并输入新的缩写。如果他想缩写的单词已经有了缩写,该程序员机会注意到它,并且去使用它而不是再去创建新的。

本原则中体现出的核心问题,是方便编写代码同方便阅读代码这两种理念之间的差别。上面的方法会给编写时带来麻烦,但是程序员们在整个项目生命中会把大多数的时间用来阅读代码而不是编代码。这种方法提高了阅读代码的方便性。当一个项目结束时,他肯内阁还会提高编写代码的方便性。

记住,名字会与代码读者的意义要比独坐着的意义更重要   去读一读你自己写的并且很久没看过的代码,注意哪些名字是你需要花费很大功夫才能看懂的。应下决心改变这种做法 。

11.7 应该避免的名字

下面哪些变量名应该避免给出指导原则

避免使用令人误解的名字或缩写   要确保名字的含义是正确的。例如:?常用做?的反义词,如果用它作为“?”的缩写就很糟糕了。

避免使用具有相似含义的名字  如果你能过交换两个变量的名字而不会对程序的理解有影响,那么你就要为这两个变量重新命名。例如,input和inputValue,reordNum和nunRecords,以及fileNumber和fileIndex在语义上非常相似,因此,如果把它们用在同一段代码里,会容易混淆它们,并犯下一些微妙且难以发现的错误。

避免使用具有不同含义但却有相似名字的变量   如果你有两个名字相似但含义不同的变量,那么试着给其中一个重新命名,或改写他的缩写。避免使用类似于clientRecs和clientReps这样的名字。

避免使用发音相似的名字    比如:wrap和rap 当你试图和别人讨论代码时,同音词就会产生误会。我家猫对于极限编程的抱怨之一是它过于聪明的使用了Goal Donor和Goal Owner两个概念,他们读起来很难区分,记住,电话测试也适用于发音相似的名字。

避免名字中使用数字   如果名字中的数字真的重要,就涌数组代替单个的变量。如果数字不合适,那么数字就更不合适了。例如:要避免使用有file1和file2。你不能说永远不要数字,不过在创建一个新的含数字的名字前,请考虑还有没有更好的方法。

避免在名字中拼错单词   弄清楚单词实际应该怎么拼写是更难的。想让人们想出什么是“正确的”错拼更是勉为其难。

避免使用英语中常常写错的单词  很多英语手册中多含有一份常常拼写错的单词,要避免使用他们。

不要紧靠大小写来区分变量名  如果你用一种大小写敏感的语言编写的话,你也许会倾向于用frd来代替fired,应避免这样做,尽管这些名字是唯一的,但把其中任一名字与某个特殊含义关联起来的方式却太随意,且让人感到迷惑。

避免使用多种自然语言  在多语言的项目中,对于全部代码,如类名,变量名等,要强制使用一种自然语言,阅读其他程序员的代码可以称为一种挑战;阅读火星东南部的语言写成程序代码则是绝无可能的。

一种更微妙的问题产生于英语的变体。如果一个项目在多个说英语的国家进行,就应该以其中一种英语的版本为标准,以便你不用一直为在代码中应该使用”color”,还是”colour”,,”check”还是”cheque”等感到迷惑。

避免使用标准类型,变量和子程序的名字  所有的编程语言指南都会包含一份该语言保留的和预定义的名字列表。请不时读一读这份列表,以确保你自己的命名没有冒犯你所用的语言。例如,下面代码在PL/I中是合法的,但除非你是个十足的傻瓜,否则是不会这么用的:

if if = then then

  then = else;

else else = if;

不要使用与变量名含义完全无关的名字  如果你在程序中点缀着诸如margaret和pookie这样的字,就会在事实上保证没有其他人能够理解它。避免用你男朋友的名字,妻子的名字,最喜欢的啤酒的名字或者其他自作聪明的(也就是傻的)名字来为变量命名,除非你的程序真的是与你的男朋友,妻子或者最爱的啤酒有关。即使如此,你也应该明智地认识到这其中的每一项`都可能会变的,所以boyfriend,wife和favouriteBeer这些通用的名字会更好!

避免在名字中包含易混淆的字符  要意识到有些字符看上去是非常接近,很难把它们区分开来。如果两个名字的唯一区别就是这些字符中的一个,那么你区分这些名字就会变得非常困难。例如,试着这把下列每组中不属于改组的名字圈出来:

eyeChart1                     eyeChartI                            eyeChartl

TTLCONFUSION         TTLCONFUSION         TTLC0NFUSION

hard2Read                    hardZRead                    ard2Read

GRANDTOTAL            GRANDTOTAL            6RANDTOTAL

tt15                              tt1s                              ttls

很难区分的“对”包括(1(数字1)和l(小写字母L)),(1和I(大写字母i)),(.和,),(0(零)和O(大写字母o)),(2和Z),(;和:),(S和5)以及(G和6)。

像这样的细节真的有用吗?没错!Gerald Weinberg报导说,在20世纪70年代,一条Fortran FORMAT语句中的句号错写成了逗号。结果科学家们算错了太空飞船的轨道,导致了太空探测器的丢失-损失高达16亿美元(Weinberg1983)。

 

核对表:变量命名

 

命名的一般注意事项

l 名字完整并准确的表达了变量所代表的含义吗?

l 名字反映了现实世界的问题而不是编程语言方案吗?

l 名字足够长,可以让你无需苦苦思索吗?

l 如果有计算限定符,它被放在名字的最后吗?

l 名字中用Count或者Index来代替Num了吗?

 

为特定类型的数据命名

l 循环下标的名字有意义吗(如果循环的长度超出了一两行代码或者出现了嵌套循环,那么就应该是i,j或者k以外的其他名字)?

l 所有的临时变量都重新名以更有意义的名字了吗?

 

l 当布尔变量的值为真时,变量名能准确表达其含义吗?

l 枚举类型的名字中含有能够表示其类别的铅坠或后缀了吗?例如,把Color__用于Color_Red,Color_Green,Color_Blue等了吗?

l 具名常量是根据它所代表的抽象实体而不是它所代表的数字来命名的吗?

 

命名规则

l 规则能够区分局部数据,类的数据和全局数据吗?

l 规则能够区分类型名,取名常量,枚举类型和变量名吗?

l 规则能够在编译器不强制检测只读参数的语言里标志出子程序中的输入参数吗?

l 规则尽可能的与语言的标准规则兼容吗?

l 名字为了可读性而加以格式化吗?

 

短名字

l 代码用了长名字吗(除非有必要使用短名字)?

l 是否避免只为了省一个字符而缩写名字的情况?

l 所有单词的缩写方式都一致吗?

l 名字能够读出来吗?

l 避免使用容易被看错或者读错的名字吗?

l 在缩写对照表离队短名字做出说明吗?

 

常见命名问题:你避免使用……

l ……容易让人误解的名字吗?

l ……有相近含义的名字吗?

l ……只有一两个字符不同的名字吗?

l ……发音相近的名字吗?

l ……包含数字的名字吗?

l ……为了缩短而故意拼错的名字吗?

l ……英语中经常拼错的名字吗?

l ……与标准裤子程序名或者与定义变量名冲突的名字吗?

l ……过于随意的名字吗?

l ……含有难读的字符的名字吗?

要点

l 好的变量名是提高程序可读性的一项关键要素。对特殊种类的变量,比如循环下标和状态变量,需要加以特殊的考虑。

l 名字要经可能的具体。那些太模糊或者台通用以至于能够用于多种目的的名字通常都是很不好的。

l 命名规则应该能够区分局部数据,类数据和全局数据。他们还应当可以区分类数据,具名常量,枚举类型名字和变量名。

l 无论做哪种类型项目,你都应该采用某种变量命名规则。你所采用的规则的种类取决于你的程序的规模,以及项目成员的人数。

l 现代编程语言很少需要用到缩写。如果你真的要使用缩写,请使用项目缩写词典或者标准前缀来帮助理解缩写。

代码阅读的次数远远多于编写的次数。确保你所取的名字更侧重于阅读方便而不是编写方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值