最近看到有同学提问:“代码重构有意义吗?”,“关于代码重构有什么好的方法论吗?”,个人对代码重构非常感兴趣,在13年就开发接触代码重构的概念,学习相关理论方法,一直在坚持实践,现在基本已养成一种习惯了,所以周末系统梳理了重构原理、相关概念和操作技巧,抛砖引玉,跟大家分享交流。
Refactoring是对软件内部结构的一种调整,目的是在不改变外部行为的前提下,提高其可理解性,降低其修改成本。
《重构》里有一段话非常有启发性:“一开始我所做的重构都像这样停留在细枝末节上。随着代码渐趋简洁,我发现自己可以看到一些以前看不到的设计层面的东西。如果不对代码做这些修改,也许我永远看不见它们,因为我的聪明才智不足以在脑子里把这一切都想象出来。Ralph Johnson把这种‘早期重构’描述成‘擦掉窗户上的污垢,使你看得更远’。研究代码时我发现,重构把我带到更高的理解层次上。如果没有重构,我达不到这种层次。
容易理解的代码可以很容易的维护和做进一步的开发。即使对写这些代码的程序员本身,容易理解代码也可以帮助容易地做修改。程序代码也是文档。而代码首先是写给人看的,然后才是给计算机看的。
重构可以从很大程度上去辅助设计,通常情况下我们的设计不是能贯穿我们软件开发全过程的,在这个过程中,我们的需求变更的可能性非常大,当需求变了,设计可能也需要变,但是我们已有的实现怎么办?全部推翻也不太现实,这时候就要依靠重构来解决这种矛盾。
关于重构,有一个常被提出的问题:它对程序的性能将造成怎样的影响?重构并不意味着性能更好,但是重构可以让性能优化更容易!
首先,一个构造良好的代码让你有足够的时间进行性能调整,因为它你可以更快速地添加功能,也就有更多时间用在性能问题上(准确的度量则保证你把这些时间用在恰当的地点)。
其次,面对构造良好的程序,你在进行性能分析时便有较细的粒度,于是度量工具把你代入范围较小的程序段落中,而性能的调整也比较容易些。由于代码更加清晰,因此你能更好地理解自己的选择,更清楚哪种调整起关键作用。
引用“重构的七宗罪” https://insights.thoughtworks.cn/refactoring/
重构经过了十几年的发展和应用,可以说它是极限编程中程序员最爱的实践之一了,但实施过程中可能走偏,最终事与愿违
不清楚重构目标,不清楚重构范围等,浪费时间在简单的个人偏好上进行代码修改,要搞清楚什么是重构,用它来解决什么问题?
重构是整理代码保持轻装前行的重要手段,然而我们也需要能够明确知道重构要做什么,最终的产出如何验证。在重构开始前可以指定具体的衡量指标,比如消除多少行重复代码、代码复杂度降低多少,代码耦合度降低多少等,有清晰的目标就知道什么时候算重构完成
很多人都认同这一观点。但对遗留的应用软件、构筑过半的项目却容不得推倒重来。以我观察,在开始重构时仅凭自己对代码的理解就进行剪切、复制、删除、添加等大刀阔斧修改的人不在少数,尤其还没有完全掌握重构手法的新同学。结果当然错误百出,导致Bug一堆。修复这些错误代码少则几个小时,多则几天,这不是重构,这是重写。重构是一种经千锤百炼形成的有条不紊的程序整理方法。在《重构》一书中Martin明确提出了68个代码级别的重构手法,这些手法都是等价的。在重构的过程中即使错了也没关系,都可以安全回退,重新开始。其中比较常用的手法就是桥接,如当我们要删除一个方法的时候,会新添加一个方法,然后将它的引用逐一的迁移过去,直到旧方法成为孤岛,就可以将它删除了。它能保证重构前与重构后的程序代码功能完全一致,从而实现安全重构。下文会介绍重构技巧。
重构过程中,经常出现为了消除一个坏味道,改了A类的方法,又改了B类的变量,不得不改了C类;最后发现这三者之间还有依赖,导致进行不下去了,波及面越来越广,时间越来越长,PM还在催,最后不得不放弃所有的代码。调整一个正在运行中的系统也如治国,不要期望一次性调整到漂亮的代码或架构,而是要遵循“小步前进”的方法。从问题着手,每次重构一小步。针对一个问题有目的修改,修改完后测试,测试通过后提交代码,再进入下一轮重构。如果在改动过程中发现了其他需要修改的地方,不要顺便重构,你可以把它记下来,作为下一轮重构的内容。
这种做法在代码和模块层面都是相对比较容易实践,而针对架构层次的调整就相对比较复杂。这也是很多架构师需要去思考的问题,如何渐进式重构。不要搞一下子半年一年的重构,而是以周以月为单位,快速的迭代,能够很快的验证结果获得收益。
对于简单的代码级别重构如果做得好是可以不用验证结果的,然对于模块级别或架构级别的重构,是需要的。否则会越做越不知道改的对不对,最终可能重构失败。
这个时候一个可以衡量重构的指标就体现它的价值:能时刻检验我们的成果,确认我们的重构还在解决当初的问题。目前常见的量化指标有如下四类,可供参考。
《重构》是Martin和Kent对他们多年以来整理代码的实践的总结,然这背后体现的是他们对软件技术的深层次思考和经验。很多新人执着于学习重构手法而疏于学习背后的心法,有些可惜。
Robert C Martin的《代码整洁之道》和《敏捷软件开发:原则、模式与实践》、《设计模式》、Eric的《领域驱动设计:软件核心复杂性应对之道》、《架构之美》等都是帮助大家修炼心法的不错选择,他们可以让你更深层的了解代码,更高层面看待系统,锻炼你的嗅觉,提升你的代码能力。
我们不得不承认对代码的重构是有风险的,尤其是模块或架构级别。这段代码的业务是什么,为什么当时这么设计,测试覆盖率是多少,如果这样改会不会影响到其他模块?对其他角色有什么影响?这些问题都要逐一回答。在风险相对较大的改动更要如此,需要和团队成员,各个角色进行沟通,谈论这次重构的好处和风险,获得足够的评估,从而能够做出合适的重构决策,将风险降到最低。
上文也有提到 数量: 代码的行数 质量: 代码复杂度,耦合度,可读性,架构依赖复杂度等 成本: 花费的时间 回报(成果): 支持后续功能的快速叠加,解决现有因代码设计问题无法优化的矛盾等
要进行代码重构,我们需要能识别出代码的坏味道,在《重构》一书中,作者列出了20+代码坏味道,大多数都非常认同,包括:重复代码、过长方法、条件逻辑过度复杂、分支语句等,尤其重复代码可以说是万恶之源,很多Bug都是这样来的。重构的理由
简要总结下各类重构技巧,主要分为以下几个方面:
这些技巧有些很好理解,有些比较晦涩,后续计划将常用的技巧补充一篇Code Demo,加深理解。
重构应当作为开发同学的必备技能,优秀的程序员应当尽量避免低质量的代码,最好能够把重构作为日常开发的一部分,一边开发一边重构。在快速堆叠代码实现基本需求功能的基础上,写好测试用例,保证功能不变,逐步重构。也不是所有场景的业务都需要重构,比如时效只有几天的活动页可以不用重构,但核心业务是需要长期坚持重构投入的,并且投入产出比会很高。
参考的书籍和知识点如下: