合格的重构
在谈手法和心法之前,可能很多人会有疑惑,觉得重构并不像你说的那么难啊,我们每天都在做,就是改改代码改改设计,哪有你说的那么邪乎?那我就先来讲讲我认为怎么样才算是一次合格的重构。
对于什么是重构,《重构》书中已经有明确的定义,分名词和动词两种形式。
重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
就像「看板」不是「我们看到的那个白板」一样,「重构」也不是「重新修改代码」那么简单。
我就看到过太多打着重构的幌子,把系统改的面目全非,最后出了问题直接甩锅到重构身上的场景了。
那怎样才算是一次合格的重构呢?我觉得至少需要做到以下几点:
消除味道:一个重构应该是从识别一个坏味道(Bad Smell)开始,以消除一个坏味道结束,任何不以消除坏味道为目标的重构都是耍流氓。
始终工作:即重构定义中的“在不改变软件可观察行为的前提下”,说白了就是重构过程不能破坏甚至改变软件外在功能。
持续集成:不需要为重构单建分支,重构过程可以做到 Feature 开发在同一分支上持续集成持续交付。
随时中止:例如一个方法重命名,需要修改 100 个调用点,当改到 50 个的时候有个紧急的 Feature,我可以随时暂停重构,立即切换到 Feature 开发上,且不需要回滚已做的重构。
断点续传:还是上边的例子,假如我已经完成了紧急 Feature 的开发,可以随时继续之前的重构,完成剩下 50 个调用点的重命名。
过程可逆:对于重构,经常有人会问:你怎么保证重构就会更好而不是更坏呢?重构的伟大就在于他跳出了对错之争,将关注点放到如何快速平滑安全的变化上,当然也包括反向重构。所以我的回答是:无法保证,但是我可以一分钟就重构回来。如果仔细看,《重构》书里的所有重构手法都是双向的,比如「Extract Method」和「Inline Method」。
可以反思一下,我们平时自认为的那些重构,是否都符合了以上的这些要求?
多少次我们打着重构的旗号,七零八碎,无法复原。
多少次我们打着重构的旗号,分支开发,集成困难。
多少次我们打着重构的旗号,半途而废,迷途难返。
多少次我们打着重构的旗号,孤注一掷,进退两难。
在我的眼里,这些都不是合格的重构,甚至都不能称之为重构,好的重构应该像一边开车一边换轮胎一样,保证系统随时可工作的前提下,还可以对其结构做出安全高效的调整。
可见重构并不简单,那要怎样才能达到上述的那些要求呢?
重构的心法
在过去的几年,我一直在学习和思考重构的各种手法。从刚开始的乱改一气,到学习基于 IDE 和插件的各种快捷键流的重构手法,以及研究如何通过组合各种基础重构手法形成“连招”,从而快速实现更复杂的重构过程。
随着对于基于 IDE 的快捷键重构手法越来越娴熟,在 IDE 和插件的帮助下,我的重构手法越来越华丽而迅捷,在沾沾自喜的同时心里也慢慢萌生了一些质疑:难道这就是重构么?如果没有 IDE 没有了插件,我还会做重构么?如何用编辑器(Vim,Emacs)做重构?重构只是代码级别的么?数据库如何重构呢?系统架构如何重构呢?工具框架如何重构呢?微服务架构下的服务重构呢?公司组织重构呢?
这种感觉就像是武侠小说中的某个柔弱书生,无意中掉到了一个悬崖下,找到了一本武林秘籍,照着上边的招式练了练就自以为已绝学在身,结果出去虽然能招架一时,但禁不住更大的挑战。
被打的体无完肤后,重新掏出那本秘籍,收起浮躁,怀着诚敬之心努力去参悟那些招式背后更深的哲理,也就是所谓的心法。
此时对于我来说,而那本武林秘籍就叫做《重构》。
在带着这些疑问重读《重构》的过程中,我欣喜地发现书中那些细致入微但看似笨拙拖沓的重构手法(例如 Rename,使用现代 IDE 一个快捷键就可以搞定,但是老马用了很多步骤才完成),其实都蕴含着重构最重要最基本的原则和思路,只要按着这些原则去做,无论什么层次的重构:代码重构、架构重构、服务重构甚至是组织重构,都可以做到上面提到的一个合格重构的基本要求,即平滑安全可停可续。
把其中的原则思路抽取出十六个字,即所谓的:重构十六字心法。
解释起来也很简单,往往我们做重构的时候就是在旧的结构(这里的结构可以是一个方法、一个对象、一个服务、一个数据库、一个服务甚至是一个组织结构)上直接修改,导致系统长时间处于一个中间不可用状态,这个状态持续的时间越长,重构失败的可能性和负面影响就会越大。
而《重构》告诉我们,做内部结构调整时,先不要直接修改旧的结构,保持旧的结构不变,先按照新的设计思路创建一个新的结构,因为这个过程中对于旧的内部结构没有任何影响,所以是安全的,可持续集成的。当新的结构构件完成时,我们再把对于旧结构的依赖一个个的切换到新的结构上,即所谓的「一步切换」。最后当确认所有对于旧的结构都切换到新的结构上,而且没有问题后,再将已经没有任何引用的旧结构删除掉,完成整个重构过程。
这里的「一步切换」并不是说整个重构的切换过程必须是一步完成的,例如前面重命名的例子,100 个调用点的切换可能是分多次完成的,在这个例子里一步切换指的是每一个调用点的切换过程。这个切换过程是最容易暴露出问题的,所以越简单越快速越好,一旦出现了问题,就快速的切换回旧的结构后再慢慢排查问题,从而实时保证系统的可用性。
大道至简,一旦领悟并掌握了这个心法,就发现自己一下从之前狭义的代码重构中跳脱出来,任何广义上的重构都立刻变得有章可循。
在架构重构中常用的抽象分支(BranchByAbstraction),以及在微服务架构下服务重构常用到的绞杀者模式,其实都是这种原则的一种体现。
总结
重构可以使软件更容易地被修改和被理解。
通过不断地改进软件设计以达到简单设计的目标,减少由于设计与业务的不匹配带来的架构与设计腐化。
掌握了重构的手法和心法,会让重构变得更加简单安全高效可控,从而真正的发挥出其巨大的威力,让我们的软件永葆青春。
本文转载自健荐公众号。
原文链接:https://mp.weixin.qq.com/s/SyKu1m7FVXzB5fInmj44wQ
评论