有意义的命名

By | 2019年3月3日

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,强制通过静态方法生成对象。