代码整洁之道-命名的艺术

Author Avatar
我爱吃包子 5月 28, 2019

前言

最近在看 《clean code 代码整洁之道》里面说了很多编码的技巧和需要注意的问题。正好最近这段时间接了个native 和 web 相结合的项目,需要把这个全部改成 h5 页面。这个项目总之一言难尽,从命名到方法的实现处处都不合理。总之就是一言难尽,所以准备写一个 clean code 的系列,希望能给大家一点帮助。

这是本系列的第一篇,主要讲命名。

注意本系列采用的编程语言是java,因为java 是强类型的语言,对于一些名称等有更好的直观展示作用。我们主要学习下 clean code 应该注意的事项,和语言没啥关系,java 只是一种载体,不要以为我转岗了,我依然是一个页面仔。

有意义的命名

clean code 有意义的命名章节主要讲了以下几点。

  • 名副其实
  • 避免误导
  • 做有意义的区分
  • 使用读得出来的名称
  • 使用可搜索的名称
  • 避免可使用编码
  • 类名非动词
  • 方法名为动词
  • 别扮可爱,别搞小聪明
  • 每个概念对应一个词
  • 对用双关语
  • 使用解决方案领域的名称
  • 使用源自所涉及问题领域的名称
  • 添加有意义的语境
  • 不要添加没有用的语境

总结一下也就是几点。

  • 1、名副其实,不要使用读不出来或者这种只有自己才知道的命名,多用大家都知道的单词,每个命名都只有唯一的意思。
  • 2、使用能够分辨的命名,不要使用上面我说的 amout、amout1 这种。
  • 3、多用专业的名词。
  • 4、在能够让人清楚的前提下尽量使用短名称。

一些例子

1、采用描述性名称

不要太快取名,确认名称是否具有描述性。名称太重要了,不可随意对待,花时间来取一个好名字是值得的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public int x() {
int q = 0;
int z = 0;

for(int kk = 0, kk < 10; kk++) {
if (l[z] === 10) {
q += 10 + (l[z + 1] + l[z + 2]);
z += 1;
} else if (l[z] + l[z + 1] === 10) {
q += 10 + l[z + 2];
z += 2;
} else {
q += l[z] + l[z + 1];
z += 2;
}
}

return q;
}

上面是一个不好的例子,代码的并不完整,但是不难理解他要干啥,当然前提是你要仔细读一遍先,里面充斥着各种魔术字符,会让后面接手的人疯掉。我们在看一下,改造后函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int score() {
int score = 0;
int frame = 0;
for (int frameNumber = 0; frameNumber < 10; frameNumber++) {
if (isStrike(frameNumber)) {
score += 10 + nextTwoBallsForStrike(frame);
score += 1;
} else if (isSpare(frame)) {
score += 10 + nextBallForSpare(frame);
frame += 2;
} else {
score += twoBallsInFrame(frame);
frame += 2;
}
}
return score;
}

上面的代码不如第一段代码完整,但是你马上能推断出他想它要做什么,而且很有可能依据函数的命名补写出缺少的函数。这就是一个好的命名的重要性。

2、无歧义的名称

选不会混淆函数或变量意义的名称,每个名称都只有单一的意思且不予其他混淆。

1
2
3
4
5
6
7
8
9
10
11
private String doRename() throws Exception {
if (refactorReferences) {
renameReferences();
}

renamePage();

pathToRename.removeNameFromEnd();
pathToRename.addNameToEnd(newName);
return PathParser.render(pathToRename);
}

看这个例子,doRename 函数中还存在着一个 renamePage 函数的调用,这就显得有点混淆了。我们说过在明确表达意思前提下名称越短越好,这显然并不在此列。依据函数的功能或者应该叫 renamePageAndOptionalReferences ,虽然名称很长,但是可以让人一眼就能看出这个函数要干什么。

3、为较大作用范围的变量选用较长的名称

可以先看下面一段代码

1
2
3
4
5
private void rollMany(int n, int pins) {
for(int i = 0; i < n; i++){
g.roll(pins);
}
}

这段代码很短,功能也很明白,变量 i 的作用范围很小,所以使用起来完全没得问题,如果使用 rollCount 反而会觉得突兀。但是想象一下如果 i 是一个全局变量命名或者一个其他作用范围很大的变量,这简直都是灾难。

4、名称应该说明一切信息

名称应该说明函数、变量的一切信息,不能只确定一部分功能。

1
2
3
4
5
6
7
public Logistics getLogistics() {
if (logistics === null) {
logistics = new Logistics(logisticsCode);
}

return logistics;
}

该函数的功能不仅仅是获取 logistics, 它的功能是判断 logistics 是否存在,不存在则创建一个再返回。所以更好的名称应该是 createOrReturnLogistics。

总结一下

我们在平常的开发工作中大部分时间是在读别人的代码,或者在别人代码的基础上增加功能。完完全全从零开始写一个系统、一个功能的场景很少,所以这个时候取一个好的命名就显得尤为重要,这样可以快速的阅读代码,了解以前的逻辑是啥,不必知道具体具体的实现就可以去尝试实现自己的功能。最后在说一句,花时间在取名上面并不是一件很羞耻的事情,也不是一个浪费时间的事情,好的名称真的太重要了。在你开发中,你想到了更好的名称来描述的时候,不要懒,直接从源头改一遍,只要你觉得这个名称可以帮助别人更好的理解你的代码,这样做就是值得的。