《Clean Code》阅读笔记
-
Chapter 2 命名
- 命名要表现意图
- 避免歧义和误导,增强区分
- 命名可读性:便于发音,增强印象,便于交流
- 命名可查性:增强区分,便于搜索
- 类和对象的命名:名词或名词短语
- 方法的命名:动词或动词短语
- 使用仅表述单一概念的词,如 get,避免多概念的词,如 fetch
- 使用行业术语及业务术语
-
Chapter 3 方法
- 尺寸:越小越好,不应超过20行
- if、else、while 等 statement 不应超过一行
- 单一责任
- 单一抽象层级
- 无参最优,一参数次之,二参数最次,不应超过3参数,否则应引入参数结构或对象
- 避免将参数作为输出
-
Chapter 4 注释
- 尽量少用注释,避免过时的注释
- 如果能用良好的命名来表现意图,就不用注释
- 如果能用代码来表现意图,就不用注释
- 良好的注释:
- 提供补充信息
- 解释意图
- 警告
- TODO 注释
- 强调注释
-
Chapter 5 格式
- 垂直格式:
- 单个类文件,不超过100行/150行/200行
- 使用空行分隔方法
- 自顶向下的方法依赖分布
- 水平格式
- 单行不超过80字符/100字符/120字符,屏幕能够全部显示
- 保持缩进
- 使用空格,运算符左右使用空格等
- 显式地突出空循环,避免省略地写为一行
- 优先遵循团队规定的格式
- 垂直格式:
-
Chapter 6 对象和数据结构
- 数据结构和对象的对立性:
- 数据结构以 public 暴露内部属性,不提供访问方法
- 对象以 private 隐藏内部属性,提供访问方法
- 数据结构易于增加新方法,同时不影响已有结构。对象反之,易于增加新类,同时不影响已有方法。
- 数据结构不便于增加新数据结构,因为需要改变每个已有方法。对象反之,不便于增加新方法,因为需要改变每个已有类。
- Law of demeter:最少知识原则,一个对象应对其他对象有尽量少的了解。类C的 f 方法,应只调用这些方法:
- 类C的方法
- f 创造的对象的方法
- 传递给 f 的参数对象的方法
- 类C的成员实例变量的方法
- 数据结构和对象的对立性:
-
Chapter 7 错误处理
- 使用 exception, 避免使用返回的错误码
- 逐层 throw exception,在最顶层 catch,即定义一个错误传递流
- 将 try/catch 抽取出来作为独立函数
- 在需要时定义新的 exception 类
- 不要返回 null,定义一个 Null 类
- 不要传递 null
-
Chapter 8 边界
- 在自己的代码和第三方代码的边界处,使用自己编写的代码适配(适配器),避免将第三方代码直接嵌入自己的代码
- 测试第三方代码
-
Chapter 9 单元测试
- TDD
- 测试代码和生产代码同样重要,但测试代码不一定需要遵循同样的效率和资源限制
- 保持测试代码的clean,避免过时和冗余等
- BUILD-OPERATE-CHECK 的测试模板
- given/when/then 的测试方法命名建议
- 模板方法模式可能有所帮助
- 每个测试方法,仅测试一个概念
- F-I-R-S-T原则
- Fast,测试效率要高
- Independent:每个测试应当独立
- Repeatable:在不同环境下均可运行
- Self-Validating:应拥有一个boolean的输出,自身直接输出测试是否通过
- Timely:每个测试应 just before 写在生产代码之前
-
Chapter 10 类
- 类成员顺序如下,自顶向下
- public static constants
- private static constants
- private instance variables
- public functions
- 被 public function 调用的 private functions 紧跟在该 public function 之后,满足自顶向下的阅读顺序
- 尺寸:类尽可能小,不同于用物理行数衡量方法的尺寸,应该用 responsibilities 衡量类的尺寸。类应保持单一责任。
- 类应该保持单一责任(SRP),而类名应该描述这一责任。
- 类名不应超过25个单词,同时避免 if、and、or、but等代表多责任的词,同时少用大范围的含糊的词,如super,manager等。
- 尽可能提高类的内聚度:尽量使类中的每个成员变量被类方法使用
- 将大类分隔为很多小类,优化组织性,提高内聚。但也会造成类数量的激增。
- 依赖倒置原则(DIP),类依赖于抽象,而不是具体实现。降低耦合。
- 将类中会变化的部分抽象出来,让客户代码依赖于抽象,而不是具体实现。
- 类成员顺序如下,自顶向下
-
Chapter 11 系统
- 将系统构造部分,与使用部分解耦
- 将构造和初始化部分,全部放在main函数中,再在main函数中,将构造完毕的对象,以参数形式传递给具体使用它们的函数。这是一个方法。
- 工厂方法可能有所帮助。
- 依赖注入(DI)和控制反转(IoC)机制可能有所帮助。
- (这一章没怎么看懂)
-
Chapter 12 迭进
- Kent Beck 认为的“Simple Design”的四条规则。重要性由上而下递减。
- 运行所有测试
- 无重复(良好重构)
- 表现编程者意图
- 良好命名
- 保持类和方法尽可能小
- 遵循公有标准和共同语言,如通用规范、设计模式等
- 良好的单元测试
- 最小化类和方法的数量
- 经过去重、重构等操作之后,类和方法的数量可能激增。因此在此基础上,尽量减少它们的数量。
- Kent Beck 认为的“Simple Design”的四条规则。重要性由上而下递减。
-
Chapter 13 并发
- 复杂且不成熟,跳过。
-
Chapter 14 坏味道和启示
-
注释 Comments
- C1 不适宜的注释:修改历史、作者、最近修改日志等,都不适宜出现在注释中。注释应该仅保留技术和业务相关的要点。
- C2 过时的注释
- C3 冗余的注释:注释是代码的重复,没有提供额外信息
- C4 写的很烂的注释:认真措辞,不要随意
- C5 注释起来的代码:没人知道它的重要性和意义,其他人根本无法轻易地改动它。如果可以,直接删除它
-
环境 Environment
- E1 构筑环境需要不只一步:做到一键 build 你的项目环境。
- E2 单元测试需要不只一步:做到一键 test 你的项目的所有单元测试。
-
方法 Functions
- F1 太多参数:无参最佳,1参次之,2参再次,3参再次,多余三个参数,则考虑引入参数结构或对象。
- F2 输出参数:避免改变参数,然后将它作为结果输出。
- F3 Flag参数:避免使用Flag参数,这意味着这个方法将做不只一件事。
- F4 Dead方法:无人问津的方法,直接删除它。
-
通用 General
- G1 单个源码文件中,出现多种编程语言:解耦、分离他们。
- G2 明示的行为没有被实现:应该遵循“最低惊异原则”,任何明示而被期待的行为,都应该被正确实现。否则,代码阅读者将会对更多的代码失去信任。
- G3 边界处的行为都要被准确实现:任何边界条件、角落case、巧合case、异常case的行为,都需要被准确实现。否则,代码阅读者将会对更多的代码失去信任。
- G4 覆盖安全机制:避免覆盖你使用的框架提供的安全代码机制,如警告等,不要轻易重载、覆盖、取消它们。
- G5 重复:不仅包括明显重复的代码,还包括重复的设计等。善用模板方法和策略等设计模式。
- G6 错误/混乱抽象分层:不同抽象层级的隔离应该明确,一个方法应该仅在同一抽象层级上操作。
- G7 基础类依赖于它们的衍生类:不要让抽象上层依赖于抽象下层,抽象上层应该对抽象下层一无所知。也有例外:比如上层严格控制了下层衍生物的数量等。
- G8 单个方法/类/模块负责通过多责任:约束尺寸,约束责任,约束信息。
- G9 Dead Code:避免永远不会执行的代码,直接删除它。
- G10 垂直分隔:局部变量和方法需要被定义在他们的使用者附近。private function应该被定义在just below他们的首次调用者处。保持自顶向下的阅读顺序。
- G11 不一致性:相似的行为应该保持相似一直的结构和命名,保持“最低惊异原则”。
- G12 杂物:无用的变量,无用的方法,无信息的注释,都应该被尽早删除。
- G13 人为耦合:解耦那些不必要的、不小心造成的耦合关系。
- G14 位置不当:类中的方法应该尽量只使用此类中的变量和其他方法,如果某方法更多地使用其他类中的变量和方法,那么它可能需要挪动位置了。
- G15 传递boolean参数:不要将boolean值作为方法参数,这意味着该方法负责不只一件事。
- G16 含混的意图:代码自身要意图明确。
- G17 位置不当:一些变量和方法应该正确地放置在它们最被期待的地方。“最低惊异原则”
- G18 不适当的static:某些方法适合static,有些不适合,正确处理它们。
- G19 变量意图不明:使用有解释力和变现力的变量,如合适命名等,增强可读性。
- G20 方法意图不明:使用有变现力的方法名,增强可读性。
- G21 对算法理解不明
- G22
- G23 建议使用多态来代替if/else和switch/case,当后者重复超过1次时,考虑使用多态替换他们。工厂模式可能有所帮助。
- G24 建议遵循标准惯例
- G25 建议使用 Named Constants 代替魔数:魔数自身的变现力低,使用具名常量增强表现力。
- G26 建议当你首次完成系统时,对细节的完成尽可能详细,不要偷懒。
- G27 良好结构和惯例之间的取舍,前者优先
- G28 建议封装条件:将if/else和switch/case处的条件式,用独立而良好命名的方法重新表述,使条件处永远只有一行,并且让那个独立的方法的名字,替你解释此处条件是什么。
- G29 避免否定条件:首先使用肯定条件。
- G30 方法只做一件事
- G31 隐藏临时耦合
- G32 别随意
- G33 封装边界条件:将边界条件集中、封装,不要让他们散落各处
- G34 方法只活动在同一层抽象上
- G35 让配置信息和数据,保持在高层抽象上,不要定义在底层抽象上,通过参数逐层传递它们。
- G36 避免多级传递:遵守Law of Demeter
-
Java
- J1 包引入:如果引入了一个package中的超过两个成员,那么直接引入整个包。
- J2 不要继承常量:不要将常量声明在基类或接口中,然后继承它们。将常量声明在子类中。
- J3 使用枚举代替一系列常量。
-
命名 Naming
- N1 选择有描述力、表现力的名字
- N2 在命名时体现不同抽象层级
- N3 如果可能,采用某些标准惯例下的命名
- N4 明确命名,避免含混用词
- N5 相关域越大,其命名可以越长
- N6 避免编码:??
- N7 不要将方法不会去做的事,体现在其命名上
-
测试 Tests
- T1 不充分的测试
- T2 使用IDE提供的测试覆盖工具
- T3 不要跳过那么低级的、显而易见的trivial测试
- T4
- T5 细心测试边界条件
- T6 当你发现bug时,正儿八经地测试它
- T7 测试失败pattern
- T8
- T9 测试需要快速
-