「原创」你为什么重构?Or not?让百度告诉我们
当我尝试这样询问身边的同事的时候,我发现他们要么根本不知道重构是什么,要么把重构和重写混为一谈,更有甚者在知道了重构的定义以后,直接粗暴的说,那我还不如重写!
所以我决定换一个方式,利用网络搜索工具,听到更多的人的意见。
关键词:“周围的同事不愿意代码重构怎么办?”
搜索工具:百度
最开始我想知道的主要是:人们为什么会那么反对重构。
所以这个关键词具有一定的指向性,但48页的搜索页结果,其实已经消除了这个指向性,其中充斥着大量比我更为狂热的重构支持者。
最后需要说明的是,为什么采用中文搜索工具?
按我的经验,搜索技术话题,英文最好,中文经常是大量重复,而仅有的几篇内容也通常流于平庸。但这次,我仍然选择中文,理由有二:
1.我生活工作在中国,所以我想知道的答案应该更多来源于读写说用中文的人。
2.虽然我用英语阅读技术资料不会特别困难,但我预测这次的搜索统计需要阅读大量文章,所以用中文会比英文更加流畅。
当然,最重要的还是第一个理由。
对于每一篇文章或者帖子(重复的不算),一般来说,都会有作者对重构的态度,此外,帖子回复者,文章评论者,也会表达自己的意见。作者和每个回复,评论者都作为一个独立的个体。我把他们的观点提炼,列在表格里,出现重复的计数。
最后,再做统计,以期得到最多人关心,最多人介怀的地方。
这里需要说明的是,这个统计的数值不能作为一个精确的结果。因为,是我在对内容做过滤和提取,不管我如何试图客观,仍然有所难免。此外,由于工作跨越的时间比较长,我在不同状态下的提取和过滤结果也不尽相同。
我这么看待这个结果,它就像给类似于喜爱,厌恶,满意等的程度做个分值量化,0~5分比如说。数值的绝对值本身没意义,重点在于数值的相对大小。
百度搜索统计结果
按照上一目提到的方式,大量统计了48页百度搜索页的表格后,按照四个方面去总结统计结果。
1、为什么反对重构?
2、为什么支持重构?
3、重构需要的条件;
4、重构需要注意的事项;
核心的是前两个,它们完全相对。有一种可能是,同一种论述、客观事实都将被双方引用。反对者认为它是反对重构的原因,而在支持者眼里,它可能会成为需要注意和提前准备,扫除障碍的点。
在这种情况下,我们不认为这是简单重复,因为事实上这些重合点,很可能正是整合反对者和支持者的关键区域。支持者可以通过提出针对它们的有效处理方式,消除反对者的疑虑,从而推动双方达成某种程度的一致。
1.为什么反对重构?
1-1.重构即便成功,它也不产生直接效益(毕竟不改变现有软件功能),它也只能让开发者证明自己的能力,只对开发有作用,而对其他环节毫无价值——而那些环节可能恰恰是真正价值所在)。
1-2.即使经过重构的代码,当变化来临时,或者产生了新的想法和意见时,你又得继续重构,这容易产生一种强迫症,没完没了。
1-3.重构好的代码能否达到原来的水准(或是否能比原来好?),是否会改变原来的成果;/ 可能会引入新BUG,需要额外时间去修复
1-4.该系统很关键;
1-5.你对系统来说,或者说对于团队,项目,语言来说,是新手;
1-6.你没有时间去充分理解,但代码逻辑对你来说很复杂/你其实不了解业务;
1-7.积极重构,然而却引发了系统崩溃等严峻的后果时,是难以承受的职场压力;
1-8.好的重构成功率并不高/重构不容易/很难按照小步骤的计划去执行;
1-9.目前并没有出现性能瓶颈,而且性能优化应该向后推;
1-10.根本就没得救了,干脆推倒重写;
结论:
重构本身是有风险的,轻则做的不如从前,重则导致崩溃死机。
另外,重构的实施需要投入很多东西,但重构即使成功也不会带来什么直接效益,相反,实施重构需要相当的资源投入(详见下列第三点:重构的条件)。
很多时候,重构要求实施者对项目具有相当的了解,和技能方面相当高的要求。
没有直接好处,却有风险,而且对人员资源要求高,自然也就反对重构了。
2.为什么支持重构?
2-1.因为原有的代码经过很多人的修改,风格混乱,接口和结构混乱,各模块耦合地非常厉害),难以理解,而重构可以使其清晰。
2-2.(主观)感觉很好很酷,代码很漂亮,结构好懂。或因为完美主义,为重构而重构。
2-3.(成功的重构)可以证明自己的能力;
2-4.当前的代码很复杂,而我确实已经经过完全分析,可以做得更简洁。
2-5.不知道原作者的意图和这么实现的原因,甚至有的时候原作者已经离职,找不到人/接手的代码本身就有错误或者不完整;
2-6.重构即使一开始花费了时间,但长远来说,还是速度更快,也更利于维护、修改和扩展
2-7.代码量减少(尽管不是根本目标,但至少是一个系统良好的信号)
2-8.BUG少了,不那么容易死机崩溃了。
2-9.有时直接就顺带解决了新需求(或旧BUG) ——但这样真的好吗?
2-10.为即将的重写做准备;
结论:
大多数时候,遗留代码都是腐烂的:风格混乱,结构接口混乱,模块耦合严重,代码难以阅读,而且缺乏文档,原开发者可能不在或者忘却了实现意图......在这种情况下,无论是维护和扩展都极其困难,而重构尽管一开始需要花费更多时间,但是重构后的代码会更加清晰,对后期维护和扩展都有提速作用。
只是需要注意的是,很多时候,风格或者代码混乱这种事情,是很主观的,好或者坏,都缺乏量化评价标准。
3.(意欲)重构的条件;
3-1.个人足够的(技术)能力;
3-2.改动的代码需要在自己控制的范围内(比如该软件部分是你主导)
3-3.对原有的项目、代码要有足够的了解;
3-4.了解代码涉及的实际业务本身;
3-5.要明确原有代码坏在哪里?要明白重构后的目标,并列出一个清晰的重构方案。
3-6.要对重构可能引发的危险进行评估,同时还有需要的时间、人力和测试的更多投入;
3-7.重构完后必须做完善测试,并有时间做回归测试;
3-8.需要和原作者沟通,并最好得到认可;重构完成后,要进行代码走查,最好有原作者参与;
3-9.重构完成后,要长期跟进,直到大多数参与人员都能明白为止;
总结起来:
1.对项目本身要了解,尤其是涉及的实际业务(这方面,笔者有惨痛经验);
2.明确重构的目标,制定具体方案,了解并评估其风险;
3.重构需要相当的时间人力测试等资源投入;
4.最后一点很重要也是长期被忽略的,与人的沟通,重构涉及的最重要的沟通是与代码的原作者或者所在的团队人员;
4.重构需要注意的事项:
4-1.重构要小步伐进行;
4-2.把重构控制在一个尽可能短的周期内完成;
4-3.除非是一些轻量的重构动作(如重命名),否则一个时间段内只该做一个重构;
4-4.改动要从 轻的小的重构动作(如整理代码外观和重命名函数)到 较大的重构动作(如抽取重复的代码,提炼方法、类等)过渡;
4-5.改动要尽可能独立化,或者说其他措施,已保证很容易哪里出了问题(并回到安全点)。
4-6.涉及框架和结构的重构很难;
4-7.当你试图重构时,如果需要和人协作,并且对方比你资深而且拒绝重构,将很困难;
4-8.当你的重构得不到支持(而你还是坚持要做的时候),你一定会忐忑不安,害怕出错,害怕出错后被指责。
4-9.在得不到支持的情况下,你可能需要先重构,并且自己完全测试,再和别人说(这真的不是好事)。
4-10.在项目时间很紧或者项目尾期的时候,不宜重构;
4-11.重构必然是会引起时间延迟的。
重构,真的不是万灵丹
由上可见,我们至少可以得出一些简单结论:
1.重构,即使成功,它也不会带来什么直接效益,它只对开发者有用,而对项目的其它环节则没有;
2.重构带来的这些好处,说实在的,很多也只是出于开发者主观的看法:好懂、清晰都不是客观存在,难以界定和衡量,所以很多时候,很难证明这到底是真的对开发有帮助,还是只是开发者的个人臆想。
(关于MF等人总结出来的 一些“代码坏味道”以及相应的解决方法,后续将有专门论述,论述其中一些处理手法其实没有本质帮助——比如对含有大量局部变量和全局变量采用 方法提升为类 时,把局部变量提升为类的变量的办法,来减少提取后的新方法之间传递的参数减少,但实际上,这些变量仍然混杂在类内部,和一个小模块或者长函数中的全局变量没有太大区别,个人认为,这种转变,意义不大。)
3.要进行重构并不容易,比如准备完善的测试(尤其需要自动化测试)就是一道很大的关卡,此外,它需要重新投入相应的人力、时间、测试资源,也会对这些方面新添压力。
4.在真正了解项目代码,尤其是涉及的业务之前,进行重构是非常危险的,可能会摧毁已有的成果,这在职场和商业上是难以容忍的。
然而,很多时候,重构本身就有助于阅读和理解代码(或者说这是重构的一个目的,Martin Follwer 和 Michael Feather均表现出这种倾向)。
可以说这是一个典型的 鸡生蛋蛋生鸡的困境,这种困境唯一的解决方案就是,以其中一个为准绳,基准,而这一次,我们很可能必须接受 先理解再重构,因为,不理解就重构,风险更大,后果难以承担。
与此类似的,还有添加测试的困境。在Michael Feather的经典著作《修改代码的艺术》一书中提到,遗留代码可以定义为缺乏测试的代码,而为了给这些遗留代码添加测试,很多时候需要首先做重构,来解开难以测试的部分。
又是一个鸡生蛋蛋生鸡的困境。
好吧,不得不承认,这才是真正的软件世界。
重构未动,测试先行
在上述的分析,以及我自己对已有经历(惨痛教训)的反思后,我终于意识到,重构不仅不是万灵丹,还是一件相当危险的开发工具。
所以,下一次,再有这种想法,我会努力按捺自己内心强烈的重构的欲望,但我仍然认为,重构是一柄解决很多遗留代码的神兵利器,不能因为它危险,它曾经给我带来过伤害,我就避之不及。凡事都有两面,重构也一样。何况,重构如刀剑,用的好坏全在于人。
在上述的调查中,我们也得到了比较完整的关于 重构应该怎么做,和怎样可以避免重构的危险的一些很具体的措施。
在这其中,特别引起我注意,和需要强调的就是,测试。
是的,测试。
只有测试,才可以真正确保我们到底做了什么,破坏了什么,成就了什么。
就算不重构,测试依然神兵利器
事实上,除了重构这种开发思想,在极限编程,在敏捷开发里,或者干脆就是TDD(测试驱动开发)里,测试都被强调,并且处在一个非常重要的中枢位置。
这,是完全可以理解的。
如前所说,只有测试,才可以真正保证我们对代码做了什么,发生了什么改变。已有的是否被破坏,出现的问题是否被解决,要新添的功能是否已经完成。
当然,测试并不容易。和重构一样,但这里已经就重构展开地够多了,因此,关于测试的内容,有机会,我们将在后续中提及。
关于这次反思重构的所有内容,到此结束。
END