前言
第1章 代码应当易于理解
代码的写法应当使别人理解它所需的时间最小化;
第一部分 表面层次的改进
第2章 把信息装到名字里
使用专业的单词:例如,不用Get,而用Fetch或者Download可能会更好,这由上下文决定;
避免空泛的名词,像tmp和retval,除非使用它们有特殊的理由;
使用具体的名字来更细致地描述事物:ServerCanStart()这个名词就比CanListenOnPort更不清楚;
给变量名带上重要的细节:例如,在值为毫秒的变量后面加上_ms,或者在还需要转义的,未处理的变量前面加上raw_;
为作用域大的名词采用更长的名字:不要用让人费解的一个或两个字母的名词来命名在几屏之间可见的变量。对于只存在几行之间的变量用短一点的名字命名;
有目的地使用大小写,下划线:例如,你可以在类成员和局部变量后面加上"_"来区分它们,具体详见Google开源项目格式规范的C++代码;
第3章 不会误解的名字
不会误解的名字是好名字:阅读你代码的人应该理解你的本意,并且不会有其他的理解;
当定义一个值的上限或下限时,max_和min_是很好的前缀。对于包含的范围,first和last是好的选择。对于包含/排除范围,begin和end是最好的选择,因为他们最常用。当为布尔命名时,使用is和has这样的词来明确表示它是个布尔值,避免使用反义词(例如disable_ssl);
要小心用户对待特定词的期望。例如,用户期望get()或者size()是轻量(O(1))的方法。如果是重量(O(n)或更高)的话,重命名如countSize()等;
第4章 审美
如果多个代码做相似的事情,尝试让它们有相同的剪影;
让代码按列对齐可以让代码更容易浏览;
选择一个有意义的顺序,并始终用这样的顺序:如果在一段代码提到A、B和C,那么不要在另一段中说B、C和A;
用空行把大块代码分成逻辑上的“段落”;
第5章 该写什么样的注释
注释的目的是尽量帮助读者了解得和作者一样多。
什么地方不需要注释:
能从代码本身中迅速推断的事实;
用来粉饰烂代码(例如蹩脚的函数名)的“拐杖式注释”--应该把代码改好;
你应该记录下来的想法包括:
对于为什么代码写成这样而不是那样的内在理由(“指导性批注”);
代码中的缺陷,使用TODO:或者XXX:这样的标记;
常量背后的故事,为什么是这个值;
站在读者的立场上思考:
预料到代码中哪些部分会让部分读者说:"啊?为什么是这样",并且给它们加上注释;
为普通读者意料之外的行为加上注释;//不赞同
在文件/类的级别上使用“全局观”注释来解释所有的部分是如何一起工作的;
用注释来总结代码块,使读者不致迷失在细节中。
第6章 写出言简意赅的注释
注释应当有很高的信息/空间率,即把更多的信息装入更小的空间里。
当像it和this这样的代词可能指代多个事物时,避免使用它们;
尽量精确地描述函数的行为;
在注释中用精心挑选的输入/输出例子进行说明;
声明代码的高层次意图,而非明显的细节;
用嵌入的注释(如Function(/*arg==*/....))来解释难以理解的函数参数;
用含义丰富的词来使注释简洁;
第二部分 简化循环和逻辑
第7章 把控制流变得易读
在编写比较时(white(bytes_expected > bytes_received)),把改变的值写在左边并且把更稳定的值写在右边更好一些(white(bytes_received > bytes_expected));
你也可以重新排列if/else语句中的语句块。通常来讲,先处理正确的/简单的/有趣的情况。有时这些准则会冲突,但是当不冲突时,这是要遵循的经验法则。
某些编程结构,像三目运算符(:?)、do/While循环,以及goto经常导致代码的可读性变差。最好不要使用它们,因为总是有更整洁的代替方式。
嵌套的代码块需要更加集中精力去理解。每层新的嵌套都需要把更多的上下文“压入栈”。应该把它们改写成“线性”的代码来避免深嵌套。
通常来讲提早返回/跳过可以减少嵌套并让代码整洁。“保护语句”(在函数顶部处理简单的情况时)尤其有用。
第8章 拆分超长表达式
引入“解释变量”来代表较长的子表达式的好处:
它把巨大的表达式拆成小段;
它通过用简单的名字描述子表达式来让代码文档化;
它帮助读者识别代码中的主要概念;
德摩根定理有时可简化布尔表达式(例如if(!(a&&!b))变成if(!a||b));
尽量使if语句内只有一个活两个表达式,有时需要把问题反向或者考虑目标的对立面;
拆分独立表达式的方法同样适用于大的代码块;
第9章 变量与可读性
减少变量,即那些妨碍的变量。我们给出几个例子来演示如何通过立刻处理结果来消除“中间结果”变量;
减小每个变量的作用域,越小越好。把变量移到一个又最小代码可以看到它的地方。眼不见,心不烦;
只写一次的变量更好。那些只设置一次值的变量(或者const、final、常量)使得代码更容易理解;
第三部分 重新组织代码
第10章 抽取不相关的子问题
把一般代码和项目专有代码分开。其结果是,大部分代码都是一般代码。通过建立一大组库和辅助函数来解决一般问题,剩下的只是让你的程序与众不同的核心部分。
这个技巧使程序员只关注有良好定义的小问题,这些问题已经同项目的其它部分脱离。其结果是,对于这些子问题的解决方案倾向于更加完整和正确。你也可以在以后重用它们。
第11章 一次只做一件事
一次只做一类事,相同类的事情放到一起去做。
如果你有很难读的代码,尝试把它所做的说有任务列出来。其中一些任务可以很容易地变成单独的函数(或类)。其他的可以简单地成为一个函数中的逻辑“段落”。具体如何拆分这些任务没有它们已经分开这个事实那样重要,难的是要准确地描述你的程序所做的所有这些小事情。
第12章 把想法变成代码
用自然语言描述程序然后用这个描述来帮助你写出更自然的代码。这个技巧出人意料地简单,但很强大,看到你在描述中所用的词和短语还可以帮助你发现哪些子问题可以拆分出来。
“用自然语言说事情”的过程不仅可以用于写代码,在遇到解决不了的问题时还可以找到解决问题的办法。
如果你不能把问题说明白或者用词语做设计,估计是缺少了什么东西或者什么东西缺少定义,把一个问题(或想法)变成语言真的可以让它变得具体。
第13章 少写代码
每行新的代码都需要测试、写文档和维护。另外,代码库中的代码越多,它就越“重”,而且在其上开发就越难。
避免编写新代码的方法:
从项目中消除不必要的功能,不要过度设计;
重新考虑需求,解决版本最简单的问题,只要能完成工作就行;
经常性地通读标准库的整个API,保持对它们的熟悉程度;
第四部分 精选话题
第14章 测试与可读性
在测试代码中,可读性仍然很重要。如果测试的可读性很好,其结果是它们也会变得很容易写。因此大家会写更多的测试。并且,如果你把事实代码设计得容易测试,代码的整个设计会变得更好。
如何改进测试的要点:
每个测试的最高一层应该越简明越好。最好每个测试的输入/输出可以用一行代码来描述;
如果测试失败了,它所发出的错误信息应该能让你容易跟踪并修改这个 bug;
使用最简单的并且能够完整运用代码的测试输入;
给测试函数取一个完整描述性的名字,以使每个测试所测到的东西很明确。不要用Test1(),而用Test_<FunctionName>_<Situation>这样的名字;
最重要的是,要使它易于改动和增加新测试。
Recommend Book
《code Complete: A Practical Handbook of Software Construction, 2nd edition》,
《The Practice of Programming》, by Brian Kernighan and Rob Pike
《The Pragmatic From Journey to Master》
《Clean Code: A Handbook of Agile Software Craftmanship》
《Design Pattern: Elements of Reusable Object-Oriented Software》
《Programming Pearls, 2nd edition》