首先需要对“重构”(refactoring)这个词作一个明确的定义。按照《重构》书中的定义:
第一个定义是名词形式:
重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
“重构”的另一个用法是动词形式:
重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
上述定义中,第一个值得注意的关键词是“不改变软件可观察行为”。
第二个关键词是“重构手法”。一次重构之所以能成为一次重构,它必须遵循一定的重构手法,而不是随便调整。很多人在阅读这本书的时候只看前四章,真正动手改代码的时候根本不遵循重构手法,这样的行为也不是重构。我在这里采用一个较为严格的限定:只有遵循已经发表的重构手法时,才能认为是在进行重构。
第三个关键词是“软件内部结构”。这实际上是非常有玄机的一个词。玄机我们暂且按下不表,从Opdyke的论文到《重构》再到《修改代码的艺术》,重构理论的早期奠基作品谈论的都是单进程、单代码库的代码重构。因此,我倾向于将“软件内部”限定为“单进程、单代码库内”。
以下内容出自《重构》第2.2节。我用粗体标注出了一些关键词,这些关键词将有助于我们理解重构技术背后的假设。
重构改进软件设计
如果没有重构,程序的设计会逐渐腐败变质。当人们只为短期目的,或是在完全理解整体设计之前,就贸然修改代码,程序将逐渐失去自己的结构,程序员愈来愈难通过阅读源码而理解原本设计。重构很像是在整理代码,你所做的就是让所有东西回到应该的位置上。代码结构的流失是累积性的。愈难看出代码所代表的设计意涵,就愈难保护其中设计,于是该设计就腐败得愈快。经常性的重构可以帮助代码维持自己该有的形态。
重构使软件更容易理解
所谓程序设计,很大程度上就是与计算机交谈:你编写代码告诉计算机做什么事,它的响应则是精确按照你的指示行动。你得及时填补“想要它做什么”和“告诉它做什么”之间的缝隙。这种编程模式的核心就是“准确说出我所要的”。除了计算机外,你的源码还有其他读者:几个月之后可能会有另一位程序员尝试读懂你的代码并做一些修改。我们很容易忘记这第二位读者,但他才是最重要的。计算机是否多花了几个小时来编译,又有什么关系呢?如果一个程序员花费一周时间来修改某段代码,那才关系重大—如果他理解你的代码,这个修改原本只需一小时。
重构帮助找到bug
对代码的理解,可以帮助我找到bug。我承认我不太擅长调试。有些人只要盯着一大段代码就可以找出里面的bug,我可不行。但我发现,如果对代码进行重构,我就可以深入理解代码的作为,并恰到好处地把新的理解反馈回去。搞清楚程序结构的同时,我也清楚了自己所做的一些假设,于是想不把bug揪出来都难。
重构提高编程速度
我强烈相信:良好的设计是快速开发的根本──事实上,拥有良好设计才可能做到快速开发。如果没有良好设计,或许某一段时间内你的进展迅速,但恶劣的设计很快就让你的速度慢下来。你会把时间花在调试上面,无法添加新功能。修改时间愈来愈长,因为你必须花愈来愈多的时间去理解系统、寻找重复代码。随着你给最初程序打上一个又一个的补丁,新特性需要更多代码才能实现。真是个恶性循环。
从这几个重构的原因中,我们看到了下列几个(我在引文中加粗的)关键词,我们可以来分析一下它们背后隐含的假设:
所以从2.2节中我们不仅可以读到为什么重构,而且可以读出作者没有明言的假设:作者在谈论的是规模较大、生命周期较长、承担了较多责任、有一个较大(且较不稳定)团队在其上工作的单一代码库。当然所有这些都是相对度量,例如多少行代码算大、生存多长时间算长,没有绝对而清晰的界定。
可能令人吃惊:重构出现的时间相当早,比Java早,比设计模式早。William Opdyke的博士论文Refactoring Object-Oriented Frameworks 发表于1992年,这篇论文是重构理论的奠基之作。(BTW,Opdyke这位老兄相当牛逼,他的博士导师是GoF之一的Ralph Johnson。)那么在Java都还没出现之前,重构的早期玩家们玩的是什么语言呢?答案是“Smalltalk”,传说中的“Ruby之根”——好吧,跑题了,打住。《重构》的出版是1999年,所以基本上我们可以认为,重构是上世纪90年代浮现、并在本世纪头十年引起重视的技术。
本文节选自《重构》一书译者熊节的文章,原文链接见 https://www.zhihu.com/question/19552812/answer/79635260