之前做过一个《重构和生活》的分享,昨天又参加了部门同学的《重构》的分享,总感觉有些意犹未尽,毕竟重构是一个可以不断咀嚼的问题。因此决定补发一篇,分享下个人对重构的一些之前没有分享的认识。


1 接触重构

我刚毕业就加入了一家外企研发中心,主要从事分布式数据采集软件开发。公司的规范让我在否定过去自己的同时也开始接触更多的东西。积极进行和参加Code Review的过程中我开始大量吸收代码工业中的一些好的实践,但沉淀下来的却比较零碎的。半年后我接触了《重构——改善现有代码的艺术》一书,当时真是一口气看完了,在书中我发现了很多现实中的对应,不过书中的描述会更系统、更精确,毕竟之前的仅仅是Mentor们的口口相传。看完后我就把坏味道和重构手段的列表打印出来贴在工位上,并开始对我的之前知识进行"重构",慢慢地我就开始在Code Review中更主动,逐步主导更多的Review,同时也传递更多的重构理念。团队开始用"重构"的语言开始交流,逐步形成了一堆行话。行话是每个行业都存在的,除了为了装逼、体现专业的同时更是为了加速交流。比如重构中“提取方法”“内联方法”等等行话,如果团队可以普遍掌握,后面代码交流中就省事不少,当然也也会给没有掌握的同学压力,加速团队学习成长。


2 重构的本质

重构本质就是"等价变换",和数学证明题一样,通过一系列变化证明ac等于b平方,最初复杂表达经过若干变换后得到简单的结果,这个过程很美妙。但如果其中一步不是等价变换,就是错误的推导。重构就是通过重构手段进行一系列的等价变化以实现"不改变外部功能的情况下改善内部结构"的效果。在项目实践中,就要时刻关注等价变化,如果推倒重写或者剧烈变化就不是重构。因此重构不仅仅是简单的Rename或者提取方法,仅仅了解和利用这些手段就进行的重构很可能是"伪重构"。很多重构无法安全进行的原因无非两个:重构手段掌握不够扎实导致经常使用不等价变化以及缺乏单元测试、集成测试等安全网。


3 重构中的"对子"

哲学中有很多"对子",其实是矛盾的两个方面,比如内因和外因,比如经济基础和上层建筑等等。同样重构中也存在很多"对子",比如"内联方法"和"用查询替代临时变量"、"改双向引用到单向引用"和"改单向引用到双向引用"、"子类方法提到基类"和"基类方法下放到子类"等等,这些对子的存在让重构也经常充满矛盾和纠结,这也是很多人接触重构的时候比较困惑的。软件设计和编程讲究的是平衡,因此在对子中实现某种平衡是一种需要不断思考和实践才能习得的能力,个体间平衡感会存在差异,但通常又会因为"英雄所见略同"而实现和而不同。平衡感的获得更重要的是可以保证设计和代码一致性,不会因为一时一事而发生改变,不会陷入今天要单向引用,明天感觉双向引用更好的反复中。


4 重构和预构

重构信奉"生产代码都是有价值的,不管有多烂",因为它们是实现了功能并经过测试的稳定代码,同时重构的人都自信他们有化腐朽为神奇的魔力。但这些不能成为制造烂代码的理由,不要想着反正后面可以重构的,因为重构需要更好的能力以及相应更高的成本。不断生成烂代码,然后请一帮高手进行重构是一件很悲催的事情,如果可以重来,真不如让高手一开始写漂亮代码,正因为如此面对恶心代码的时候很多人也都有重来的冲动,可能结果不一定都更好。因此在相对于大家熟知的重构运动同时,预构也悄然出现。预构不否定重构,不过更强调利用经验累积而得的洞察力开发新的解决方案,透过重构而获得的专业知识也属于这类经验。预构强调在开始编码前要想的事情,而重构是在编好代码后所进行的代码变换。经历更多重构后可能会意识到预构的重要性并且掌握好预构的技巧,毕竟第一次是将事情做好的最好时机,回炉是痛苦的也是需要很好能力保障的。


5 重构与性能

重构中可能大家比较纠结的一点就是性能,不断提取方法,提取变量、提取类的后果可能会照成更多的间接,从而从一定程度上影响代码的性能。重构很可能会让程序运行更慢,但同时也让可以更容易进行性能调优。快的程序的秘密就是可以先写出可调优的程序然后进行调优以达到足够的速度。重构好的代码可以从两个方面帮助代码优化。一是可以让你有时间进行性能调优,由于代码结构很好,因此可以更快增加功能,这样让你可以有更多时间关注性能。另一方面,结构良好的代码可以保证性能分析时有更好的粒度。分析器可以定位到更小的代码段,从而更容易调优,同时由于代码更清晰,你对可能的措施以及哪种调优有效果有更好的认识。可能重构期间会让程序变慢,但这使得优化阶段的调优更容易,最终可能得到更快的程序。


最后,分享一句之前看到的一句话:每天重构一小步,代码进步一大步!