浅谈代码重构

2019/10/29      2226 文(wén)章来源:优百 丨 作者:史禹哲

什么是代码重构?

在Martin Fowler的名著《重构:改善既有(yǒu)代码的设计》中对重构做了如下定义:
重构(名词):对软件内部结构的一种调整,目的是在不改变软件可(kě)观察行為(wèi)的前提下,提高其可(kě)理(lǐ)解性,降低其修改成本。
重构(动词):使用(yòng)一系列重构手法,在不改变软件可(kě)观察行為(wèi)的前提下,调整其结构。

由此可(kě)见,代码重构的根本意义在于提高代码的可(kě)理(lǐ)解性以及降低修改成本,也就是说代码重构前后,功能(néng)保持不变,性能(néng)有(yǒu)可(kě)能(néng)提升也有(yǒu)可(kě)能(néng)降低。

為(wèi)什么要代码重构?

那么一定有(yǒu)人会问我们又(yòu)為(wèi)什要去做这种既不能(néng)提升性能(néng)又(yòu)没有(yǒu)提供新(xīn)功能(néng)的事情呢(ne)?
结合公司现有(yǒu)的项目情况以及实际工作遇见的问题,代码重构所带来的的好处不是短期的,而是要从長(cháng)遠(yuǎn)的角度去考虑。一般情况下一个软件从最初开发的时候开始就不是一个人能(néng)够完成,而且通常需要很(hěn)多(duō)程序员进行后续的维护,那么代码是否通俗易懂就显得尤為(wèi)重要。
在软件在制造之初,我们会对其进行精心的设计,讨论技术方案,制定命名规则以及代码规范,然后程序员根据产品设计的需求进行开发。此时的代码架构是良好的,并且代码也具备很(hěn)好的可(kě)扩展性。开发完成之后软件就要经受真正的挑战了,交付给客户,此时客户会提出大量的需求,也有(yǒu)可(kě)能(néng)会发现一些潜在的BUG。尤其是项目周期较短的项目,此时就需要程序员在短时间内修改尽量多(duō)的BUG完成尽量多(duō)的需求开发,通常这时候的程序员是在没有(yǒu)完全理(lǐ)解软件的整體(tǐ)架构及设计情况下,就着手去做的。于是,就会出现越来越多(duō)的随意的对象命名,混乱的排版布局,没有(yǒu)一行注释的功能(néng)模块。那么当新(xīn)人来维护这套代码的时候,会发现源码晦涩难懂,架构混乱不清,也因此越来越难通过阅读源码来理(lǐ)解最初的设计意图。此时维护软件的难度及时间都会大幅度提升,而定期的代码重构可(kě)以在一定程度上避免这种情况的发生,从而降低运维的成本。

何时进行代码重构以及如何重构?

当我们发现阅读源码已经比较困难的时候,再准备大刀(dāo)阔斧的开始重构代码吗?严格来说这个时候已经有(yǒu)些晚了,重构应该是在工作中每时每刻都在做的。以下的几个时间节点是我认為(wèi)的比较合适做代码重构的时机

添加新(xīn)功能(néng)时

在拿(ná)到新(xīn)需求时,可(kě)以不必急于动手,先想一下整个系统中是否有(yǒu)相似的功能(néng)模块,结合公司现有(yǒu)的主数据项目,物(wù)料分(fēn)类是一个树形结构的主数据,物(wù)料模板以及物(wù)料代码都需要通过物(wù)料分(fēn)类这个树形结构来进行查询以及快速定位,那么我们可(kě)以在物(wù)料模板跟物(wù)料代码页面上增加物(wù)料分(fēn)类的树形结构,此时如果模板先前已经将分(fēn)类加在了页面中,我们在对代码页面进行优化的时候,可(kě)以直接将模板中的方法复制过来稍加修改就使用(yòng),但这意味着如果将来某一个新(xīn)的主数据也需要关联物(wù)料分(fēn)类的类别树时,你只能(néng)再一次的复制修改。同时,如果这块功能(néng)需要完善并进行修改时,你也需要找到所有(yǒu)曾经复制的地方,并进行多(duō)次修改。而此时如果将先前的模板里已经写好的代码抽成函数,并稍加修改,此时的工作量可(kě)能(néng)会比直接复制要多(duō),但是却大大节省了后续维护的成本,此时如果再有(yǒu)别的地方需要用(yòng)的这个功能(néng),那么我们就只需要调用(yòng)这个函数并传参

修改BUG时
主数据系统多(duō)个主数据功能(néng)模块间业務(wù)层面上功能(néng)差距较大,但实际底层代码上,很(hěn)多(duō)地方的逻辑都大同小(xiǎo)异。这也就导致有(yǒu)的时候业務(wù)人员发现了某个BUG,开发人员改完之后,业務(wù)人员又(yòu)在另一个主数据模块发现了同样的BUG。此时我们可(kě)以审视一下系统,将相似的功能(néng)模块进行测试,在排除BUG的同时,我们可(kě)以将所有(yǒu)相似的功能(néng)模块进行重构,毕竟修改一处总是要比修改多(duō)处省时的多(duō)

有(yǒu)时我们修改的BUG可(kě)能(néng)一开始并不是我们自己所写的,那么此时我们要先通过阅读源码来理(lǐ)解这段代码,如果在阅读的过程中发现代码结构十分(fēn)糟糕,对象及变量命名一塌糊涂,那么此时我们也可(kě)以参照命名规则及代码规范对代码进行重构

性能(néng)优化时
首先我们要明确代码重构并不能(néng)带来性能(néng)的提升,相反有(yǒu)可(kě)能(néng)会导致性能(néng)下降。但是这并不影响我们在性能(néng)优化时进行不影响性能(néng)的代码重构

记得某一个项目客户反映主数据系统页面查询太慢,我们调查代码发现,此类主数据在查询时,有(yǒu)大量的外键关联,上一任开发人员写了过多(duō)的循环嵌套,导致页面上仅仅只显示20条数据,后台却查了100多(duō)次数据库。此时发现,除了通过在数据库中增加索引以提升性能(néng)外,我们还应该对代码进行重构,减少嵌套,优化逻辑,提高代码的可(kě)读性

代码检查时

在做主数据开发的时候,因為(wèi)功能(néng)模块较多(duō),工作量较大,所以整套程序必然是由多(duō)个开发人员共同完成,那么此时代码检查就显得尤為(wèi)重要。通常我们可(kě)以通过代码检查来确保系统是完全按照开发规范以及命名规则来开发的,同时我们也可(kě)以在代码审核中发现一些冗余代码。每一个人拿(ná)到的需求都是自己那一个模块的需求,然而通常都会存在跨模块间有(yǒu)相同或相似的功能(néng),那么当代码检查时发现这种情况时,我们可(kě)以对这部分(fēn)功能(néng)进行代码重构

是否所有(yǒu)的代码都要重构?

那么有(yǒu)没有(yǒu)什么时候是不应该重构的?答(dá)案是肯定的。毕竟我们在一开始就提到了代码重构的意义是提高代码的可(kě)理(lǐ)解性以及降低修改成本,所以当你发现某一块代码重构的代价已经比重写还要大了,那么当然就没必要重构了。如果某一块代码你不需要使用(yòng),而且也不存在BUG需要修改,那么也没必要重构,毕竟世界上没有(yǒu)什么是绝对完美的