《代码整洁之道》读书笔记

162 阅读9分钟

代码整洁之道

第一章 整洁代码

要想写好整洁的代码必须谨记几个原则。

  • 稍后等于永不:不要去容忍写得十分糟糕的代码,积极地重构
  • 永远假设代码是写给第三方看:使它变得可读可维护,cr的时候大家一眼就能看懂
  • 不要重复代码:Don't repeat yourself
  • 包括尽量少的实体: 如无必要,勿增实体
  • 测试驱动:保证有足够多的测试,并且通过它

第二章 有意义的命名

  1. 一个变量的含义不应该太过依赖于它的注释

    // bad
    var d int // 消逝的时间,以日计
    // good
    var elapsedTimeInDays int
    
  2. 避免误导,名称和含义必须保持一致

    accountList 是不是List类型, 否则不如用accounts。

    提防使用不同之处较小的名称比如1和l

  3. 有意义的区分

    不要用一些毫无意义的东西,比如userInfo和user 没有区别, data 和productData 没有区别。 a, the variable 就不应该出现在变量名里,

    命名里带上类型不是好习惯

  4. 能念出来的命名

    不要用生捏出的词

  5. 使用可搜索的名称

    一个变量的复杂程度往往可以和它的作用域成正比,方便搜索。

  6. 避免思维映射

    如果一个变量名会在读者脑力翻译成其他含义,请修改它

  7. 类名和对象名应该是名词或者名词短语。

    避免使用Manager,Processor,Data 这样的类名,也不应该是动词

  8. 方法名应该是动词或者动词短语

    参考Javabean标准

  9. 每个概念对应一个词

    给每个抽象概念选一个词,而且一以贯之,不要改来改去

  10. 添加有意义的语境

    可以添加同样的前缀比如addr_, 以此提供语境

第二章 函数

  1. 短小

    一般来说,不应该长于一屏。如果太长应该拆分成几个。太多重的if for循环也会降低可读性,避免出现一个函数里的多个for if嵌套

  2. 只做一件事

    函数应该做一件事,做好这件事,只做这一件事。避免做一些函数签名中不包括事情。

  3. 每个函数一个抽象层级

    确保函数中的语句都要在同一抽象层级上

  4. 使用描述性的名称‘

    函数做的事情往往和名称成正比,函数分工越隔离,名称往往也越短。但是不要畏惧用长的描述性名称。

  5. 函数参数,尽可能还是要减少函数的入参个数。

    如果函数需要很多入参,说明其中一些参数应该封装为类了。

  6. 命名中可以把参数的名称编码成函数名

  7. 无副作用

    函数承诺只做一件事情,但有时候会把其他做的事情藏起来,这样会导致预期外的风险和古怪的耦合。亚亮的事故就是源自于函数的副作用!

  8. 面向对象编程一定程度上可以减少输出的个数

    如果输出的参数太多。往往是因为不够面向对象

  9. 结构化编程

    每个代码块都应该有一个入口一个出口,减少break和continue,不要有goto

第三章 注释

  1. 注释不能美化糟糕的代码

    如果代码太糟糕,还是重写吧

  2. 好的注释>坏的注释>没有注释>错误的注释

  3. 如果阅读注释的时间比阅读代码的时间还长 就是坏注释

  4. 阐释性注释,需要确认其正确性

  5. 用好代码管理文件! 可以减少很多不必要的注释

  6. TODO注释定时查看 删除不再需要的。

  7. 注释可以放大看来不合理的东西的重要性

  8. 注释掉的代码最好还是删掉,用git去记住它

第四章 格式

  1. 相关的代码块放在一起,比如一个变量的声明和初始化

  2. 用好空格去分割不同逻辑聚合的代码

  3. 声明变量应该尽可能靠近其使用的位置

    如果函数很短,本地变量应该在头部出现。

    实体变量应该在类的顶部声明

  4. 相关函数应该放在一起

    如果一个调用了另外一个,就应该把他们放在一起且调用者放在被调用者上面

  5. 相关性越强的代码,彼此之间的距离就应该越短

  6. 长度不要太长,积极换行

第五章 数据结构

  1. 具象和抽象的重要性

    隐藏实现关乎抽象,类应该向外暴露接口,而不是用取值器和赋值器

    对象应该把数据隐藏于抽象之后

  2. 面向对象和面向过程编程的重要区分就是函数和变量的区别

    面向对象,不方便添加新函数,因为要修改所有类。面向过程,难以添加新数据结构,因为必须修改所有函数

  3. 得魔忒耳律

    模块不应该了解它所操作对象的内部情况。对象隐藏数据,保留操作。对象不应通过存取器暴露其内部结构。类C的方法f只应该调用以下对象的方法:C,由f创建的对象,作为参数传递给f的对象,由c的实体变量持有的对象

第六章 错误处理

  1. 错误处理不应该打乱代码的结构
  2. Try 像是一种事务, error的处理都可以看成对事务的一个特殊情况处理
  3. 别取null值,能返回非null的就不要返回null
  4. 更不要传递null值

第七章 边界

  1. 学习性测试很重要!

    这也是一种投资,如果后续版本升级了你也可以通过学习性测试来验证功能是否改变

第八章 整洁的测试

  1. TDD三定律

    定律一 在编写不能通过的单元测试前,不可编写生产代码

    定律二 只可编写刚好无法通过的单元测试,不能编译也算不通过

    定律三 只可编写刚好足以通过失败测试的生产代码

  2. 保持测试的整洁性

    脏测试 = 没测试

    测试代码和生产代码一样重要

  3. “单个断言”是个好准则,但不强求。需要保证的是单个测试中的断言数量应该最小化

    ps:断言这个东西在实际的生产代码中十分危险,最好只出现在测试里

  4. 每个测试一个概念

  5. 整洁的测试遵循5条规则 F.I.R.S.T

    快速,独立,可重复,自足验证,及时

第九章 类

  1. 类应该短小,不应该拥有太多不合理的指责

  2. 单一职责原则

    系统应该由许多短小的类而不是少量巨大的类组成。每个小类封装一个全责,只有一个修改的原因,并与少数其他类一起协同达成期望的系统行为。

  3. 内聚

    即每个方法都高度依赖于类的所有变量

  4. 为了修改而组织

  5. 依赖倒置原则,类应当依赖于抽象而不是依赖于具体细节

第十章 系统

  1. 将系统的构造与使用分开

    启始过程和启始过程之后的运行时逻辑应该分离。

    Lazy init 除外,有时候就违背了单一职责原则

    依赖注入可以实现分离构造与使用

  2. 摒弃先做大设计的原则,自底向上

第十一章 迭代

  1. 通过迭代设计达到整洁目的

  2. 运行所有测试

    测试越多,越关注测试就会越容易进行解耦,方便测试。测试也消除了对清楚代码就会破坏代码的恐惧

  3. 积极重构

  4. 不可重复

  5. 即可能少的类和方法

第十二章 并发编程

  1. 对象是过程的抽象。线程是调度的抽象
  2. 并发是一种解耦策略,它帮助我们吧做什么和何时做分解开。
  3. 并发编程的原则和技巧
    • 单一职责原则
    • 限制数据作用域
    • 使用数据复本
    • 线程尽可能的独立
  4. 警惕同步方法之间的依赖
  5. 不要把系统错误归咎于偶发事件
  6. 尽早并经常地在所有目标平台上运行线程代码

第十三章 味道与改进

  • 不恰当的信息

  • 废弃的注释应该删除

  • 冗余的注释应该删除,比如什么也没说的注释

  • 注释的代码应该删除!前提是用好git

  • 过多的参数

  • 输出参数违反也可以尽可能少,修改它所在对象的状态好了

  • 标识参数是bad

  • 永不调用的方法应该丢弃

  • 明显的行为未被实现

  • 不正确的边界行为

  • 重复代表遗漏了抽象

  • 在错误的抽象层级上的代码

  • 基类依赖于派生类

  • 信息过多,不应该暴露出太多的设计。良好的接口不需要提供太多的函数。

  • 不要死代码

  • 垂直分割的距离不要太远,定义和使用的位置尽可能近一些

  • 消除特性依恋,但特性依恋有时候也是不得不做的事情

  • 用选择算子来选择函数行为的参数-> 应该改为多个函数

  • 位置错误的全责很重要

  • 应该使用解释性变量

  • 函数名称应该足以表达其行为

  • 理解算法

  • 把逻辑依赖改为物理依赖

  • 用多态替代ifelse,或switch/case

  • 用命名常量替代魔术数

  • 结构甚于约定

  • 封装条件,如果if的逻辑难以理解应该把意图的函数抽离出来

  • 避免否定性条件。尽可能把条件表示为肯定形式

  • 只做一件事

  • 掩蔽时序耦合,被调用的次序显而易见很重要,可以用排列函数参数 a

    a()
    b()
    c()
    可以改成
    resA = a()
    resB = b(resA)
    resC = c(resB)
    
  • 封装边界条件,不要出现四散的+1,-1

  • 函数应该只在一个抽象层级上

  • 在较高层级放置可配置数据

  • 避免传递浏览

  • 采用描述性名称

  • 名称应该与抽象层级相符合

  • 名称不应该有歧义

  • 为较大范围选用较长的名称

  • 名称应该说明副作用

  • 使用覆盖率工具

  • 别忽略小测试

  • 测试边界条件

  • 全面测试相近的缺陷