What’s Refactoring
“Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.” - Martin Fowler
重构是在不改变软件外在功能的前提下,通过对软件内部代码及其结构的调整,改善代码质量,使程序设计架构更趋合理,从而提高软件代码的可读性,可维护性和可扩展性,降低代码的维护成本。
重构是对代码坏味道的清理,它包含着一系列已验证的重构手法,小步安全的进行。重构以测试为保障,每完成一小步,便运行测试,从而确保外部功能不受影响。
严格意义和安全的重构并不会破坏软件的外在功能或者引入新的缺陷,但是为什么在平常开发中会有很多针对重构的抱怨呢?很大一部分是因为那些所谓的“重构”实际上是重写。
What’s not Refactoring
Sometimes redesigning and rewriting code is the right thing to do. But be honest and clear. Don’t hide this under the name of refactoring. - Jim Bird
重新设计和重写有时候是正确的事情。但出于坦诚和表述清楚,请不要把这些活动赋以重构的名义。
以我观察,很多开发人员在重构时往往不使用安全的重构手法,”重构”开始后凭着对代码的理解,剪切,复制,大刀阔斧的进行修改,结果错误百出,导致测试失败。修复这些错误代码少则几个小时,多则几天,这不是重构,这是重写或者重新设计。
有时也常常听到项目组开发人员说,“我要对某某模块重构”,这其实是重新设计的信号,而不是重构。极有可能是他对这模块已经诟病很久,实在忍耐不下去了,于是有一天大呼,我要重构。
可重构不是这样的,重构是简单的、小步的、低调的、持续不断的,它与我们平常的开发工作融入一起,是不可缺少的一部分,不需要向任何人解释这部分工作,它自然的开始与结束,比如消除重复、重命名、给方法变量类一个合适的名称、删除死代码、简化复杂的条件语句、把大方法分解成数个容易理解的小方法等等,让这些小步简单的改进无声无息的发生在开发过程中,这才是重构。
重构不是宣扬技术的噱头,不能为了重构而重构。重构是清理代码,让代码更清晰易懂的措施,每次重构开始时以坏味道为驱动,消除它改进代码的质量,以测试通过为重构的结束;然后进行下一个开发活动,或继续重构或开发新功能;不要对那些没有计划进行重构的代码顺便重构,如果发现确实需要改进,那么请把它记下来,作为下一个重构的任务。
Why Refactoring
在开发维护中常有这样的对话,“哎,还不如重写得了”,“看半天没看懂”。为什么会出现这样的对话呢?很多时候是现有代码难懂,结构混乱不清晰,存在各种坏味道,而更改的代价大于重写,所以开发人员会倾向于重写。然而,重写真正能解决这个问题么?
在开始阶段,软件通常拥有良好的设计。随着时间的推移,新的需求产生了,原有的需求发生了变更,甚至有些需求被删掉,同时缺陷逐渐出现,软件经常修修补补,导致系统的设计逐渐违背了最初的想法,开始腐化,最后实现新需求的成本远远大于开发一个新系统的成本,软件的生命周期到了尽头,也就有了重写的需求。但随着系统的重写,这样的问题又会一步一步的重现。
重构就是这样一种实践,通过不断的清理代码及其结构,使软件系统对需求的变更始终具有较强的适应能力,它是阻止系统腐败变质,保持生命力的有效手段。
When to start
重构是有成本的,什么时候重构呢?并不是开发人员说“我觉得这里很不爽“所以重构,而是要有在当前讨论得上下文中合理的开始重构。重构以坏味道为驱动,可以在下面四种情境中开始。
1.No More than three
“重复是代码万恶之源“,一旦同一重复代码在系统中出现两次以上,这是一种很强烈的重构信号,需要清理它;还有一种代码,第一次碰到它感觉有些别扭,可以忍;第二次碰到它,有些难受了,再忍;第三次碰到它,那就实在忍无可忍,说明这里必须得整理,这就是重构开始得必然标志。
2. New Feature
重构可发生在添加新功能之前,通过整理代码,比如抽取方法,移动,抽取接口等,会使得添加新功能更简单有效。
3. Fixing Defects
4. Code Review
这些都是重构得良好时机。
When not Refactoring
这是一个很值得商榷的问题。对于一个具体的开发项目而言,这与项目和团队相关。如果在测试覆盖率完备的情况下,重构可以随时随地发生。但是实际开发项目中很难完全达到,在开发末期,比如上线的关键时刻可以平衡各种任务,处理优先级更高的事情。如果在末期发现有关键点需要重构,这说明重构应该更早开始。而如果当前系统功能无法满足需求,更改的成本又大于重写,那么就不要重构直接重写;
How to Refacotring
Matin重构一书中给出了重构的各种手法和步骤,总结如下:
- 首先找到坏味道,确认需要重构的代码块。
- 运行测试,保证功能正确。
- 开始重构,运用重构手法,消除坏味道。
- 运行测试,所有测试通过
- 重构结束
目前有很多重构工具(IDE+Resharper)可以帮助我们很有效的进行重构。
Summary
重构是从思维与抽象两个维度来做事情。重构需要有计划的小步前进,让它变成开发习惯的一部分,它是低调的自然的存在;而重构的具体发生需要依赖于对坏味道的辨识以及代码的抽象总结能力,需要持之以恒的练习。