Good Routine Names
好的子程序名字
好的子程序名字能清晰地描述子程序所做的一切。这里是有效地给子程序命名的一些指导原则:
描述子程序所做的所有事情 子程序的名字应当描述其所有的输出结果以及副作用(side effects)。如果一个子程序的作用是计算报表总额并打开一个输出文件,那么把它命名为ComputeReportTotals()就还不算完整。ComputeReportTotalsAndOpenOutputFile()是很完整,但是又太长且显得有点傻。如果你写的是有一些副作用的子程序,那就会起出很多又长又笨的名字。解决的方法不是使用某个描述性较弱的子程序名,而应该换一种方式编写程序,直截了当地解决问题而不产生副作用。
避免使用无意义的、模糊或表述不清的动词 有些动词的含义非常灵活,可以延伸到涵盖几乎任何含义。像HandleCalculation()、PerformServices()、OutputUser()、ProcessInput()和DealWithOutput()这样的子程序名字根本不能说明子程序是做什么的。最多就是告诉你这些子程序所做的事情与计算、服务、用户、输入、输出有关。当然,当动词“handle(处理)”用做“事件处理(event handling)”这一特定的技术含义时是个例外。
有时一个子程序中仅有的问题就是其名字表述不清,而子程序本身也许设计得很好,但如果把它的名字由HandleOutput()改为FormatAndPrintOutput(),那你就很容易看清楚这个子程序的功能了。
还有另外一些情况,其中的动词之所以含糊,是由于子程序执行的操作就是含糊不清的。这种子程序的问题在于目的不明确,而其模糊不清的名字仅是一种表象。如果是这种情况,那么最佳的解决办法便是重新组织该子程序及任何与之相关的子程序,以便使它们都具有更为明确的目的,进而赋予其能够精确描述这些目的的更为清晰的名字。
不要仅通过数字来形成不同的子程序名字 有个程序员把所有的代码都写成一个大的函数,然后为每15行代码创建一个函数,并把它们分别命名为Part1、Part2等。在此之后,他又创建了一个高层次的函数来调用这些部分(Partn)。这种创建子程序和给子程序命名的做法实在是骇人听闻(我真希望这很少发生)。但程序员们有时会用数字来区分类似于OutputUser、OutputUser1和OutputUser2这样的子程序。这些名字后面的数字无法显示出子程序所代表的抽象有何不同,因此这些子程序的命名也都很糟糕。
根据需要确定子程序名字的长度 研究表明,变量名的最佳长度是9到15个
字符。子程序通常比变量更为复杂,因此,好的子程序名字通常也会更长一些。另一方面,子程序名字通常是跟在对象名字之后,这实际上为其免费提供了一部分名字。总的来说,给子程序命名的重点是尽可能含义清晰,也就是说,子程序名的长短要视该名字是否清晰易懂而定。
给函数命名时要对返回值有所描述 函数有返回值,因此,函数的命名要应该针对其返回值进行。比如说,cos()、customerId.Next()、printer.IsReady()和pen.CurrentColor()都是不错的函数名,它们精确地表述了该函数将要返回的结果。
给过程起名时使用语气强烈的动词加宾语的形式 一个具有功能内聚性的过程(procedure)通常是针对一个对象执行一种操作。过程的名字应该能反映该过程所做的事情,而一个针对某对象执行的操作就需要一个动词+宾语(verb-plus-object)形式的名字。如PrintDocument()、CalcMonthlyRevenues()、CheckOrderInfo()和RepaginateDocument()等,都是很不错的过程名。
在面向对象语言中,你不用在过程名中加入对象的名字(宾语),因为对象本身已经包含在调用语句中了。你会用document.Print()、orderInfo.Check()和monthlyRevenues.Calc()等语句调用子程序。而诸如document.PrintDocument()这样的语句则显得太臃肿,并且当它们在派生类中被调用时也容易产生误解。如果Check(支票)类是从Document(文档)类继承而来的,那么check.Print()就很显然表示打印一张支票,而check.PrintDocument()看上去像是要打印支票簿或是信用卡的对账单,而不像是打印支票本身。
准确使用对仗词 命名时遵守对仗词的命名规则有助于保持一致性,从而也提高可读性。像first/last这样的对仗词组就很容易理解;而像FileOpen()和_lclose()这样的组合则不对称,容易使人迷惑。下面列出一些常见的对仗词组:
add/remove increment/decrement open/close
begin/end insert/delete show/hide
create/destroy lock/unlock source/target
first/last min/max start/stop
get/put next/previous up/down
get/set old/new
为常用操作确立命名规则 在某些系统里,区分不同类别的操作非常重要。而命名规则往往是指示这种区别的最简单也是最可靠的方法。
在我做过的一个项目的代码里,每个对象都被分配了一个唯一标识。我们忽视了为返回这种对象标识的子程序建立一个命名规则,以至于有了下面这些子程序名字:
employee.id.Get()
dependent.GetId()
supervisor()
candidate.id()
其中:Employee类提供了其id对象,而该对象又进而提供了Get()方法;Dependent类提供了GetId()方法;Supervisor类把id作为它的默认返回值;Candidate类则认为id对象的默认返回值是id,因此暴露了id对象。到了项目中期,已经没人能记住哪个对象应该用哪些子程序,但此时已经编写了太多的代码,已经无法返回再重新统一命名规则了。这样一来,项目组中每个人都不得不花费不必要的精力,去记住每个对象上采用的获取id的语法细节。而这些问题完全可以通过建立获取id的命名规则而避免。







