前言
1. 代码应当易于理解
把理解代码所需的时间最小化
第一部分 表面层次的改进
2. 把信息装到名字里
1)使用专业的单词,如将get换位fetch、download等;
2)避免空泛的名字,如tmp,retval,除非使用他们有特殊的理由;
3)用具体的名字代替抽象的名字;
4)给变量名带上重要的细节,如未处理的变量前面加raw_,值为毫秒的变量后面加_ms;
5)为作用域大的名字采用更长的名字,不要用让人费解的一两个字母来描述满屏可见的变量,对于只存在与几行之间的变量可用短一点的名字;
6)有目的的使用大小写,下划线等,如在类成员和局部变量后面加“_”区分。
word | alternatives |
stop | kill, pause(反: resume) |
send | deliver, dispatch, announce, distribute, route |
start | launch, create, begin, open |
make | create, set up, build, generate, compose, add, new |
3. 不会误解的名字
1)不会误解的名字是最好的名字;
2)当要定义一个值的上下限时,可以用max_、min_这样的前缀;对于包含的范围,first和last是好的选择;对于包含/排除范围,begin和end是最好的选择;
3)当为布尔值命名时,使用is或has这样的词来明确表示它是个布尔值,避免使用反义的词;
4)小心对特定词的期望,如f.size() 和 f.countSize()、getMean() 和 computeMean()等。
4. 审美
1)使用一致的布局,让读者很快习惯这种风格;
2)让相似的代码看上去相似;
3)把相关的代码行分组,形成代码块。
5. 该写什么样的注释
关键思想:注释的目的是尽量帮助读者了解的和作者一样多;
不要为那些从代码本身就能快速推断的事实写注释;
什么地方不需要注释:
1)能从代码本身中迅速推断的事实;
2)用来粉饰烂代码的“拐杖式注释”,应该把代码改好。
应该记录下来的想法包括:
1)对于为什么代码写成这样而不是那样的内在理由(“指导性批注”);
2)代码中的缺陷,使用像TODO、XXX这样的标记;
3)常量背后的故事,为什么是这个值。
站在读者的立场上思考:
1)预料到代码中哪些部分会让读者产生“?”,并给他们加上注释;
2)为普通读者预料之外的行为加上注释;
3)在文件/类的级别上使用“全局观”注释来解释所有部分是如何一起工作的;
4)用注释来总结代码块,使读者不至迷失于细节之中。
6. 写出言简意赅的注释
关键思想:注释应当有很高的信息/空间率,把更多的信息装入到更小的空间里
1)当像 “it” 和 “this” 这样的代词可能指代多个事物时,避免使用他们;
2)尽量精确地描述函数的行为;
3)在注释中用精心挑选的输入、输出例子进行说明;
4)声明代码的高层次意图,而非明显的细节;
5)用嵌入的注释(如function(/*arg = */...))来解释难以理解的函数参数;
6)用含义丰富的词来使注释简洁。
第二部分 简化循环和逻辑
第一部分介绍了表面层次的改进,那是一些改进代码可读性的简单方法,一次一行,在没有很大的风险或者花很大代价的情况下就可以应用。
第二部分将进一步深入讨论程序的 “循环和逻辑” :控制流、逻辑表达式以及让你的代码正常运行的那些变量。
7. 把控制流变得易读
关键思想:
把条件、循环以及其他对控制流的改变做得越 “自然” 越好,运用一种方式使读者不用停下来重读你的代码;
相对于追求最小化代码行数,更好的度量方法是最小化人们理解它所需的时间;
当你对代码做改动时,从全新的角度审视它,把它作为一个整体来看待;
编程结构 | 高层次程序流程是如何变得不清晰的 |
线程 | 不清楚什么时间执行什么代码 |
信号量/中断处理程序 | 有些代码随时都有可能执行 |
异常 | 可能会从多个函数调用中向上冒泡一样地执行 |
函数指针和匿名函数 | 很难知道到底会执行什么代码,因为在编译时还没有决定 |
虚方法 | object.virtualMethod()可能会调用一个未知子类的代码 |
1)在写比较时,把改变的值写在左边并且把更稳定的值写在右边更好一些,如 " x>=n";
2)在排列if/else语句时,通常先处理正确的、简单的、有趣的情况;
3)某些编程结构,如三目运算符、do/while结构、goto等会导致代码的可读性变差,尽量不要使用他们;
4)嵌套的代码块需要更加集中精力去理解,每层新的嵌套都需要读者把更多的上下文“压栈”;
5)通常来讲,提早返回可以减少嵌套并让代码整洁。
8. 拆分超长的表达式
关键思想:
把超长表达式拆分成更容易理解的小块
要小心“智能”的小代码段,它们往往在以后会让别人读起来干到困惑
1)引入 “解释变量” 来代表较长的子表达式;
2)用德摩根定理来操作逻辑表达式,如 if(!(a && b)) 变成 if(!a || b)。
9. 变量与可读性
三个问题:
1)变量越多,就越难全部跟踪它们的动向;
2)变量的作用域越大,就需要跟踪它的动向越久;
3)变量改变得越频繁,就越难以跟踪它的当前值。
关键思想:
让你的变量对尽量少的代码可见
操作一个变量的地方越多,就越难确定它的当前值
1)减少变量,即那些妨碍的变量,通过立刻处理结果消除 “中间结果” 变量;
2)减小每个变量的作用域,越小越好,把变量移到一个有最少代码可以看到的地方;
3)只写一次的变量更好,通过const、final 使得代码更容易理解。
第三部分 重新组织代码
第二部分讨论了如何改变程序的“循环与逻辑”来让代码更有可读性。
第三部分会讨论可以在函数级别对代码做出更大的改动。具体来讲,会讲到三种组织代码的方法:
- 抽取出那些与程序主要目的 “不相关的子问题”;
- 重新组织代码使它一次只做一件事;
- 先用自然语言描述代码,然后用这个描述来帮助你找到更整洁的解决方案。
10. 抽取不相关的子问题
积极地发现并抽取出不相关的子逻辑
把一般代码和项目专有代码分开
11. 一次只做一件事
关键思想:应该把代码组织得一次只做一件事
12. 把想法变成代码
用自然语言描述程序然后用这个描述来帮助写出更自然的代码
13. 少写代码
关键思想:最好读的代码就是没有代码
避免编写新代码:
1)从项目中消除不必要的功能,不要过度设计;
2)重新考虑需求,解决版本最简单的问题,只要能完成工作就行;
3)经常性地通读标准库的整个API,保持对他们的熟悉度。
第四部分 精选话题
14. 测试与可读性
关键思想:
测试应当具有可读性,以便其他程序员可以舒服地改变或者增加测试
应当选择一组最简单的输入,它能完整地使用被测代码
又简单又能完成工作的测试值更好
测试原则:对使用者隐去不重要的细节,以便更重要的小细节会更突出
测试驱动开发
如何改进测试的几个具体要点:
1)每个测试的最高一层应该越简明越好,最好每个测试的输入、输出可以用一行代码来描述;
2)如果测试失败了,它所发出的错误消息应该能让你容易跟踪并修正bug;
3)使用最简单的并且能够完整运用代码的测试输入;
4)给测试函数取一个有完整描述性的名字,使每个测试所测到的东西很明确,如test_<function>_<situation>;
5)要使他易于改动和增加新的测试。
15. 设计并改进“分钟/小时计数器”
附录 深入阅读