1. 自我解释
变量名称应该自我解释,说明它为什么存在,做什么事,应该怎么用。如果名称需要注释来补充,那就不算是名副其实。
int d; //elapsed time in days
名称d什么也没说明,应该选择指明了计量对象和计量单位的名称。
int elapsedTimeInDays; int daysSinceCreation;
下面的代码目的何在?
public List<int[]> getThem() { List<int[]> list1 = new ArrayList<int[]>(); for (int[] x : theList) { if (x[0] == 4) { list1.add(x); } } return list1; }
上面代码不复杂,只是变量语义不清楚,所以看不懂它要做什么事。其实它是一个扫雷游戏:
- theList是单元格列表,应当命名为gameBoard;
- 数组第0个元素,代表状态值;值4表示已经标记了;应当取个有意义的名字。
public List<int[]> getFlaggedCells() { List<int[]> flaggedCells = new ArrayList<int[]>(); for (int[] cell : gameBoard) { if (cell[STATUS_VALUE] == FLAGGED) { flaggedCells.add(cell); } } return flaggedCells; }
还可以进一步,把int[]表示的单元格,封装成Cell类,该类isFlagged()返回其是否被标记了,从而掩盖那些magic number.
public List<Cell> getFlaggedCells() { List<Cell> flaggedCells = new ArrayList<>(); for (Cell cell : gameBoard) { if (cell.isFlagged()) { flaggedCells.add(cell); } } return flaggedCells; }
2. 避免误导
最可怕的就是用小写字母l和大写字母O作为变量名,尤其是它俩组合在一起,它们太像数字1和0。
int a = l; if (O == l) { a = O1; } else { l = 01; }
3. 使用读得出来的名称
一是方便记忆,二是方便讨论。如果名称读不出来,讨论的时候就像个傻鸟。“哎,这个XXX怎么怎么了…”
我们应当用恰当的英语的单词,而非自造词。比如genymdhms就不如generationTimestamp好。
4. 接口和实现
不要在接口前面加I,例如IShapeFactory,不需要且不想告诉客户给他们的是接口。我们只需对接口的实现进行命名:
- 如果接口有很多实现类,就根据实现类的特点命名。例如:List的实现类有ArrayList, LinkedList, CheckedList等。
- 如果接口仅有一个实现,就加个后缀impl。例如:ShapeFactory的实现类ShapeFactoryImpl。
5. 每个概念对应一个词
每个名称或行为,使用一个词,并且一以贯之。例如,fetch、retrieve和get;XXXControler和XXXManager。不要有的地方这样用,有的地方那样用。
6. 添加有意义的语境
在有良好命名的package,类和函数的情况下,通过这些语境,我们能知道变量是干什么用的。
例如,firstName, lastName, street, houseNumber, city, state和zipCode放在Address类里,我们知道它们构成的是个地址。假使只有孤零零的state,就无法知道它代表什么。此时给名称添加前缀就是最后一招了,改为addrState。
UI开发中,经常给变量添加后缀表示变量类型,例如xxBtn, xxLbl, xxTxt等。也可以用前缀表示类型。
7. 不要添加没有意义的语境
假设在开发一个缩写为LT的软件,给每个类的前面都加个LT前缀。这个就很没必要,而且在开发工具中输入LT,提示列表恨不得有一英里那么长。
8. 静态工厂方法
方法名称解释了如何生成对象,例如:
Complex fulcrumPoint = Complex.fromRealNumber(23.0);
通常好于
Complex fulcrumPoint = new Complex(23.0);
此时可以考虑将构造方法设置为private,强制通过静态方法生成对象。