《代码整洁之道》第 2 章 有意义的命名
第 2 章 有意义的命名
2.2 名副其实
如果名称需要注释来补充,那就不算是名副其实。
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>() ;
for (int[] x : theList)
if (x[0] == 4)
list1.add(x) ;
return list1;
}
为什么难以说明上列代码要做什么事?里面并没有复杂的表达式。空格和缩进中规中矩。只用到三个变量和两个常量。甚至没有涉及任何其他类或多态方法,只是(或者看起来是) 一个数组的列表而已。问题不在于代码的简洁度,而是在于代码的模糊度:即上下文在代码中未被明确体现的程度。上列代码要求我们了解类似以下问题的答案:
(1) theList 中是什么类型的东西?
(2) theList 零下标条目的意义是什么?
(3) 值 4 的意义是什么?
(4) 我怎么使用返回的列表?
问题的答案没体现在代码段中,可那就是它们该在的地方。
修改方法:
-
零下标条目是一种状态值,而这种状态值为 4 表示“已标记”。
if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell);
-
另写一个类,该类包含一个名副其实的函数(isFlagged)从而掩盖住 "4" 这个魔术数。
if (cell.isFlagged()) flaggedCells.add(cell)
2.3 避免误导
- 应当避免使用与本意相悖的词。
- 提防使用不同之处较小的名称。比如用小写字母
l
和大写字母O
作为变量名。
2.4 做有意义的区分
以数字系列命名(a1、 a2,...N)是依义命名的对立面。这样的名称纯属误导一完全没有提供正确信息;没有提供导向作者意图的线索。
public static void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i] ;
}
}
如果参数名改为 source
和 destination
,这个函数就会像样许多。
假设你有一个 Product
类。如果还有一个 ProductInfo
或 ProductData
类,那它们的名称虽然不同,意思却无区别。Info 和 Data 就像 a、an 和 the 一样,是意义含混的废话。
废话都是冗余。Variable
一词永远不应当出现在变量名中。Table 一词永远不应当出现在表名中。NameString
会比 Name
好吗? 难道 Name
会是-一个浮 点数不成?如果是这样,就触犯了关于误导的规则。设想有个名为 Customer
的类,还有一个名为 CustomerObject
的类。区别何在呢? 哪一个是表示客户历史支付情况的最佳途径?
2.5 使用读得出来的名称
命名时不要使用傻乎乎的自造词,要使用恰当的英语词。
2.6 使用可搜索的名称
单字母名称和数字常量有个问题,就是很难在一大篇文字中找出来。
找 MAX_CLASSES_PER_STUDENT
很容易,但想找数字 7 就麻烦了,它可能是某些文件名或其他常量定义的一部分,出现在因不同意图而采用的各种表达式中。如果该常量是个长数字,又被人错改过,就会逃过搜索,从而造成错误。
同样,e 也不是个便于搜索的好变量名。它是英文中最常用的字母,在每个程序、每段代码中都有可能出现。由此而见,长名称胜于短名称,搜得到的名称胜于用自造编码代写就的名称。
窃以为单字母名称仅用于短方法中的本地变量。名称长短应与其作用域大小相对应。若变量或常量可能在代码中多处使用,则应赋其以便于搜索的名称。再比较
for (int j=0; j<34; j++) {
s += (t[j]*4)/5;
}
和
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * real DaysPerIdealDay;
int realTaskweeks = (realdays / WORK_DAYS_PER_WEEK) ;
sum += realTas kWeeks;
}
注意,上面代码中的 sum 并非特别有用的名称,不过它至少搜得到。采用能表达意图的名称,貌似拉长了函数代码,但要想想看,WORK_DAYS_PER_WEEK
要比数字 5 好找得多,而列表中也只剩下了体现作者意图的名称。
2.7 避免使用编码
匈牙利语标记法(Hungarian Notation,HN)和成员前缀已经过时,是旧代码的标志物。
2.9 类名
类名和对象名应该是名词或名词短语,如 Customer
、WikiPage
、Account
和 AddressParser
。避免使用 Manager
、Processor
、Data
或 Info
这样的类名。类名不应当是动词。
2.10 方法名
方法名应当是动词或动词短语,如 postPayment
、deletePage
或 save
。属性访问器、修改器和断言应该根据其值命名,并依 Javabean 标准加上 get
、set
和 is
前缀。
string name = employee.getName();
customer.setName("mike") ;
if (paycheck.isPosted())...
重载构造器时,使用描述了参数的静态工厂方法名。例如,
Complex fulcrumPoint = Complex.FromRealNumber (23.0);
通常好于
Complex fulcrumPoint = new Complex (23.0);
可以考虑将相应的构造器设置为 private
,强制使用这种命名手段。
2.16 添加有意义的语境
设想你有名为 firstName
、lastName
、street
、houseNumber
、city
、state
和 zipcode
的变量。当它们搁一块儿的时候, 很明确是构成了一个地址。不过,假使只是在某个方法中看见孤零零一个 state
变量呢?你会理所当然推断那是某个地址的一部分吗?
可以添加前缀 addrFirstName
、addrLastName
、addrState
等,以此提供语境。至少,读者会明白这些变量是某个更大结构的一部分。当然,更好的方案是创建名为 Address
的类。这样,即便是编译器也会知道这些变量隶属某个更大的概念了。