Clean Code 由 Robert C. Martin 在 2009 年出版。他是一个著名的软件工程师,以推广敏捷开发著称。所以,敏捷开发的思想也渗透在这本书里。在这本书中,他详细描述了编程的方法论,包括最重要的:如何鉴别好代码和坏代码,以及写干净代码的准则。这些准则环环相扣,形成一个统一的整体。
主题
要写干净的代码。没有人能一次写出干净的代码,要多次重写;但是这值得。
命名
计算机不看代码。计算机不认识任何文字。代码是写给人的文章,文章的主角是变量,而情节是指令。写程序是为了向人(自己或者别人)解释我们想要计算机做什么事情。
选择好名字,对同一个概念用同一个名字,不要冗余。
函数
每个函数都应该做一件事情。因为函数如果做很多事,会很难理解,而且容易出错。复杂的函数也很难命名,所以要避免。如果一个函数很长,那么它往往没有做一件事。所以函数应该很短。
函数还应该尽量无副作用。因为副作用很难理解。我们通常把会产生副作用的函数封装成一个类。
函数的控制流应当简明清晰,避免用不相干的语句(例如处理错误码)混杂逻辑,因此使用异常来分离错误和处理错误的代码。
函数的参数要尽可能少,最好一个,以小于三个为宜,最好不要用 flag 来控制函数,因为这意味着函数有多个功能。
不要在输入里输出。
函数之间最好不要有时间上的耦合性(必须先调用 A 再调用 B),如果有,要尽量让它们实际上耦合起来。
注释
注释有害。因为代码更新的时候,注释不一定更新(因为注释更新不是强制的,所以很容易不更新)。这样,注释和代码就不一样了,注释就会误导读者。读者弄不明白:到底注释说的是对的(所以代码有 bug),还是注释错了(所以代码是对的)。
所以注释只应该用来解释设计思想,阐述 “why”,这些往往不变动的内容。而不是具体实现细节。具体实现细节如果需要注释才能看懂,说明这段代码应该重写。
对象和数据结构
对象和数据结构是不同的东西。
对象将数据和方法放在一起。对象容易增加新的种类,而不改变原有的功能(用这个实现多态,取代 if-else 或者 switch)。但是要改变原有的功能时,对象非常麻烦,因为要在接口定义新的虚函数,每一个实现接口的对象都要加入新的内容。与此相反,数据结构容易增加新的行为,只要加入一些函数。但是对已有行为,很难让它适配数据结构。
错误处理
使用异常,而不是错误码。在异常中写明相关信息。
正常的执行流要顺畅。
不要用 null,有需要的话,用 Optional 或者 Status,加上 Monadic 的操作。
边界
调用第三方 API 很常用。
使用一个包管理器。
用写单元测试的方法学习和使用一个第三方库。这样当第三方库更新的时候,你可以知道这个库是否和以前不兼容。
单元测试
测试不是可有可无的。它是软件的一部分,因为程序员需要通过测试明白自己要做什么,用户往往要看测试,才能明白函数如何调用。
测试驱动开发三大定律:
第一定律:你不能写功能代码,除非写了一个失败的单元测试。
第二定律:你的单元测例应该刚刚好失败,编译失败也是失败。
第三定律:你不能写比满足目前的单元测试更多的功能代码
要让单元测试高质量。需要满足下面的特性:快速、独立、可重复、及时、自验证(不需要看日志)
单元测试允许重构。因为如果没有单元测试,你就不知道自己的重构会不会毁了整个代码仓库。
类
面向对象编程范式通过隐藏状态来管理状态。类应该很小,高内聚低耦合。不同的类之间通过组合沟通,一个类内部最好每个方法使用每个变量。毕竟这是使用类的充分理由。
设计
基本设计规则:1. 运行所有的测试 2. 实现新功能 3. 重构
应该可以一键构建,一键运行测试。
不同层级的抽象不能混杂在一起。
参考资料
[1] Clean Code: A Handbook of Agile Software Craftsmanship, Robert C. Martin, Pearson. 2008.