《代码简洁之道》阅读摘录

Published: 05 Nov 2018 Category: mind

又是Robert C. Martin(自称鲍勃大叔 Uncle Bob)写的书。(2010年1月第一版)

阅读本书有两种原因:第一,你是一个程序员;第二,你想成为更好的程序员。

开篇讲到,学习写整洁的代码很难。它不止于要求你掌握原则和模式。还需要去学习实践:用功去阅读大量代码,学习归纳总结,然后自己实践。

可读性基本原理:代码的写法应当使别人理解它的时间最小化。(《编写可读代码的艺术》)

C1 整洁代码

  • 勒布朗法则:稍后等于永不(Later equals never)。
  • 读写花费时间的比例超过10:1。写新代码时,我们一直在读旧代码。所以我们就像让读的过程变得轻松。
  • 美国童子军有一条简单的军规:让营地比你来时更干净!
  • 艺术书并不保证你读过之后成为艺术家,只能告诉你其他艺术家用过的工具、技术和思维过程。

C2 有意义的命名

  • 命名要名副其实。如果名称需要注释来补充,那就不算是名副其实。
  • 避免误导。别用accountList来指称一组账号,除非它真的是List类型。这时干脆就用accounts。
  • 做有意义的区分。假设你有一个Product类,如果还有一个ProductInfo或ProductData类,那么他们的名称虽然不同,意思却没有区别。Info和Data就像a、an和the一样,是意义含混的废话。
  • 废话都是冗余。Variable一词永远不应当出现在变量名中,Table一词永远不应当出现在表名中。
  • 使用可搜索的名称。长名称胜于短名称。
  • 前缀已成不入法眼的废料,变作了旧代码的标志物。比如用m_表示成员变量,用IName来表示接口等,这些命名统统不可取。
  • 每一个概念对应一个词。例如,避免混用fetch、retrieve和get来命名函数,要一以贯之。
  • 别用双关语。add能表示添加,但是它也能表示两个数的和,这个时候应该用insert或append表示插入。
  • 精确正是命名的要点。

不放试试上面这些规则,看你的代码可读性是否有所提升。如果你是在维护别人写的代码,使用重构工具来解决问题。效果立杆见影,而且会持续下去。

C3 函数

  • 短小——函数的第一规则。
  • 还要更短小——函数的第二条规则。
  • 函数应该做一件事。做好这件事。只做这一件事。
  • 要让代码拥有自顶向下的阅读顺序。让每个函数后面都跟着位于下一抽象层级的函数,这样一来,在查看函数列表时,就能循着抽象层级向下阅读了。
  • 别害怕长名称。长而具有描述性的名称,要比短而令人费解的名称好。长而具有描述性的名称,要比描述性的长注释好。
  • 函数参数越少越好。最理想是0参数,其次是一个,再次时两个,应尽量避免三个参数的函数。
  • 参数少对于编写测试用例也有好处。
  • 使用异常替换返回错误码。使用错误码很难避免一大堆的if语句,而抛出异常能将错误处理代码从主路径代码中分离出来。

如何写出符合上述规则的函数?

写代码和写别的东西很像。在写论文或文章时,你先想到什么就写什么,然后再打磨它。Bob写函数时,一开始都冗长而复杂。不过他会写单元测试,然后打磨这些代码,分解函数,修改名称,消除重复,同时保持通过。
不用从一开始旧按照规则写函数,没人能一下就做到最好。

C4 注释

  • 什么也不会比乱七八糟的注释更有本事搞乱一个模块。
  • 若编程语言足够有表现力,或者我们擅长于用这些语言来表达意图,就不那么需要注释。
  • Bob我为什么要极力贬低注释?因为注释会撒谎。代码会改,但是注释不会改。程序员不能坚持维护注释。
  • 写注释的常见动机之一是存在糟糕的代码。这时不是写注释去粉饰,而应该把代码弄干净。

let code say

C5 风格

  • 先明确一下,代码格式很重要。代码格式不可忽略,必须严肃对待。代码格式关乎沟通,而沟通是专业开发者的头等大事。

(我已经有一套自己的代码风格了)

C6 对象和数据结构

  • 它们的差异: 对象把数据隐藏于抽象之后,暴露操作数据的函数。数据结构暴露其数据,没有提供有意义的函数。它们是对立的。
  • 过程式代码难以添加新的数据结构,因为必须修改所有函数。面向对象代码难以添加新函数,因为必须修改所有类。
  • 对于这两种东西,优秀的开发者是不带成见地去对待,并依据手边工作的性质选择其中一种。

https://blog.csdn.net/l1024049542/article/details/78906073

C7 错误处理

  • 错误处理很重要,但如果它搞乱了代码逻辑,就是错误的做法。
  • 可控异常的使用有代价。代价就是违反开放/闭合原则。如果在方法中抛出可控异常,就得在catch语句和抛出异常处之间的每个方法签名中声明该异常,即在函数签名上添加throw子句,最终封装被打破了。
  • 如果你在编写一套关键代码,则可控异常有时也会有用,但前提是你必须捕获异常,否则其依赖成本通常会高于收益。
  • 平时写代码,尽量避免返回null值。也要避免将null值传递给其他方法。

C8 边界

(PDF有内容丢失,我去;好在这部分内容不是很重要)

C9 单元测试

  • TDD 测试驱动开发三定律
      1. 除非所有unit test通过,否则不允许编写任何产品代码.(测试先行)
      1. 在一个单元测试中只允许编写刚好能够导致失败的内容(编译错误也算失败)。(测试一旦失败,开始写生产代码)
      1. 只允许编写刚好能够使一个失败的unit test通过的产品代码。(老测试一旦通过,返回写新测试)
  • 测试代码和生产代码一样重要。
  • 尽量在每个测试函数里只包含一个断言语句。
  • 整洁的测试应遵循5条规则: FIRST
    • Fast: 测试的运行要足够快,这样就可保持频繁运行。
    • Independent: 测试应该相互独立。你应该可以单独运行每个测试,以任何顺序运行测试。千万不要有依赖。
    • Repeatable:测试应在任何环境中重复通过,不管是生成环境还是开发环境,不管是有网还是无网环境。
    • Self-Validating: 自足验证,测试应该有布尔值输出,或断言。
    • Timely: 测试应及时编写。最好是恰好在生产代码之前编写。

C10 类

  • 类应该短小,越短小越好(羞)。当然这里的『小』不是指代码量,而是指职责,遵循单一职责原则SRP。
  • 保持高内聚。

C11 系统

  • 系统应该分离构造和使用。(就像建筑工队建酒店,旅客住酒店一样)
  • 有一种强大的机制可以实现分离构造和使用,那就是依赖注入。

后续内容从略