读《Clean Code》
是的,我们就是一群代码猴子,上蹿下跳,自以为领略了编程的真谛。可惜,当我们抓着几个酸桃子,得意洋洋地坐到树枝上,却对自己造成的混乱熟视无睹。那堆“可以运行”的乱麻程序,就在我们眼皮底下慢慢腐坏。
当我时常为写出“短小精炼”的代码而沾沾自喜时,这本书无疑给了我当头一棒,让我下定决心痛改前非。本书第一章举了一些例子来说明糟糕代码能带来多坏的影响。从第二章到第十三章,本书列举了整洁代码应有的一切特性。作者提倡注重细节,“应当用为自己的孩子命名般的谨慎去给变量命名”,正是这种对细节的执着造就了代码的整洁性。“写整洁代码,需要遵循大量的小技巧,来贯彻刻苦习得的‘整洁感’。”同时作者认为,要写出整洁代码,必须进行大量练习,否则“会如同第一次骑车的车手,即使学习了骑车的所有要领,还是会跌倒在地。”
对于软件而言,没有不变的需求,也没有不变的代码,实际上我们会花很多时间来读自己或他人的代码。因此,从软件的第一个版本开始,专注于细节的整洁,并保持代码的良好可读性就变得尤为重要。另外,i+++i
这种东西百害而无一利,它们只会把代码变成没人愿意看的一团乱麻。
整洁代码的要领
整洁代码原则上要像散文一样流畅,能自上而下地反映系统的设计。每一行代码,都能准确反映作者的意图。没有重复代码,还要包括尽量少的实体(类、方法、函数等)。
变量命名不怕长,要名副其实,遵循驼峰命名法。用常量代替“幻数(magic number)”,首字母大写。简单举例,
const PI = 3.14;
function getAreaOfCircleByRadius(radius) {
return radius * radius * PI;
}
就比
function f(a) {
return a * a * 3.14;
}
要好。
一个函数只做一件事情,并且保持简短。事实上这两个条件相辅相成。大的函数要拆分为小的函数的组合。函数参数不应当多于三个,否则一些参数应当被封装为对象。
作者认为用来表达意图的注释是“不得已的恶行”,不如修整代码本身。如果代码写得足够清爽,是不必有这种注释产生的。作者建议,当想要添加解释型注释时,不如用切分小函数的方式。例如,
function bigFunction() {
// do something
/* CODE */
// do something else
/* CODE */
}
不如用下面的写法:
function bigFunction() {
doSomething();
doSomethingElse();
}
function doSomething() {
/* CODE */
}
function doSomethingElse() {
/* CODE */
}
如果有暂时不用的代码,作者强烈呼吁直接删除而不是注释掉。否则过了一两个星期,我们怎么知道这代码还有没有用?不但放着碍眼,想删还不敢删。版本控制系统会记住历史代码,所以放心大胆地删掉无用代码吧。
保持一直的垂直空行、空格和缩进格式。逻辑联系紧密的代码,视觉上也应当紧密,反之亦然。
避免否定运算符出现判断条件中,if(isGood)
就比if(!isBad)
更容易理解。
单元测试的至高原则是可读性。捕获错误时,应当抛出异常而不是使用错误码。要为每种边界情况编写测试,保持整齐的代码边界。
出现重复的代码意味着抽象不足,应把重复代码的共性抽象为类或方法。
类如同函数,应当只有一个权责。臃肿的类要依据权责切分为小类的组合。
可配置数据要在较高层级上放置,局部变量要出现在第一次使用时的上方。
最重要的一点是,不要把上面当作教条来执行。必须做大量的练习,观察自己的失败或他人的失败,形成自己的“代码感”。本书最后用了三章篇幅,每一章都展示了对一个 Java 项目的逐步改进过程,值得深入体会。
代码规范
几乎所有的流行规范都要比原生语法更加严格,这也印证了“发送时要保守,接受时要开放”的设计原理,或者叫“严于律己,宽以待人”。目前,我主要写 JavaScript 和 php,遵守的是JSLint和php-fig规范。
JSLint 简明地阐述了代码中什么叫“好的部分”:如果一种语言特性有时候很有用然而有时候很糟糕,而且存在一种更好的替换方案,那么永远使用那个更好的方案。尤其是对于 JavaScript 和 php 这种对开发者友好的弱类型脚本语言来说,语法中存在不少弊端,例如一位熟练的开发者都未必能回答出0==null
到底返回什么。所以这两种规范都禁止使用==
运算符。
Fortran 首先将
=
作为赋值符号,犯了第一个错误。更糟糕的是,大多数语言把这个错误发扬光大了。C 语言将==
作为比较运算符,两者视觉上的相似性让它成为第二个错误。JavaScript 的==
行为更加古怪。0==''
,0=='0'
,''=='0'
,猜这里有几个返回true
?所幸,===
的存在一定程度上弥补了这个问题。
规范中有些部分,比如禁止省略大括号,不仅适用于 JavaScript 和 php,而适用于大多数语言规范。从根源上说,越能坚持严格的代码风格,就越能回避语法陷阱。即使跳出编程这件事,这句话也不会错。