重构之如何让代码变得越来越好

353 阅读12分钟

自从完成翻译后,就再也没有读过它。不,不是因为我已经对它烂熟于心,而是因为重构已经变成了我的另一种生活方式,变成了我每天的“面包与黄油”,变成了我们整个团队的空气与水,以至于无需再到书中寻找任何“神谕“。 ---- 熊节

一、前言

Martin Fowler的《重构 - 改善既有代码的设计》读书心得分享分享。

二、重构原则

What - 什么是重构?

重构就是在不改变代码外在行为的前提下,对代码作出修改,改进程序的内部结构。简单说就是对用户来说没有什么变化,但代码实现可能已经完全不一样了,就好像人每七年全身的细胞就会替换一次,但你还是原来那个你。

在开发需求的过程中,一般会有两种过程:添加新功能以及重构。在开发新功能时,一般不会修改既有代码,重构时则不会添加新功能。这两种过程经常交替进行,在添加新功能时,可以先重构让代码更容易修改,重构后再继续添加新功能。

重构可以很大,对一个复杂的业务代码修改是重构。重构也可以很小,对一个函数内部实现的优化也是一个重构。它们的最终目的都是为了让代码变得更容易理解,更好维护。

Why - 为什么重构?

对抗代码熵增

事物总是自发、不可逆的朝着熵增方向进行,代码也是同理,在迭代过程中会逐渐变得混乱。一般原因可能是需求交付时间紧张、整体设计太复杂,在没有完全理解设计意图之前就贸然修改代码,在这个过程中,代码逐渐失去原有的设计结构。所以在需求迭代的过程中,经常性的重构可以对抗熵增。

更容易理解的代码

代码除了运行之外,能够容易理解也是非常重要的,铁打的代码流水的程序员,只要业务还在,就需要有程序员来维护代码。代码混乱也会造成难以理解,一个具有复杂业务的系统,若没有良好的设计和实现支撑,会非常难以理解以至于修改成本很高,重构也能够使软件更容易理解,更易理解的代码也有助于发现代码中的bug。

提高编程速度

这一点有可能是反直觉的,有人会说,我重构需要花费很多额外的开发、测试成本,也可能引入新的问题,效率怎么会增加呢?事实是良好的设计一定是快速开发的根本,没有良好的设计,你可能在短时间内效率变高了,但时间一长,你花费在调试、测试、理解上的时间成本会越来越高,系统会越来越容易引入问题,效率变得越来越低。

When - 什么时候重构?

前面有说到,重构可以是大范围的重构,也可以是很小的修改,所以重构可以随时随地的进行,也应该随时随地的进行,最常见的几个时机如下:

  • 添加新功能时:在开发时,若发现重构可以帮助当前代码更好维护、更容易理解,那么就可以在这个时机进行重构
  • 修复bug时:在修改错误时,可以思考下当前代码设计是否不够好?以致于更容易产生bug
  • 代码审核(CR)时:借助团队的力量,在CR时别人可能提出很好的建议,帮助我们提升代码设计

专门时间的重构

作者提到不应该专门拨出时间进行重构,我的理解是这样,在实际情况中,可能确实需要单独的时间来做一些重构工作,让已经混乱的代码回到正确的轨道上,但这里重构是手段而不是目的,不能为了重构而重构,通过重构解决我们实际的问题,如非常容易出现问题的代码、混乱到严重影响效率的代码、已经无法良好的承载未来业务需求扩展的代码。

重构与设计

重构与设计是互补的关系,在开始开发之前,我们应做好提前设计,可以极大减少返工成本,在持续迭代过程中,通过重构可以随时随地的应对需求变化。

有了重构这把武器,在设计阶段也无需寻找最完美、最灵活的方案,这个成本可能是非常高的,只要寻找一个足够合理的设计即可。

简单来说,设计够用即可,能够满足当下以及很明确未来的需求,就可以了。没有完美的设计,只有持续的重构。

三、代码的坏味道

如何形容什么样的代码需要重构?作者起了一个很有趣的名字,代码的坏味道,当你发现一段代码不容易理解需要重构时,就是有代码的坏味道。这里要理解,没有精确的标准能够形容何时应该重构,代码的坏味道主要帮助我们指引正确的方向。书中列举了很多,这里就不一一列举,仅说明一些在工作中比较常见的。

代码坏味道坏在哪里?常见手法
重复代码当你因为某个需求而修改代码时,发现同样的逻辑你需要修改4、5处,是否会发疯?1. 将类似的代码提炼到同一个函数中
2. 将子类中相同的代码提炼到父类中
过长的函数越短的函数能活的越久,当看到一个有300行的函数时,是否会恐惧?将内部的逻辑提炼到新的函数中
过大的类超级类,想必都见过吧?一个类承载了所有业务的功能,协议请求、业务逻辑、数据处理、工具类等等等等,超过2000多行,内部的实例变量也会很多拆分不同的功能到不同的类中
过长的参数列参数太多会导致难以理解,不易使用考虑用对象代替,或让参数在函数内部获取
发散式变化一个类可能因为不同的原因修改,这个就是过大的类的前兆拆分成不同的类
霰弹式修改和“发散式变化”刚好相反,每当有一个需求变化,要在许多不同的类中修改,不仅很难找到他们,也容易漏掉将这些需要修改的地方放到同一个类中
依恋情结一个类中调用了另一个类太多的函数,比如A类中为了计算某个值从B类中获取了很多变量来计算显而易见可以让B来计算这个值即可
数据泥团这个可能会导致过长的参数列,比如说有一类数据x、y、z,被作为好几个函数的参数进行传递将x、y、z放入一个新对象传递
冗赘类没有用的类,或者子类和父类内容相同的果断删除,或者合并父子类
夸夸其谈未来性比较常见的,为了不存在场景进行设计,比如设计了协议,并且在想“总有一天会需要的“够用即可
狎(xia)昵(ni)关系过于亲密的两个类,对理解、扩展都不利;继承也可能会导致过度紧密的关系1. 提炼新的类;
2. 双向关系变单向关系;
异曲同工的类两个类做了类似的事情合并+删除
纯稚的数据类数据类被外部过多的操作内部的数据,会导致这些字段难以维护应封装数据字段,对外暴露接口
被拒绝的遗赠不合适的继承关系,子类is not a父类采用组合的关系吧
过多的注释合理的注释是必要的,但是如果需要非常多的注释才能解释清楚,可能根本原因是代码设计很糟糕通过过多的注释来发现坏味道

上面的列表有很多,而书中对于每种坏味道,提供了不同场景下不同的方式来解决,对于如何识别坏味道和如何重构,大家应该持续的思考和学习,最好手边可以放一本《重构》可以随时查阅。

四、测试体系

书中主要讲的就是单元测试,通过单元测试保证重构后的代码的质量,自动化的单元测试也能减少调试的时间,这对于编程速度的提升是有很大的帮助的。但在实际执行过程中,有人可能会觉得需要写额外的代码,并不是很愿意这么去做。作者认为的一些好处如下:

  • 当你确实体验到这种方法对编程速度的提升,才会更愿意去做
  • 在开始编程之前编写测试代码是最有用的,让自己的注意力集中在接口而非实现

当然,测试也存在边际收益递减的情况,合理的测试能够发现很多bug,但增加测试量所带来的收益会逐渐降低。对于重构中的测试,我们应该聚焦于最容易出错的地方,不要因为无法捕捉所有bug就不写测试,因为测试的确可以捕捉到大多数bug。

五、谈谈个人想法

破窗理论

破窗理论是指,当一个窗户上有一个破洞一直没有人修好,时间一长,上面可能就会有越来越多的破洞。软件项目也是如此,如果代码中有低劣的设计、糟糕的代码,之后接手的人也是会倾向于让它变得越来越糟糕。

结合熵增的理论,重构可以让代码持续变得更好,避免代码中的”破洞”越来越多。

如何运用本书?

在看本书时,个人已有多年开发经验,所以会发现书中的很多重构手段,和自己的经验认知是类似的,那对于自己来说,更多的是通过这本书全面的了解作者对于重构的想法,以及补全一些没有遇到或考虑到的内容。

在日常开发中,这本书也适合在重构时没有好的想法时,拿出来看一看有没有类似的场景,或许会找到想要的那个答案。

另外也是建议多读、多总结、多交流:

  • 多读:建议多读一读其他的技术经典书籍,比如代码大全、设计模式等,从不同角度了解如何让代码变得更优秀。
  • 多总结:在工作中遇到了一个难题、或者解决了一个难题,可以思考下为什么会被难住,或者总结这个经验分享给其他人,多总结可以让你进步的更快。
  • 多交流:和其他人思维的碰撞非常重要,一个人的想法总是有限的,多个人交流时可能会产生更好的想法,比较好的时机就是技术方案评审、Code Review等。

时间紧张怎么办?

有人说,开发周期真的很短,为了保证质量,不得不用不太好的实现来完成需求。这个在实际工作中确实是比较常见的,毕竟写代码最终也是为了公司业绩,而不是为了代码更漂亮。

在这种不得不用更差的方法实现的情况下,就可以算是引入了技术债务,短期内我们可以接受技术债务,但是一定要记录下来,在合适的时机去优化掉这段技术债务,比如下一次有需求改动到类似的地方同时也有比价充裕的时间时,甚至是有单独的技术需求。

体会重构的好处

刚成为程序员的同学,可能不太理解重构到底有什么好处,这个不是他们的错,毕竟重构这个事情也是很需要经验的,只有亲自经历后,才能体会到重构带来的好处。

比如说当你因为修改很复杂很难理解的代码,甚至因为修改它而引发了比较严重的线上问题时,可能就会觉得,这个代码怎么这么难以维护?比较爱思考的同学在这个时候可能就会去想如何优化这段代码了,虽然新人不一定会有太多的经验知道如何该做,但对于怎么做来说,知道应该去做重构这个事情更重要。

对于新人来说,重构的能力是需要持续提升的,看完一本书可能没有多少提升,当经历过足够多的项目后,能力才可能会突飞猛进。

六、重构和身体健康状况

最后,我想谈一谈身体健康状况,作者在书中提到,代码开发和身体健康状况的情况很类似,这一点我非常赞同,尤其是当年龄过了30后,体会更深刻。

年轻的时候,虽然知道要健康生活,但从来没有贯彻过,经常性的熬夜、不规律饮食,在20多岁的时候因为身体可以,看起来好像也没什么问题。越接近30岁,就会发现身体的毛病越来越多,身体越来越差,这个时候想要重新恢复健康的身体,花费的代价会很大。

而长期坚持规律运动和健康饮食的人就可以拥有旺盛的精力和超高的工作效率。这就好比持续的重构,可以让代码变得更好。

最后,希望大家不管是身体健康,还是正在维护的代码,都可以持续的变得越来越好。