第九章 数据名称
9.2 特定数据类型命名
除了对数据命名通常需要考虑的一些问题之外,对特殊数据类型必须给予特殊的考虑。本 书将论述循环变量、状态变量、临时变量、逻辑变量、枚举变量和命名常量的命名问题。
9.2.l 循环变量命名
由于几乎每个程序中都含有循环,因此,对循环变量的命名问题加以专门考虑是十分必要 的。
简单循环中的循环控制变量的名字往往也是十分简单的,常用的是 i、j、k 等。下面是一个 Pascal 循环的例子:
for i:=FirstItem to LastItem do Data[i] := 0;
如果这个变量还要在循环外使用,那么应该用比 i、j、k 更能说明问题的名称。比如,你正 从文件中读取记录,并且要知道已经读取了多少个记录,那么用 RecordCount 作为其名称似乎 更合适些,请看下面的这个 Pascal 程序:
RecordCount:= 0
while not eof(InputFile) do
begin
RecordCount := RecordCount + 1;
ReadLn ( Score [ Recordcount ] ) end;
{ lines using RecoudCount }
…
如果循环体长度较长的话,那就很容易使人忘记它代表的是什么,因此最好给循环控制变 量一个富有意义的名字。由于经常进行更改,扩展和拷贝等代码到另一个程序中,因此,大多 数有经验的程序员都避免用 i、j、k 这类的名字。
使循环体变长的一个常见原因是嵌套。因此,对于一个有多重嵌套的循环,最好给循环控 制变量以较长的名字以便改善其可读性:
for TeamIndex := i to TeamCount to begin
for EventIndex := 1 to EventCount[ TeamIndex ] do Score[ TeamIndex, EventIndex ] := 0
end;
通过精心对循环控制变量进行命名,可以避免它们的交叉:当你想用 i 时误用了 j,或者 想用 j 时却又误用了 i。这样做也可以使对数组的存取操作更为明了。Score [ TeamIndex, EvenIndex ]显然要 Score[i,j]更能说明问题。总之,应尽量避免使用 i、j、k 来命名。如果不得 不使用它们的话,那除了把它们用作循环控制变量之外,最好不再用作别的变量名。这一约定 是众所周知的,如果不遵守它只会引起别人的困惑。
9.2.2 状态变量命名
状态变量描述的是程序所处的状态。下面论述了它们的命名原则。
用比 flag 更好的名称来命名变量,最好不用 flag 作为状态变量的名字。之所以要避免使用 flag 作为标志名称,是因为它不能告诉你关于这个标志的任何信息。为了清楚起见,应该给标 志赋值,并且用枚举类型、命名常量或当作命名常量使用的全局变量对其进行测试。下面是一 个在 C 语言中不恰当命名标志的例子。
if( Flag )...
if( StatusFlag & 0x0F )...
if( PrintFlag == 16 )...
if( ComputerFlag == 0 )...
Flag = 0x1;
StatusFlag = 0x80;
PrintFlag = 16;
ComputerFlag = 0;
如果这个程序不是你写的,而且也没有注释告诉你的话,你是无法知道类似 statusFlag = 0x80 之类的语句到底是要干什么的,而且你也无法知道 Statusflag 和 0x80 到底是什么意思。以
下是功能与内容相同但清楚得多的程序:
if ( DataReady )...
if ( CharacterType & PRINTABLE_CHAR )...
if ( ReportType == AnnualRpt )...
if ( RecalcNeeded == TRUE )...
DataReady = TRUE;
CharacterType = CONTROL_CHARACTER;
ReportType = AnnualRpt;
RecalcNeeded = FALSE;
显然,第二个例子中 CharacterType = CONTROL_CHARACTER 的意义要比第一个中 StatusFlag = 0x80 的意义要清楚得多。同样,第二个例子中的条件语句 if ( ReportType ==
AnnualRpt ) 也显然要比第一个中的 if ( PrintFlag == 16 )清楚得多。第二个例子表明,你可借 助预先命名的常量或枚举类型来使用这种方法。以下是如何利用枚举类型和命名常量来设置上 例中用到的值,仍用 C 来实现:
/* values for DataReady and RecalcNeeded */
#define TRUE 1
#define FALSE 0
/* values for CharacterType */
#define LETTER 0x01
#define DIGIT 0x02
#define PUNCTUATION 0x04
#define LINE_DRAW 0x08
#define PRINTABLE_CHAR ( LETTER | DIGIT | PUNCTUATION | LINE_DRAW )
#define CONTROL_CHARACTER 0x80
/* values for ReportType */
Typedef enum { DailyRpt,MonthlyRpt,,QuarterlyRpt,
AnnualRpt,AllRpts}REPORT_TYPE_T;
当你发现自己“侦破”了一段代码时,应该考虑对变量重新命名。侦破一桩凶杀案是可以 的,但你不应该去“侦破”一段代码。代码应该是具有良好可读性的。
9.2.3 临时变量命名
临时变量用来保存中间运算结果,如用作暂时保留某个位置或保留内务操作值。通常把它 们叫做 TEMP 或 X 等没有什么意义的名字。临时变量的使用往往标志着程序员还没有完全理解
程序。而且,由于名义上给了它一个“临时”的状态,因而程序员们在处理它们时往往会采取 漫不经心的态度,从而增大了出错机会。
要警惕“临时”变量。通常,暂时保留某些值是完全必要的。但如果在你的程序中出现很 多临时变量的话,则说明你对它们在程序中作用和使用它们的目的还不清楚。先让我们看一下 下面用 C 语言写成一个例子:
/* Compute root of a quadratic equation.
This assumes that ( b^2 - 4 * a * c ) is positive. */
Temp = sqrt ( b^ 2 - 4 * a * c );
root[0] = ( -b + Temp )/( 2 * a );
root[1] = ( -b – Temp )/( 2 * a );
把由公式 sqrt ( b^2 - 4 * a * c ) 计算出来的值储存起来是个不错的想法,尤其是在后面还有 两处用到了它的情况下。但是用 TEMP 作为它的名称不能告诉你关于这个变量意义的任何信息。
一个较好的作法是下面这个例子:
/* Compute roots of a quadratic equation.
This assumes that ( b^2 – 4 * a * c )is positive */
Discriminant = sqrt ( b^2 - 4 * a *c );
root[0] = ( -b + Discriminant )/( 2 * a );
root[1] = ( -b - Discriminant )/( 2 * a );
事实上两段代码是完全一样的,但是通过采用更准确也更能说明问题的变量名,大大改善 了其可读性。
9.2.4 逻辑变量命名
以下命名逻辑变量的几条准则:
记住一些典型的逻辑变量名。以下是些非常有用的逻辑变量名:
Done。用 Done 来表示某项工作已经完成了。这个变量可以表示子程序或循环是否已经完 成。当某项工作没有完成时,把 Done 的值赋为 False;当某项工作已经完成时,把 Done 的值 赋为 True。
Error。用 Error 来表示发生了错误。当没有错误时将 Error 的值赋为 False;当有错误时将 其
赋为 True。
Found。用 Found 来表示是否找取了某个值。当搜寻数组来查找某一值或搜寻某一文件表 查找某一雇员的识别卡时,没有找到某一值时将其值赋为 False,而一旦找到这个值。则把 Found 值赋为 True。
Success。用 Success 来表示某一操作是否已成功地完成。当某一程序无法完成时,将 Success 的值置为 False;而当某一操作已经完成时,将 Success 的值置为 True。如果可能的话,可以用 比 Success 更准确更具有表达力的名称来代替它,用这个新名称应可以精确地表达出到底是什 么 已 成 功 地 完 成 了 。 比 如 当 某 一 程 序 完 成 处 理 后 就 可 以 认 为 是 成 功 时 , 就 可 以 用 Processingcomplete 来代替 Success。如果当找到某一值就可以为程序是成功的时,可以用 Found 来代替它。
用暗含非真即假的名字来给逻辑变量命名。像 Done 或 Success 等之所以是恰当地逻辑变量 名,是因为它们的状态是非真(True) 即假 (False)的。某项工作或者完成了或者没完成;或者 是成功的或者不成功,不会有第三种状态。而类似 Status 或 SourceFile 等则是不恰当的名字,
因为看不出它们的状态是非真即假的。如果 Status 的值是 True,那它是否意味着某种物质有状 态呢?任何物质都有状态。或者说当其值是 True 时,意味着某种物质的状态很好,而为 False 时则意味着状态不好?仅从 Status 这个名称是无法回答这些问题的。
为清楚起见,最好用 Error 或 Status_OK 等来代替 Status;用 SourceFileAvailable 或 SourceFileFound 来代替 Source。
有些程序员喜欢用 Is 用为逻辑变量名的前缀,于是变量名就成了一个问句:IsDone?
IsError?IsSuccess?当用 True 或 False 来回答上述问题时,就等于给变量赋了值。这样做的好 处是可以避免那些不恰当的名称。如 IsStatus 显然没有任何意义。
使用肯定的逻辑变量名。否定式的变量名如 NotFound、NotDone 和 Notsuccessful 等在“非”
运算中是很难读懂的,如:
if not NotFound
这类名字应该用 Found,Done,Successful 等来代替,以方便“非”运算。
9.2.5 枚举类型命名
当使用枚举型变量时,可以通过使用相同的前缀或后缀表示某一类型的元素都是属于同一 组的,如下面的这段 Ada 程序所示:
type COLOR is ( COLOR_RED, COLOR_GREEN,COLOR_BLUE );
type PLANET is ( PLANET_ERATH, PLANET_MARS, PLANET_VENUS );
9.2.6 常量命名
对常量来说,应该用它所代表的抽象实体而不是数值来命名。FIVE 是一个很不恰当的常量 名 称 ( 不 管 它 代 表 的 数 值 是 否 是 5.0 ); CYCLES_NEEDED 则 是 个 恰 当 的 名 称 , CYCLES_NEEDED 可以等于 5.0 也可以等于 6.0,而 Five = 6.0 则是个荒唐的语句。由于同样的 原因,BAKERS_DOZEN 也是很不恰当的变量名,而 MAX_DONUTS 则要恰当得多。