重构的原则

什么是重构?

  • 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
  • 重构(动词):使用一系列的重构手法,在不改变软件可观察行为的前提下,调整其结构。
  • 重构的关键:在于运用大量微小且保持软件行为的步骤,一步步达成大规模的修改,每个单独的重构要么很小,要么由若干小步骤组合而成。因此,在重构的过程中,我们的代码很少进入不可工作状态,即使重构没有完成,我也可以在任何时刻立刻停下来。

为什么要重构?

重构改进软件的设计

  • 如果没有重构,程序的内部设计(或者架构)会逐渐腐败变质。当人们只为短期目的而修改代码时,他们经常没有完全理解架构的整体设计,于是代码逐渐失去自己的结构。
  • 程序员越来越难以通过阅读源码来理解设计。代码结构的流失有积累效应。越难看出代码所代表的设计意图,就越难保护其设计,于是设计就腐败的越快。经常性的重构有助于代码维持自己该有的形态。
  • 消除重复的代码是优秀设计的根本。

    重构使软件更容易理解

  • 程序设计的根本是准确的告诉计算机我要做什么,但是程序的另外一个潜在消费者是几个月之后的另一个程序员,如果代码不易理解,那么那个“另一个程序员”原本可能一小时的代码修改,可能需要一周,即使这个“另外的程序员”就是原来的代码开发者,也有可能会“不认识”自己曾经写过的代码,甚至抱怨以前自己写的这么烂。。

    重构帮助找到bug

  • 如果对代码进行重构,我就可以深入理解代码的所作所为,并立即把新的理解反映到代码当中。搞清楚程序结构的同事,也验证了自己所做的一些假设,于是想不把bug揪出来都很难。

    重构提高编程的速度

  • 当需要添加新功能时,内部质量好的软件,让我可以很容易找到在哪里修改,如何修改。良好的模块划分,使我只需要理解代码库的一小部分,就可以做出修改,如果代码很清晰,我引入bug的可能性就会很小,及时引入bug,调试也会容易很多。通过投入精力改善内部设计,可以增加软件的“耐久性”,从而可以更长时间的保持开发的速度。

    何时重构

    在我编程的每个小时里,我都会做重构。

    预备性重构:让添加新功能更容易

  • 重构的最佳时机,就是在添加新功能之前。书中举的一个通俗的例子是需求要你往东100公里,我们不应该直接就往东100公里,而是先看看地图,发现往被20公里上高速,然后往东开100公里速度会快上三倍,这里也讲了需要消除重复的代码,避免一个问题修改好几个地方。

    帮助理解的重构:是代码更易懂

  • 我需要先理解代码子在做什么,然后才能着手去修改。这个书中举例是通过修改一些函数或者变量的命名来增加理解,这个与上面的消除重复代码都是《clean code》里面的要求。

    捡垃圾式重构

  • 在时间和精力允许的情况下,见到一个改一个,像“捡垃圾”一样,积少成多。

    有计划的重构和见机行事的重构

  • 前三种都是见机行事的重构。马丁跟建议尽量不要有计划的重构,除非真的很不容易添加新功能,而几个星期的重构在未来几个月能发挥出很大的价值,才做计划重构。否则日常见机行事的重构是一个好的选择。

    长期重构

  • 不建议让一支团队专门做重构,建议还是每次小改动让整个系统仍然正常工作。如果替换组件的这种,建议引入一层新的抽象,使其兼容两个库,一旦调用方完全改为这层抽象,替换会很容易,这个策略叫做(Branch By Abstraction【mf-bba】)

    Code-Review时重构

  • code review 有助于在开发团队中传播知识也有助于让有经验的开发者把知识传递给比较欠缺经验的人,并帮助更多人理解大型软件系统中的更多部分。我的代码对我来说可能是清晰的,当时对于他人则不然,这是无法避免的,开发者很难为那些不熟悉自己所作所为的人着想。code review可以让更多人提出有用的建议和点子,也更有助于重构。code review的形式有可能是群体review也有可能就是pr的形式,笔者建议与原作者坐在一起看,有助于了解上线文和原作者的意图。

    何时不该重构

  • 如果重写比重构还容易的话。。思考下新核心重写比重构更合适么?个人觉得要从投入产出价值入手去思考。

重构的挑战

重构的唯一目的:就是让我们开发更快,用更少的工作量创造更大的价值。
重构应该总是由经济利益驱动。

代码的所有权

  • 这个讲的是对于接口的重构应当谨慎,外部的调用者可能还在用,更多的建议是接口版本的控制,通过增加新的接口,并将老接口标为(deprecated)逐步淘汰。

    分支管理

  • 马丁建议大家使用主干分支持续集成CI,这个有助于重构,减少了feature分支的合并难度,尤其是较长时间的feature分支的合并,劣势也很明显,对于一些功能的回滚非常不利,个人感觉这个要看实际开发的情况吧,是否适合于拆分更小的功能,来配合CI快速集成。

    测试

  • 不会改变程序可观察行为,这个是重构的一个重要特征。
    需要有自测试代码,这样能够降低重构带来的风险,快速的发现问题,自测试代码也是极限编程的另一个重要组成部分,也是持续交付的关键环节。

    遗留的代码

  • 遗产对于大多数人来说是好事,不过对于程序员,那就呵呵🙄了,甚至是瑟瑟发抖(别人的代码)。
    笔者的建议还是一点点修改,不要急于求成。

    数据库

  • 数据库的重构也建议循序渐进,小修改,或者渐进式修改,比如修改某个字段名,先增加一个字段,然后同时写入新旧两个字段,然后数据迁移,之后改为读新字段,不在写入旧字段,笔者建议读者两本书《渐进式数据库设计【mf-evodb】,和 数据库重构【Ambler&Sadalage】
分享到