编写可读代码的艺术-读书笔记

第二章 把信息装到名字里

​ 主题是:把信息塞入名字中。即,读者仅通过读到名字就可以获得大量的信息。主要有以下几点:

  • 使用专业的单词–例如,不用Get,而用Fetch或者Download可能会更好,这由上下文决定。
  • 避免空泛的名字,像tmp和retval,除非使用它们有特殊的理由。
  • 使用具体的名字来更细致地描述事物——ServerCanStart() 这个名字就比CanListenOnPort更不清楚。
  • 给变量名带上重要的细节——例如,在值为毫秒的变量后加上_ms,或者在还需要转义的、未处理的变量前加上raw_。
  • 为作用域大的名字采用更长的名字——不要用让人费解的一个或两个字母的名字来命名在几屏之间都可见的变量。对于只存在于几行之间的变量用短一点的名字更好。
  • 有目的地使用大小写、下划线等——例如,可以在类成员和局部变量后面加上“_”来区分它们。

    第三章 不会误解的名字

    ​ 不要误解的名字是最好的名字——阅读你代码的人应该理解你的本意,并且不会有其他的理解。
  • 在决定使用一个名字以前,要想象一下你的名字被误解成什么。最好的名字是不会误解的。
  • 当要定义一个值的上限或下限时,max_和min_是很好的前缀。对弈包含的范围,first和last是好的选择。对于包含/排除范围,begin和end是好的选择。
  • 当为布尔值命名时,使用is和has这样的词来明确表示它是一个布尔值,避免使用反义的词。
  • 要小心用户对特定词的期望。例如,用户会期望get()或者size()是轻量的方法。

    第四章 审美

    ​ 通过把代码用一致的、有意义的方式“格式化”,可以把代码变得更容易读,并且可以读得更快。下面是讨论过的一些具体技巧:
  • 如果多个代码块做相似的事情,尝试让他们有同样的剪影。
  • 把代码按“列”对齐可以让代码更容易浏览。
  • 如果在一段代码中提到A、B和C,那么不要在另一段中说B、C和A。选择一个有意义的顺序,并始终用这样的顺序。
  • 用空行来把大块分成逻辑上的“段落”。

    第五章 该写出什么样的注释

    注释的目的是帮助读者了解作者在写代码时已经知道的那些事情。本章主要介绍如何发现所有的不那么明显的信息块并把它们写下来。
    什么地方不需要注释:
  • 能从代码本身中迅速地推断的事实。
  • 用来粉饰烂代码的“拐杖式注释”——应该把代码改好。
    应该记录下来的想法包括:
  • 对于为什么代码写成这样而不是那样的内在理由(“指导性批注”)。
  • 代码中的缺陷,使用像TODO:或者XXX:这样的标记。(TODO:还没有处理的事情;FIXME:已知的无法运行的代码;HACK:对一个问题不得不采用的比较粗暴的解决方案;XXX:危险!这里有重要的问题)
  • 常量背后的故事,为什么是这个值。
    站在读者的立场上思考:
  • 预料到代码中哪些部分会让读者产生疑问,并且给它们加上注释。
  • 为普通读者意料之外的行为加上注释。
  • 在文件/类的级别上使用“全局观”注释来解释所有的部分是如何一起工作的。
  • 用注释来总结代码块,使读者不致迷失在细节中。

    第六章 如何写出言简意赅的注释

  • 当像“it”和“this”这样的代词可能指代多个事物时,避免使用它们。
  • 尽量精确地描述函数的行为。
  • 在注释中用精心挑选的输入/输出例子进行说明。
  • 声明代码的高层次意图,而非明显的细节。
  • 用嵌入的注释来解释难以理解的函数参数。
  • 用含义丰富的词来使注释简洁。

    第七章 把控制流变得易读

    有几种方法可以让代码的控制流更易读
  • 在写一个比较时(while (bytes_expected > bytes_received)),把改变的值写在左边,并且把更稳定的值写在右边更好一些(while (bytes_received < bytes_expected))。
  • 可以重新排列if/else语句中的语句块。通常来讲,先处理正确的/简单的/有趣的情况。有时这种准则会冲突,但是当不冲突时,这是要遵守的经验法则。
  • 某些编程结构,像三目运算符(:?)、do/while循环,以及goto经常会导致代码的可读性变差。最好不要使用它们,因为总是有更整洁的代替方式。
  • 嵌套的代码块需要更加集中精力去理解。每层新的嵌套都需要读者把更多的上下文“压入栈”。应该把它们改写成更加“线性”的代码来避免深嵌套。
  • 通常来讲提早返回可以减少嵌套并让代码整洁。“保护语句”(在函数顶部处理简单的情况时)尤其有用。

    第八章 拆分超长的表达式

    关键思想:把超长的表达式拆分为更容易理解的小块

  • 引入“解释变量”来代表较长的子表达式。这种方式有三个好处:
    • 它把巨大的表达式拆成小段;
    • 它通过用简单的名字描述子表达式来让代码文档化;
    • 它帮助读者识别代码中的主要概念。
  • 用德摩根定理来操作逻辑表达式——这个技术可以把布尔表达式用更整洁的方式重写;
  • 任何复杂逻辑的地方都可以进行拆分。

    第九章 变量与可读性

    减少变量
    • 删除没有价值的临时变量
    • 减少中间结果
    • 减少控制变量
      缩小变量的作用域

      关键思想:让你的变量对尽量少的代码行可见。

只写一次的变量更好
总结
本章是关于程序中的变量是如何快速累积而变得难以跟踪的。你可以通过减少变量的数量和让它们尽量“轻量级”来让代码更有可读性。具体有:

  • 减少变量。
  • 减少每个变量的作用域,越小越好。把变量移到一个有最少代码可以看到的地方。
  • 只写一次的变量更好。那些只设置一次的变量(或者const、final、常量)使得代码更容易理解。

    第十章 抽取不相关的子问题

    ​ 本章一个简单的总结就是“把一般代码和项目专有的代码分开“。其结果是,大部分的代码都是一般代码。通过建立一大组库和辅助函数来解决一般问题,剩下的只是让你的程序与众不同的核心部分。
    ​ 这个技巧有帮助的原因是它使程序员关注小而定义良好的问题,这些问题已经同项目的其他部分脱离。其结果是,对于这些子问题的解决方案倾向于更加完整和正确。你也可以再以后重用它们。

    第十一章 一次只做一件事

    关键思想:应该把代码组织得一次只做一件事情

​ 如果你有很难读的代码,尝试把它所做的所有任务列出来。其中一些任务可以很容易地变成单独的函数。其他的可以简单地变成为一个函数中的逻辑”段落”。具体如何拆分这些任务没有它们已经分开这个事实那样重要。难的是要准确地面描述你的程序所做的所有这些小事情。

第十二章 把想法变成代码

​ 本章讨论了一个简单的技巧,用自然语言描述程序然后用这个描述来帮助你写出更自然的代码。这个技巧出人意料地简单,但很强大。看到你在描述中所用的词和短语还可以帮助你发现哪些子问题可以拆分出来。但是这个“用自然语言说事情”的过程不仅可以用于写代码。另一个看待这个问题的角度是:如果你不能把问题说明白或者用词语来做设计,估计是缺少什么东西或者什么东西缺少定义。把一个我那天变成语言可以让它变得更具体。

第十三章 少写代码

关键思想:最好的代码就是没有代码。

  • 质疑和拆分你的需求
  • 保持小的代码库

​ 随着项目的增长,项目加进来的越来越多的源文件。项目很大,没有一个人自己全部理解它。增加新功能会变得很痛苦,而且使用这些代码还很费力还令人不快。最好的解决办法就是”让你的代码库越小,越轻量级越好“,可以尝试如下方法:

1. 创建越多越好的”工具“代码来减少重复代码;
2. 减少无用代码或没用的功能;
3. 让你的项目保持分开的子项目状态;
4. 总的来说,要小心代码的”重量“。让它保持又轻又灵。
  • 熟悉你周边的库

总结

​ 本章是关于写越少代码越好的。每行新的代码都需要测试、写文档和维护。另外,代码库中的代码越多,它就越”重“,而且在其上开发就越难。可以通过以下方法来避免重新编写新代码:

  • 从项目中消除不必要的功能,不要过度设计;
  • 重新考虑需求,解决版本最简单的问题,只要能完成工作就行;
  • 经常性地通读标准库的整个API,保持对他们的熟悉程度。

第十四章 测试与可读性

在测试代码中,可读性仍然很重要。如果测试的可读性很好,其结果是他们也会变得很容易写,因此大家会写更多的测试。并且,如果你把事实代码设计得容易测试,代码设计会变得更好。以下是如何改进测试的具体要点:

  • 每个测试的最高一次应该越简明越好。最好每个测试的输入/输出可以用一行代码描述;
  • 如果测试失败了,它所发出的错误消息应该能让你容易跟踪并修正这个bug;
  • 使用最简单的并且能够完整运用代码的测试输入;
  • 给测试函数取一个有完整描述性的名字,以使每个测试所测到的东西很明确。不要用Test1(),而要像Test___这样的名字。

最重要的是,要使它易于改动和增加新的测试。

坚持原创技术分享,您的支持将鼓励我继续创作!