重构不是重写!经历失败,才懂得何为重构

今天,人邮君邀请 @Jeff Tian 来给大家分享一下他与重构故事~


去年我换了份工作,进入了一个刚成立的数字化团队。不到半年时间,团队仅在云设施上的花费就超过了六千万。由于是新成立的团队,自有的软件开发人员严重不足,因此我们还花费了大量钞票聘请了顶级的咨询公司来协助开发。然而经历这一番“折腾”,最终项目上线后,还是Bug频出,导致了技术团队在业务部门的口碑不佳,始终抬不起头来。


然而就是在这样的时刻,技术团队却开始了如火如荼的“重构”(重写)大业……


重构不是重写!


为什么选择“重构”?其实,技术团队只是抱怨前人写的代码已经无法维护,想全部重写而已!


然而,这样前面上亿元的花费岂不是全打了水漂?更糟糕的是,重写时计划采用的方法和当初咨询师设计的没什么两样,代码质量也没有更高。


与此同时,新的业务需求不断出现,老的也已经发生了变化。在这样的情况下,怎么保证结局一定比之前好呢?

所以,这根本不是什么重构,而是重蹈覆辙,只是花两倍的经费犯同样的错误而已!


既然重构不等于重写,那么究竟什么是重构呢?Martin Fowler《重构》中开门见山地指出:

所谓重构(refactoring)是这样一个过程:在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。重构是一种经千锤百炼形成的有条不紊的程序整理方法,可以最大限度地减小整理过程中引入错误的概率。本质上说,重构就是在代码写好之后改进它的设计。
《重构》20年,重构升级


“屎山”真“香”


Martin Fowler在《重构》中定义“重构”时,还说:

哪怕手上有一个糟糕的设计,甚至是一堆混乱的代码,我们也可以借由重构将它加工成设计良好的代码。

这就是说,不存在什么已有代码无法维护,需要推倒重来的情况。从这里推论,我甚至觉得,“屎山”(指老旧的祖传代码)不仅不可怕,甚至可以很“香”。只要坚持正确地重构,即使是很老的项目代码,也可以被改造成高质量的代码。


测试是重构的第一步


构筑测试体系是重构的第一步,也是最重要的一步,所以作者Martin Fowler用了整整一章来详细论述。


程序员不是神,即使是Martin Fowler这样的大师也承认自己会犯错误。因此,他在进行代码重构之前,会确保即将要修改的代码拥有一组可靠的测试。


如果代码已经有测试了,就太好了;如果没有,就先添加测试,而不是直接修改实现代码。


正确的重构可以做到:在重构完成后,先前的可靠测试不用修改,可以全部通过。这些可靠且通过的测试,就验证了前面所说的“不改变代码外在行为的前提”。


因为重构不改变代码外在行为,所以它只是软件开发过程中的一个部分。作者借用 Kent Beck 的“两顶帽子”比喻,非常建议在开发软件时,将自己的时间分配给两种截然不同的行为:添加新功能和重构。添加新功能时,不应该修改既有代码,而是只去添加新功能;但重构时就不能再添加新功能了,此时只能调整代码的结构。


重构不应大刀阔斧


在前面提到的故事中,公司为了同样的业务需求,开发了两次,花了两份钱,实在是有点“冤大头”。重复开发两次称不上重构,同样的需求更不应该花两份钱。重构不应大刀阔斧,更不该打着“重构”的旗号,推掉业务的需求,行大规模重写之事。


真正的重构,绝大多数可以在几分钟到几小时内完成。Martin Fowler还专门举了一个极端的例子,哪怕是需要几个星期才能完成的库替换,他也不愿让一支团队专门做重构,而是采用 抽象模拟分支(Branch By Abstraction )的策略来保证每次小改动后,系统仍然照常工作。

重构的有效步骤

重构应该是小步快跑、长期进行的持续改进。因此,不应该跟业务团队说“因为我们这段时间要重构已有代码,所以需求要先缓一缓”之类的话。


作为专业人士,我们的工作是尽可能快速地创造出高效软件,至于怎么完成,那是我们自己的事。正因为我们认为重构是快速实现需求的方式,我们才会进行重构。


这一点看似有点矛盾,但我觉得,同样的需求不应该向客户收两份钱。这才是专业人士呀!否则,像我经历过的很多团队一样,第一次上线需求时收一笔钱,然后以重构为名又收了第二笔钱。由于这种“重构”根本不是重构,甚至又带来新的问题,导致需要更多的“重构”,很可能还需要客户再付第三份钱甚至更多。


注释是坏味道!


和程序员总是调侃前人代码是“屎山”一样,他们也会抱怨代码中没有注释。然而,《重构》这本书却恰恰持相反的观点,注释往往是一个明显的代码坏味道,是需要去除的。这符合我的经验,多数注释不仅无用,而且还会给阅读者明显的误导,甚至有的注释上写的和代码的实现完全矛盾!


当你有写注释的冲动时,应该先尝试使用各种重构手法,把代码的坏味道去除。当然,如果所有的重构手法都用了,仍然觉得需要注释的话,那还是可以写的。但是注释本身不是可以运行的软件,请别变成“文档工程师”哦。


除了注释,《重构》还列举了各种其他的代码坏味道以及相应的解决方法,这就是一系列的重构手法。

代码中的坏味道

终点:测试驱动开发


《重构》除了详细论述了各种代码坏味道和相应的重构手法,还专门花了一整章描写如何构筑测试体系。不仅仅是这一章,全书的各个角落都在强调测试的重要性。


尽管全书没有特别强调测试驱动开发(Test-Driven Development,TDD)这个词,但在测试驱动开发中,重构正是其中的一个环节,没有测试驱动的重构不是真的重构。


和测试驱动开发一样,《重构》也是浮现式设计的支持者。而这也正是重构的本意:通过调整代码改进原有的设计。软件之所以是“软”件,就是指它是可以改变的。在测试驱动开发中,应用好重构手法,才不会产生无法更改的“屎山”。

《重构》改进原有的设计

推荐阅读

======

赠人玫瑰,手留余香,不要忘记点赞、收藏、关注 @人民邮电出版社 哦~

一键三连,感恩有你~

发布于 2022-08-25 16:21