整洁代码之道 17 味道和启发

对整本书中提到的优化手段做了一系列总结

17.1 注释

17.1.1 不恰当的信息

  1. 注释只应该描述对应代码和设计的技术信息,不要说废话

17.1.2 废弃的注释

  1. 废弃的注释会远离它们曾经描述的代码,更有可能这些代码早已经不存在了

17.1.3 冗余注释

  1. 注释应该描述代码本身无法描述清楚的信息,不要重复的描述

17.1.4 糟糕的注释

  1. 如果要编写一条注释,就应该花时间保证写出最好的注释

17.1.5 注释掉的代码

  1. 如果你现在认为这段代码没用了,请直接删掉
  2. 版本管理工具会帮你记住这段代码,所以完全没必要让它留下,这只会带来混淆

17.2 环境

17.2.1 需要多步才能实现的构建

  1. 构建系统应该是单步的小操作
  2. 应该能够用单个命令导出系统,并用单个指令进行构建

17.2.2 需要多步才能做到的测试

  1. 应该能够用单个操作就可以运行全部的单元测试

17.3 函数

17.3.1 过多的参数

  1. 函数的参数数量应该尽可能少
  2. 三个以上的参数应该尽可能避免
    • 这个我目前还很难做到

17.3.2 输出参数

  1. 输出参数是一种违法直觉的行为
  2. 参数应该只用来输入,而不是输出
  3. 如果函数一定要改变输入参数的值,用来给后续步骤使用,那么应该改为用返回值

17.3.3 标识参数

  1. 一个函数应该只做一件事,而布尔值参数的传入就表明这个函数做了不止一件事

17.3.4 死函数

  1. 永远不被调用的方法就应该被丢弃

17.4 一般性问题

17.4.1 一个源文件中存在多种语言

  1. 理想的源文件应该只包括一种语言

17.4.2 明显的行为未被实现

  1. 如果明显应该实现的行为没有被实现,那么函数的名称就相当于没有真实表达代码的含义

17.4.3 不正确的边界行为

  1. 不要依赖直觉,追踪每种边界条件,并编写对应的测试

17.4.4 忽视安全

  1. 很多程序员习惯性的忽视 IDE 给出的 WARNING 警告,这非常危险,应该尽其所能消灭所有文件中的警告信息

17.4.5 重复

  1. 依据 DRY 原则( Don’t Repeat Yourself )

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

  1. 将较低层级的概念放在派生类中,将较高层级的概念放在基类中
  2. 良好的软件设计要求分离位于不同层级的概念,并将它们放在不同的容器中

17.4.7 基类依赖于派生类

  1. 基类需要依赖派生类,这显然是本末倒置的

17.4.8 信息过多

  1. 设计良好的接口提供的函数不会需要依赖其他函数

17.4.9 死代码

  1. 在设计改变时,死代码不会随之更新,虽然它还是能通过编写,但这段代码对程序本身已经没有用处,应该毫不犹豫的删除

17.4.10 垂直分隔

  1. 变量和函数定义的位置应该离它们被使用的位置尽可能的近一些

17.4.11 前后不一致

  1. 遵循 最小惊异原则 ,做到从一而终

17.4.12 混淆视听

  1. 没有用到的变量,从不调用的函数,没有信息量的注释,都应该被删除

17.4.13 人为耦合

  1. 不互相依赖的东西不该产生耦合
  2. 两个没有直接逻辑关联的模块不应该存在耦合

17.4.14 特性依恋

  1. 类的方法只应该对自身类中的变量和函数感兴趣

17.4.15 选择算子参数

  1. 使用多个函数,比向单个函数中传递布尔参数来选择函数行为要更清晰

17.4.16 晦涩的意图

  1. 代码要尽可能具有表达力

17.4.17 位置错误的权责

  1. 软件开发者做出的最重要决定之一:这段代码应该放在哪

17.4.18 不恰当的静态方法

  1. 通常应该倾向于选用非静态方法
  2. 如果的确要使用静态方法,请确保这个方法后期不会有多态的需求

17.4.19 使用解释性变量

  1. 只要把计算过程打散成一系列良好命名的中间值,就算是不透明的模块也会变得透明
  2. 但在这之前需要考虑为什么这段计算过程会不透明

17.4.20 函数名称应该表达其行为

  1. 如果必须查看函数的实现才知道其用途,说明应该换一个更好的函数名

17.4.21 理解算法

  1. 编程是一种探险,在开始使用某个工具实现功能之前,先尽可能了解这个工具能够做到的事情
  2. 不要盲目的用自己当前掌握的知识直接解决问题,请先阅读文档
  3. 通常工具中的实现会比你自己想的要更好

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

  1. 如果某个模块依赖于另一个模块,这种依赖应该是物理的,而不是逻辑的

17.4.23 用多态替代 if / else 或 switch / case

  1. 对于给定的选择类型,不应该有多个 switch 语句
  2. 如果有多个,请使用多态

17.4.24 遵循标准约定

  1. 每个团队都应该遵循基于通用行业规范的编码标准

17.4.25 用命名常量代替魔术数

  1. 魔术数不仅仅是说数字,这是泛指任何不能自我描述的符号

17.4.26 准确

  1. 表达意思含糊或不准确的代码,要么优化,要么删除

17.4.27 结构甚于约定

  1. 使用强制的结构太替换脆弱的命名约定

17.4.28 封装条件

  1. 应该把条件判断语句中的复杂表达式,抽离成方法

17.4.29 避免否定性条件

  1. 否定式比肯定式难理解,尽可能将条件表示为肯定形式

17.4.30 函数只该做一件事

  1. 遵循单一权责原则

17.4.31 掩蔽时序耦合

  1. 译者又开始文绉绉的让我受不了了,什么掩蔽?直接说隐蔽不好吗???
  2. 如果代码存在调用顺序上的耦合,就应该明确表示,放着出现因为调用顺序出错而导致的报错

17.4.32 别随意

  1. 构建代码需要理由,而理由应该和代码结构相吻合

17.4.33 封装边界条件

  1. 边界条件难以追踪,所以应该把处理边界条件的代码集中到一起,不要散落在代码中

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

  1. 当根据抽象界限对函数进行拆解时,经常会发现隐蔽在之前的结构中的新抽象界限

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

  1. 可配置的数据通常具有通用性,放置在较高层级则可以保证其通用性

17.4.36 避免传递浏览

  1. 模块本身不要了解太多协作者的信息,而应该让协作者提供给模块所需要的全部服务

17.5 Java

17.5.1 通过使用通配符避免过长的导入清单

  1. 现代 IDE ,例如 IDEA ,已经可以做到自动优化导入清单

17.5.2 不要继承常量

  1. 别利用继承来使用常量,例如将常量放置在基类中
  2. 应该直接使用常量类实现静态导入

17.5.3 常量 vs 枚举

  1. 如果你感觉某个地方需要一个常量,请使用枚举

17.6 名称

17.6.1 采用描述性名称

  1. 事物的意义随着软件的演化而发生变化,所以要经常对变量、方法的名称进行重新评估

17.6.2 名称应与抽象层级相符

  1. 类和方法的名称应该能体现当前的抽象层级

17.6.3 尽可能使用标准命名法

  1. 使用名称基于现存的约定,会更容易理解
  2. 与项目直接相关的名称越多,后来接收代码的人就可以更快的理解

17.6.4 无歧义的名称

  1. 选用不会混淆方法或变量的名称

17.6.5 为较大作用范围选用较长名称

  1. 名称的长度应该和其作用范围成正比

17.6.6 避免编码

  1. 不应该在名称中包含类型或作用范围信息,例如 m_score

17.6.7 名称应该说明副作用

  1. 名称应该说明函数、变量或类的一切信息

17.7 测试

17.7.1 测试不足

  1. 一套测试应该测到所有可能失败的情况
  2. 只要还有没被测试过的条件,或是还有没被验证过的计算,就说明测试不足

17.7.2 使用覆盖率工具

  1. 覆盖率工具可以直观的汇报测试不足的模块、类和方法

17.7.3 别略过小测试

  1. 小测试易于编写,其文档上的价值高于编写成本

17.7.4 被忽略的测试就是对不确定事物的疑问

  1. 如果因为需求不明确而不能确定某个行为细节,可以用注释掉的测试来表达疑问

17.7.5 测试边界条件

  1. 特别注意测试边界条件,算法的中间部分正确,但边界判断错误的情况很常见

17.7.6 全面测试相近的缺陷

  1. 缺陷趋向于扎堆,在某个方法中发现一个缺陷时,最好全面测试那个方法

17.7.7 测试失败的模式有启发性

  1. 通过找到测试用例失败的模式来诊断问题所在

17.7.8 测试覆盖率的模式有启发性

  1. 查看没有被测试过的代码,通常可以发现其他测试代码失败的线索

17.7.9 测试应该快速

  1. 慢速的测试是不会被运行的测试

17.8

  1. 专业性和技艺来自于驱动规程的价值观
发布了418 篇原创文章 · 获赞 47 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/asing1elife/article/details/102874214