09|TDD中的驱动(2):重构发挥了什么作用?
从“驱动”的角度讲,TDD 并不是一种编码技术,它无法驱动你写出你不会实现的代码。TDD 是一种架构技术,它能通过测试与重构,驱动单元的划分以及功能的归属,因而是一种更为落地的架构软件的方式。
在 TDD 中,重构是和测试一样重要的驱动力,驱使我们得到更好的架构和更清晰的代码结构,因而熟练掌握几种常用的重构手法,也是十分必要的。
语义化的查找替换(Semantic Find and Replace)
首先要介绍的重构手法是提取方法(Extract Method)和内联方法(Inline Method)。这是最重要的两种重构手法,它们相当于查找(Find)/ 替换(Replace)。所不同的是,这种查找替换是语义化的:在不破坏现在代码结构的前提下,完成查找替换。
方法替换实现步骤
- Extract Method
- 旧方法delegate给新方法
- Inline Method
通过提取 / 合并单元进行重架构(Extract and Merge Units)
在提取方法的基础上,我们可以进一步将提取出的行为从当前对象中分离出去,也就是提取对象(Extract Object)。
通过这些重构手法,我们对类结构进行了调整,也就是模块的重划分和功能的重分配。这就是对已有代码的重架构(Re-architecture)。
到此为止,我们介绍了重构的基本手法。通过这一组操作,我们可以完成对于代码的修改,以及对于架构的调整。这些手法如此基础,应该被看作修改代码的基本功,而不是重构:谈不上什么消除坏味道,就是高效修改代码而已。
使用多态替换条件
组合使用这些基础手法,我们就可以进行一些更大规模的重构。比如我们之前在 Args 例子中展示过的使用多态替换条件分支。
我们消除了坏味道并强化了开放封闭原则(Open-Closed Principle,OCP):新增的类型解析不需要修改 Args 的实现,可以通过扩展 OptionParser 接口实现。对修改封闭,对扩展开放。
这种架构改进方法叫做重构到模式(Refactoring to Patterns),即:将架构上的坏味道替换为设计模式(Design Pattern)。这是一种更有效的架构软件的方法,用公认的好设计(模式)替换了公认的不好的设计(坏味道),还能满足功能的需求,必然能是更好的架构(而不用虚无缥缈地归结于“品味”或“经验”)。
对于 TDD,行业中存在这样一种困惑:从功能测试出发,逐步完成软件开发,这或许没问题。但架构怎么办?实际上,红 / 绿 / 重构循环中的重构就是解决架构问题的。只不过架构并不是预先设计的(Upfront Design),而是在完成功能的前提下演进而来的,因而也称演进式设计(Evlutionary Design)。
通过重构到模式演进式地获得架构,是一种实效主义编码架构风格(Pragmatic Coding Architect)。这是习惯了预先设计的 PPT 架构师们不曾体验过的经历,因而不被理解也是很正常的了。
顺便说一句,Joshua Kerievsky 在 2004 年写过一本书,就叫 Refactoring to Patterns 。这本书的价值远被低估了,是关于软件架构非常重要的著作!
此外,如果无法借助自动化重构工具高效修改代码,那么 TDD 带来的效率将会大打折扣。而无法支持这几个核心手法的 IDE,也不足以支撑 TDD 的实施。(VSCode对重构的支持并不友好,所以考虑采用JetBrains的系列工具。学会使用IDE完成这些重构。)