《代码整洁之道》读书笔记(一)之取个好的名字

2017-01-03  本文已影响402人  清风流苏

阅读《代码整洁之道》有两种原因,第一种:你是个程序员;第二,你想成为更好的程序员。
读完后,你能获得如下技能:

Later equals never. 有些事儿现在不做以后都不会做了。

程序员基础价值谜题

以前混乱的代码拖了自己后腿,但开发者背负着期限的压力,只好继续制造混乱。而制作混乱无助于赶上期限。

赶上期限的唯一办法:始终保持代码的整洁。

写整洁代码就像是绘画。多数人知道一幅画是好是坏,但能分辨优劣并不表示懂得绘画,能分辨代码优劣的人也不意味着会写整洁代码。

写整洁代码需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。这种“代码感”就是关键所在。

什么是整洁代码

整洁的代码只做好一件事。简洁,优雅。

每个函数、每个类、每个模块都全神贯注于一事,不受四周细节的干扰和污染。

有意义的命名

变量、函数、参数、类、包、文件。有很多地方需要命名。怎么命名才能简洁明了?

1. 名副其实

说起来简单,但这是一个很严肃的问题。选个好名字要花时间,但省下来的时间比花掉的多。一旦有好的命名,就换掉旧的。

// 差的命名
int d;          // 消逝的时间,以日计算。
    
// 好的命名
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
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. 我怎么使用返回的列表

问题的答案没体现在代码段中,而这本就是它们应该在的地方。比如,我们正在开发一款扫雷游戏,我们发现盘面是名为theList的单元格列表,那就将其名称改为gameBoard。

盘面上每个单元格都用一个简单数组表示。我们还发现,零下标条目是一种状态的值,而这种状态值为4表示为“已标记”。只要改为有意义的名称,代码就会得到相当程度的改进。

public List<int[]> getFlaggedCells() {
    List<int[]> flaggedCells = new ArrayList<>();
    for (int[] cell : gameBoard) {
        if (cell[STAUS_VALUE] == FLAGGED) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

还可以更进一步,不用int数组表示单元格,而是另写一个类。该类包括一个名副其实的函数(称为isFlagged),从而掩盖住那个魔术数(就是那个4)。于是得到函数的新版本:

public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<>();
    for (Cell cell : gameBoard) {
        if (cell.isFlagged()) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

只要简单改一下名字,就能轻易知道发生了什么。这就是选好名字的力量

2. 避免误导

程序员必须避免留下掩盖代码本意的错误线索。应当避免使用与本意相悖的词。例如,别用accountList来指称一组账号,除非它真的是List类型(即便容器就是一个List,最好也别在名称中写出容器类型名)。如果包含账号的容器并非真是个List,就会引起错误的判断。所以,用accountGroup或bunchOfAccounts,甚至直接用accounts都会好一些。

提防使用不同之处较小的名称。相区分模块中某处的XYZControllerForEfficientHandlongOfStrings和另一处的XYZControllerForEfficientStorageOfStrings,会花多长时间呢? 这两个词外形实在是太像了。

误导性名称真正可怕的例子是使用小写的字母l和大写的字母O作为变量名,尤其是在组合使用的时候。问题在于,它俩看起来完全像是数字1和0。

3. 做有意义的区分

数字系列和废话名称,可以满足编译器,但是远远不够。

public static void copyChars(char[] a1, char[] a2) {
    for (int i = 0; i < a1.length; i++) {
        a2[i] = a1[1];
    }
}

以数字系列命名(a1, a2, ......aN)是依义命名的对立面。这样的名称纯属误导——完全没有提供正确的信息,没有提供导向作者意图的线索。

如果将参数名改为source和destination,这个函数将会像样很多。

废话是另一种没有意义的区分。假设你有一个Product类。如果还有一个ProductInfo或ProductData类,那它们的名称虽然不同,意思却没区别。Info和Data就像a、an和the一样,是意义含混的废话。

如果缺少明确约定,变量moneyAmout就与money没区别,customerInfo与customer没区别,accountData与account没区别,theMessage也与message没区别。要区分名称,就要以读者能鉴别不同之处的方式来区分。

4. 使用读的出来的名称

能读出来的名称更容易记忆。如果名称读不出来,讨论的时候就像个傻鸟。

Date genymdhms // 生成日期,年、月、日、时、分、秒

Date generationTimestamp;

5. 使用可搜索的名称

MAX_CLASSES_PER_STUDENT比找数字7要容易得多。同样字母e也不是个便于搜索的变量名。因为太常见了。

单字母名称仅用于短方法中的局部变量。名称长短应与其作用域大小相对应。如果变量或常量可能在代码中多处使用,则应赋予其以便于搜索的名字。

6. 避免将类型或作用域编进名称中

早期编译器不做类型检查,程序员需要用匈牙利语标记法(Hungarian Notation,HN)来帮助自己记住类型。

在Windows的C语言API的时代,HN非常重要,传说HN是为了纪念具有传奇色彩的微软程序员Charles Simonyi。这种标记法比较简单:即变量名以表明该变量数据类型的小写字母开始。

例如szCmdLine的前缀sz代表string end of zero.以0 结尾的字符串。

strPhone,代表Phone是字符串类型。

而今,大部分语言是强类型的,代码编辑环境都已经先进到在编译开始前就侦测到类型错误的程度!所以HN和其他类似的格式编码都多余了。

它们增加了修改变量,函数,或类的名称或类型的难度

PhoneNumber phoneString;

//类型变化时,名称并未变化。

也不必用 m_ 前缀来标明成员变量。应当把类和函数做得足够小,消除对成员前缀的需要。

接口前导字母I被滥用。

7. 类名应该是名词或名词短语,方法名应该是动词或者动词短语

类名如Customer、WikiPage、Account。避免使用Manager、Processor、Data或Info这样的类名

方法名如postPayment、deletePage或save。属性访问器、修改器或断言应该根据其值命名,并依Javabean标准加上get、set和is前缀。

8. 同一概念对应统一个词

比如get、fetch、retrieve表达的含义差不多,尽量保持多个类使用同一种写法。

Controller、Manager、Driver含义相近,尽量保持一致。

9. 不用双关语

10. 使用解决方案领域名称

代码是给程序员读的,用计算机领域类的术语来命名是一个很好的做法。比如AccountVisitor、JobQueue这种。

如果不能用程序员熟悉的术语来命名,就采用所涉问题的领域名称命名。

11. 添加有意义的语境、不要添加没用的语境

如果你有命名良好的类、函数或命名空间来放置名称,给读者提供语境是最好不过了。如果没有,就用最后一招———给名称添加前缀。

比如,你有名为firstName、lastName、street、hourseNumber、city、state变量,搁一块儿的时候,很明确是一个地址。如果只是在一个方法中单独的看到一个state变量呢,你会理所当然得推断是一个地址吗?

可以添加前缀addrFirstName、addrLastName、addrState来提供语境。

假设有一个名为“加油站豪华版”(Gas Station Deluxe)的应用,在其中给每个类添加GSD前缀就不是什么好点子了。

上一篇 下一篇

猜你喜欢

热点阅读