什么是重构?
重构被描述为“安全地改进现有代码设计的艺术”(Martin Fowler,参见参考文献)。因此,重构是软件源代码转换的过程。
重构不涉及添加新功能。当代码正常工作并且所有测试都通过时,应该执行重构。并且应该在添加下一个功能之前执行。因此,重构旨在找到代码中当前存在的功能的“最佳”实现。重构也不涉及重写或替换大块代码。重构是一个循序渐进的过程,旨在“保留现有代码中嵌入的知识”。
重构是紧急设计的工具。知道如何以及何时重构意味着您的代码不需要完全预先设计。重构使以后更改设计变得安全,并且确实使这样做更具成本效益。
当代码的设计“简单”到我们现在可以经济高效地完成时,重构就会停止。“简单”的一个很好的定义是极限编程中的以下规则:
- 通过所有测试。
- 传达程序员的意图,即,为每个重要概念命名。
- 一次且仅一次表达所有内容,即,它不重复任何代码、逻辑或知识。
- 包括任何不必要的东西。
这些“规则”当然是主观的,并按优先顺序在上面列出。但它们确实提供了一个很好的经验法则来帮助决定重构什么,以及是否需要进一步重构。
什么是重构?
作为一个过程的重构通常由一系列单独的“重构”组成。这些重构中的每一个(有时也称为“重构动作”)可能又由较小的重构组成,依此类推。在最好的规模下,“重构”是对代码进行最小的、安全的转换,保持其行为不变。
每个重构都有一个逆。例如,将代码片段移动到新方法中并用对该方法的调用替换原始方法的 ExtractMethod 是 InlineMethod 的逆方法。两者都不比另一个“更好”或“更差”;相反,每个都在某些情况下是合适的。
每次重构都将包含一系列步骤,其中一些可能会暂时中断软件。只有在完成所有步骤后恢复了正确的行为时,重构才完成。
一些重构有多个替代“配方”(实现重构目标的安全步骤集)。进行重构最重要的方面是找到一个能够最大限度地减少代码被破坏的时间的方法。
大多数最小的、安全的重构具有相同的基本轮廓:
- 检查重构是否可以安全执行,以及是否满足其先决条件。
- 引入一个新的代码元素。
- 找到所有需要迁移以使用新元素的现有代码元素。
- 对于要迁移的每个客户端:
-
- 迁移旧代码的客户端,以便它使用新代码。
- 运行测试。
- (可选)保存检查点。
- 删除现在不用的旧代码。
- 运行测试。
- 保存检查点。
常用的重构
“重构”是软件应用程序的任何保持行为的转换。也就是说,重构是对代码的任何更改,但不会改变它的功能。
不幸的是,这个定义非常模糊,在实践中几乎毫无用处。为了成为一个实用的工具,重构需要一个目标和一套现成的收缩包装技术。
下表显示了一小部分最知名的重构:
| 重构 | 目的 |
|---|---|
| 提取方法 | 将代码片段转换为名称描述其用途的方法。有助于提高调用者的可读性,并可能有助于减少重复。 |
| 内联方法 | 用方法体的副本替换对方法的调用。将代码集中到一个地方,为其他重构做准备。 |
| 重命名方法 | 可以帮助客户端代码变得更具可读性。如果现在更好地理解方法的职责,则通常在其他重构之后使用。 |
| 引入解释变量 | 将复杂表达式的(部分)结果保存在临时变量中。有助于提高算法的可读性。 |
| 内联温度 | 用它的值替换临时变量的使用。通常用于将代码组合在一起以准备其他重构,例如提取方法。 |
| 添加参数 | 将额外信息传递给方法。通常在较大的代码重组期间使用。 |
| 删除参数 | 删除不再由方法使用的参数。有助于保持方法的接口干净且客户端代码可读。 |
| 提取类 | 一个类有太多的职责,所以你把它的一部分分开了。新类的名称有助于代码的领域语言;可测试性和重用性也可能得到改善。 |
| 引入参数对象 | 当多个方法采用相同的一组参数时,会创建一个表示该组参数的新对象。这是另一种寻找新类和新领域概念的方法。 |
| 引入空对象 | 通过引入一个新类来表示该特殊情况来替换空检查。有助于消除重复并简化条件逻辑。 |
| 用符号常数替换幻数 | 为文字数字命名。提高代码可读性,并有助于识别算法之间的细微依赖关系。 |
请注意,其中许多是彼此的倒数。
实践中的重构
安全地做
重构涉及对代码体进行大量更改,因此会带来破坏该代码的重大风险。在开始之前确保您的代码正常工作。做到这一点的最好方法是拥有一个您信任并为您提供良好覆盖范围的自动化测试套件。使用版本控制工具并在每次重构之前保存一个检查点。这不仅意味着您可以从灾难中快速恢复,还意味着您可以尝试重构,然后在您不喜欢代码的位置时将其撤消。最后,如果您的环境有可用的重构工具,请使用重构工具。
结论
重构涉及对代码体进行大量更改,因此会带来破坏该代码的重大风险。
热点提示
- 在开始之前确保您的代码正常工作。
- 确保您拥有一个您信任的自动化测试套件并为您提供良好的覆盖范围。在每次重构之前、期间和之后频繁地运行测试。
- 使用版本控制工具并在每次重构之前保存一个检查点。这不仅意味着您可以从灾难中快速恢复,还意味着您可以尝试重构,然后在您不喜欢代码的位置时将其撤消。
- 按照上面概述的核心过程,将每个重构分解为小的、安全的步骤。
- 最后,如果您的环境有可用的重构工具,请使用重构工具。