重构?每次听到这个词,头脑里面闪现的就是“推倒重做,代码重写”,那到底重构是什么玩意?所谓“外事不决问谷歌,内事不决问百度,房事不决问天涯”,百度百科上面的解释是:重构(Refactoring)就是通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。说白了就是在不改变系统功能的前提下,调整系统内部结构,优化系统性能,让这个系统能够更好的满足客户需求,同时,希望通过重构让系统重换青春,多蹦跶几年。
一、为什么要重构?
其实上面已经描述的很清楚,说简单点就是延长生命周期更好满足客户。但只改变系统的实现方式,不改变系统功能,并没有让系统变得更漂亮,也没有让用户感知到有新功能的增加,从外在用户层面来看,重构?这不就是瞎折腾啊,有那个精力,干嘛不好好实现我的需求,这岂不是在浪费公司的投入?
说的这里,作为程序开发者来说,其实心里也很憋屈,请一定要相信我们内心是纯洁的,为公司省钱是我们的唯一目的。
既然说到这里,下来说说重构的意义何在?
1、系统不堪重负
从软件的生命周期说起,软件不等同于其他普通的产品,他是一种脑洞衍生产品,没有具体的物理形态。它既不会发生物理损耗,也不会因为使用次数多而发生接触不良,那为什么从制造出来以后,却不能一直使用下去呢?
从目前的经验来说,有两大罪魁祸首:需求变更、设计不当
需求变更,一个软件系统总是为解决某种特定的需求而产生,但随着社会在进步,政策在变化,用户的业务场景以及目标群体都在变化。不同的需求,特性不一样,有些相对稳定,有些变化频繁,有些甚至消失或者转换成其他需求.
举例来说电商业务的特性:
1)、消费者流失门槛极低,需要时刻紧盯消费者的一举一动去讨好他们,偏偏人又都是喜新厌旧的动物,因此需要经常进行业务上的调整;
2)、电商已无独有秘籍,行业抄袭成风,有新的业务机会需要尽快上马,战机稍纵即逝。
这就特性决定了电商业务需求多且繁杂、时效要求极高。
这就产生了一种糟糕的现象:系统要根据需求的不停变化,来不停的进行修复,来维持其自身生命力,就算一个自以为初期设计很完美的软件产品,但随着时间的发展、需求的变化,必须不断的修改原有的功能、追加新的功能,为了实现变更,不可避免的要违反最初的设计构架,就好比一堵墙,今天打个洞,明天钻个孔,经过一段时间以后,就变成千疮百孔了。BUG越来越多,越来越难维护,新的需求越来越难实现,软件的构架对新的需求渐渐的失去支持能力,而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本,这就是这个软件系统的生命走到尽头的时候。
设计不当,过度设计以及设计不足
1、过度设计,设计超过所需,或者设计到用户根本不会用到的功能,最后只能是费力不讨好。一个原因是沟通不畅,由于在实际的项目开发当中,是每个人负责一块东西,看似分工明确,但是会使每个人都在自己的小天地工作而不关注别人是否已经完成了自己所需,最终导致大量的重复代码。
2、设计不足,可能由于时间不足,知识不足,进度压力、战线过长等原因而产生
长期的设计不足,会使软件开发的节奏变成“快,慢,更慢,不知道怎么下手”,一个可能的演变过程如下:
(1)、版本1.0,很快就交付,代码质量谈不上好,但是还好,我们的架构还是可以的,这些问题后面可以慢慢修改和完善;
(2)、版本2.0,在做新需求的时候发现原来有些东西做得真不怎么样,让这次的版本多费了很多功夫;
(3)、版本3.0,又要交付了,兄弟们,形势比较艰苦,大家咬咬牙,原来的那些功能暂时先不管了,重点保 障这次版本的内容,保障关键功能点,千万千万不能出问题
(4)、未来版本,又来了一堆新需求!!!所有小组长过来开会,评估下要怎么玩儿。。。。。
2、重构带来的好处
重构就能够最大限度的避免系统负重现象。在不改变系统的外部功能,通过重构,不断的调整系统的内部结构,使系统对于需求的变更始终具有较强的适应能力
1)、持续修正和改进软件设计。
重构过程中本身就是对原系统的重新审视,随着对业务的熟悉增强,对整个系统的把控增强,设计将更加有针对性,更加合理。对需求变更才会有较强的适应力
2)、增强了代码的可读性。
不管什么时候,编写让人能够理解的代码。保证一直会做某个系统
3)、解决潜在风险
重构过程,会逼迫你理解原先所写的代码,思考原来设计的合理性,发现其中的问题和隐患,构建出更优秀的设计和代码。
4)、提升编程的趣味性、成就感和效率
软件80%的核心功能,通过20%的代码实现。
程序猿是一种很奇怪的动物,他们愿意坐在电脑面前用几十个小时来解决一个问题来享受那么三五分钟的快感。当你发现一个问题异常复杂的时候,往往不是问题本身造成的,而是你用错了方法,走错了路。拙劣的设计往往导致臃肿的编码和让人转的迷迷糊糊的业务逻辑。改善设计、改进编码风格,都可以有效的提升开发速度。好的设计和代码质量是成功的一半,更是成功的关键因素。在一个有缺陷的设计和混乱代码的基础上的开发,业务表面上进度较快,但是本质上是延后对设计缺陷的发现和错误的修改,也就是延后了开发风险,最终要在开发的后期付出更多的时间和代价。而研发进程中的重构,虽然在当前会减缓速度,但是带来的后发优势却是不可低估的。要知道,项目的维护成本是要远高于开发成本的。
二、什么时候重构?
前面说了那么多重构的好处以及意义,那什么时候适合重构呢,一下几种情况需要重构:
【增】增加新功能(增加新功能的时候,发现需要重构来便于新功能的添加)
【删】事不过三(消除重复)
【改】修复缺陷(修复Bug的时候)
【查】代码评审(大家通过交流提出了很多修改的主意)
具体说什么时候开始重构呢,这个没有标准的答案,应该根据各系统的具体情况来决定
比如,项目开始的时候,代码是空白的。工作的区域平坦干净,生活是美好的,这个世界是属于我的,我可以随心所欲。一切看起来都那么美好。
我们可以轻松顺利地建立起功能,哪怕我们似乎总会遇到一些波折。除了有点匆忙,一切看起来都是那么完美。我们不会注意到任何弊漏而且会迅速地让新功能上线。
然而,我们就让一些灌木丛生长在我们近乎完美的代码中。有时人们称之为 “ 技术债务 ”。但这些灌木丛只不过不是很好的代码,其实它们看起来也不是太糟糕。
为了到达目的地,不得不绕过这些灌木丛,或者推开它们。通常我们会绕道而行,不可避免的是,这会减慢我们的速度。
为了保持速度,我们甚至会比以前更粗心,灌木丛自然而然越冒越多了,新的灌木丛堆在旧的灌木丛上,严重放慢了我们的进程。我们意识到这个问题,但我们太急于抵达终点。我们迫切地想要保持我们早期的速度。不久以后,我们工作中有一半的代码背负着应付杂草、灌木丛、矮树丛和各类障碍。甚至还会掉到一些坑。
现在我们的问题非常明显,不能只是快速掠过,只做自己的事情,需要通过重构来一步步迭代剔除荆棘,来恢复我们的净土。
所以没有重构的标准开始时间,但重构不是随心所欲的,根据实际情况,假设开发道路上已经步履蹒跚了,何不回过头来重新审视一下,通过改造,然后面新的道路好走些呢
三、应该怎样进行重构?
1、重构前准备工作
1)、了解系统实际问题
“不能解决实际问题的重构就是耍流氓 ”,从实际问题出发,切勿为了重构而重构,看似简单的道理,但现实中确实存在为了重构而发起的重构,或许是想应用诱人的新技术,或许是为了跟上流行趋势,甚至 有自己主动YY需求而发起的重构。作为工程师我们需要谨记系统稳定高于一切,任何重构都存在风险,没有业务收益的重构相当于平白让业务承担非必要的风险,这是一种极不负责的表现。
所以,发起重构项目时,先想明白要解决什么实际问题,是为了提升性能?还是加强安全?或是为了快速的持续集成和发布?想明白再行动。
2)、设定明确目标
目标是否明确很大程度上决定了事情的最终效果,重构项目也要有具体的、可衡量、可执行、可实现、且有时间限制的可实现的目标,目标设定量化,而不是什么提高用户满意度等笼统的目标。
所以,发起重构项目时,先想明白要解决什么实际问题,是为了提升性能?还是加强安全?或是为了快速的持续集成和发布?想明白再行动。
3)、设计要有度
过度设计或者设计不足,都是设计失误,所以设计一定要切合实际,先设计再开工,避免返工。
4)、小步快跑**
提前做好迭代计划是在重构工作中容易被忽略的重要事项,重构方案设计之初就要考虑如何分阶段实施,甚至为了达到分阶段目的有时需要在设计方案上做一些妥协。如果把重构比作建筑施工,小步快走层层分离的策略就相当于搭建施工现场的脚手架,是一种把风险控制在可接受范围的有效手段。。
2、重构
不同人群对于如何重构,分工不同,理解不同,但重构过程中,一定要多沟通,确定目标一致性,减少重构过程中需求不停变换,否则我要一只虎,你给一只猫,进而又造就了产品与开发的爱恨情仇。
避免出现开发带着产品跑的误区(除非开发对产品设计理解很透彻),不能给产品一种假象,开发重构包治百病。
1)、产品设计
根据系统软件目前的现状,做好需要调研,提出明确的需求优化点,做好新旧系统业务数据兼容方案。
以下几个概念,可在重构需求设计过程中,减少系统冗余度,提高需求落地效率,可参考
1.1、业务耦合 -> 业务分治
随着新业务、模糊业务中的双方都越来越复杂之时,若没有及时解耦,耦合就会越来越紧,系统维护成本原来越大,最终影响到两方各自的发展。实现从业务的不健康耦合到健康耦合的转变,划清业务边界,同时推动耦合双方共同完成解耦。
1.2、多头管理 -> 归口管理
多头管理是一个下级同时接受多个上级领导的现象,在实际业务场景中,表现为一块业务,由多个团队进行维护的现象。这种情况导致的弊端主要有三个: 负责团队多,互相踢皮球; 不同团队之间团队墙导致的沟通成本过高; 业务难以标准化,业务方接入成本高。
而归口管理,则是按业务范畴进行分工管理,不同团队,不同系统,不同模块各司其职,业务边界分明。
1.3、刚性支撑 -> 柔性支撑
柔性支撑,是指外部的需求在需求小,多批次,时效要求高的情况下,以合理的成本水平迅速满足业务方需求的能力,需求完成的越迅速,付出的成本越低,其具有的支撑柔性越好。其对应的是刚性支撑,即没有考虑系统柔性的支撑。在业务初期,刚性支撑能快速满足业务方的需求,但长此以往系统整体效率下降,开发的边际成本越来越高,显然无法适应业务的快速发展。
2)、开发设计
对于开发者,侧重点更多在于代码的优化,这里没有一成不变的准则,所以不能与书本中匹对一样或类似的场景来进行改造,也不能死板硬套的套用一些模式。
这有本秘籍《重构,改善既有代码的设计》,虽然不能让你一招鲜吃遍天,但可以让你了解代码重构的真谛。
书里列举了20几种 “代码的坏味道”,但其中有几种经典的”坏味道“ ,也是平时我们经常犯的,需要重构的地方
2.1、重复代码
虽然我们不能消除重复代码,但我们可以避免重复代码
2.2、过长函数
长函数不仅难以理解、难以测试,而且容易造成重复代码,长函数里的代码块无法复用,当要使用长函数里的代码块,但是又不想去对其改动,可能最妥协的办法就是复制这一块代码。但复制一时爽,修改火葬场。如果修改这块代码,无法保证是否修改完毕,造成BUG重复出现。
2.3、过大的类
如果不对类加以整理重构,那么随着代码的增加,类变得巨大几乎是不可避免的事情。过大的类负责太多的事务,往往会导致大量的代码耦合
2.4、switch
switch往往是配合type一起使用,而type随着时间不可避免的会增多,导致类变得复杂和耦合,可以使用多态来进行解耦
2.5、散弹式修改
如果当你修改一个功能点,发现需要在各个地方都进行修改。而且你还不能保证所有的点都被修改了。这时候应该考虑把相同的代码整合到一块,并通过调用来引用功能。
其实相对于代码重构本身,我觉得养成一个好的编程习惯和一个重构意识更重要。
总结,作为一个程序开发工作者,在日常开发工作中,不可避免的会遇到重构,通过重新理解重构的概念,进而消除原心里的误区,重构并不是系统落地后才可以,而是存在于日常所撸的每行代码中。重构之道与术,可谓有道无术,术尚可求也;有术无道,止于术。