最近又温习了一遍Martin Flower的经典名著《重构:改善既有代码的设计》,感触颇多,应该总结一下:
1. 重构是减缓代码腐化的有效武器
现在很少有软件完全是从0开始开发,也很少有软件有很短的生命周期(2年)以下。那么,换句话说,很多时候你拿到的都是既有代码,它能工作,也许它的设计很好,也许很乱,但它肯定能工作。即使是优质的项目,当初的设计,架构非常合理,代码很优质,但随着时间的推移,人员的变动,不断的修复Bug,不断的添加新功能,代码总是会腐化的。长期维护的项目,最大的腐化点就是在没有完全理解代码的情况下做修改,但这却是经常发生的事情,想想新进项目的人,以及管理层的压力,导致Bug或者新功能很快上去,但修改者还没有完全理解代码,所做的修改肯定会有问题。这个时候就需要不断的重构,当你对代码有了新的认识的时候,你就会明白为什么作者那样写,知道如何修改才是最佳方案。通过重构可以把腐化的代码改成优质的代码。
2. 关键是要用心
重构是编程的一种技巧,而编程又是一种社会活动(《编程心理学习》),所以人的态度及所花的心思就起到决定性作用。如果不用心去做,那即使再好的技巧也是空谈。要肯花时间和心思在代码上面,思考作者为何那样写,思考可改进的空间,然后测试,再做出修改尝试,直到最终改成优质代码。这是一个很费心思的过程,甚至比重新写还困难:重新写你只需要理解需求即可;但改重构需要理解现有的代码,这通常都是一个很痛苦的过程,因为代码通常都很难读懂,充满各种坏味道(当然,如果代码读起来很好,写的很优美,没有坏味道,也就用不着重构了),这也是很多人不愿意重构的原因:即然代码能工作,何必理那堆滥摊子!
所以,关键还是态度和用心
3. 不要为了重构而重构
这种情况通常是刚读了书之后或者看了某篇博客之后,热血沸腾:我们的代码坏味道太多了,很我必要重构。这时就要止住,重构需要冷静的思考,认真的对待,并且在理解代码的情况下进行,并严格的进行测试,否则就是在搞破坏。头脑发热式的大刀阔斧的重构会带来更多的问题。
4. 注意重构的前提
这个Martin已经讲了,这里做些补充:首先就是政策允许,也即管理层允许随时修改代码,这听起来可笑,但有些公司确实不允许随便改代码(特别是对于成熟项目),还有时间允许,如果你24小时都在改Bug,也真的很难抽时间去重构(这样的公司确实存在);其次,就是对代码要熟悉:明白其目的,作者为啥那样写;最后就是做足测试,包括重构前和重构后,Martin讲要用单元测试,但很不幸现实中的项目很少有单元测试,现补也不太现实,其实手动测试也是一样的。
5. 小步前进,时刻保持代码可工作
这非常重要,虽然代码很乱,但不能大刀阔斧的大范围的搬移方法,拆分类等,这样做的结果只有一个:代码不会正常工作,导致混乱漫长的调试。要小步前进,虽然有很多方法不应该在现有的类中,但也要一次一个的移动,以确保我们没在搞破坏。同时要不断的测试,以保证代码时刻都能正常工作(因为要如此频繁的进行测试,所以Martin才会把单元测试当作前提,但手动测试也一样)。这一点从Martin的书中也能体现到,很多方法都会拆分成很多小步骤,每次一个步骤,然后测试,直到最后完成。
6. 重构是一个长期计划,每天都要进行
写出代码后要重构,维护代码时更要重构。这是一个长期的计划,不能像改Bug一样,改完了就不管了。也就是说每当看到代码有坏味道时就进行重构,而不是等到什么时候才进行。
7. 重构是工作的一部分但不是全部
不要把一整天的时间,或者一整星期的时间都花在重构上面,这样做很容易过度重构。重构应该穿插在日常的工作当中,比如写代码时进行或者改Bug时进行或者添加新功能时进行。但重构不应该是主要或全部工作内容。重构是改善现有代码设计,前提是代码能正常工作,所以首要的任务是让代码能正常工作。
8. 没有最好,只有更好
好代码与坏代码没有明显的界线区别,不像考试:60分及格,59就挂。好与坏都是抽象概念,相对主观,可能你认为好的东西在别人眼中很滥,反之亦然。所以就要不断的思考,当觉得有改进的地方时就去改进,而不是停步不前。
9. 时代变迁
Martin的书著于1999年,现已是十多年光景,软件界也发生了不少变化,编程方法也有了不少改进,所以某些方法可能现在不太适用。这就需要在用的时候要思考,不要死读书,更不要读死书。像8.13 Replace Type Code with Class也可以用enum来进行,Java是强类型语言,enum也会进行类型检查。再如10.13 Encapsulate Downcast就要用Generics。
10. 这本书要读二遍,并放在触手可及的地方
技术书籍至少要读二遍:第一遍快速浏览书中有哪些内容,以决定是否值得继续读;第二遍找出感兴趣的章节,逐字逐句的仔细咀嚼。
本书由于重构手法太多,每种手法都有其动机和详细方法步骤,这个不可能全记得住,至少我记不住。所以就需要放在触手可及的地方,每当觉得有类似手法可以用时,就把书找出来再分析一番,以确保没有乱用。
11. 掌握好准则
书中很多手法都是相反的,比如Extract Method和Inline Method,它们的作用恰恰相反。这就需要在用的时候思考,找到足够的理由才进行,因为没有明显的线索告诉你到底该进行Extract Method还是Inline Method。这就需要去尝试,去思考,哪种效果更好用哪种,如果实在不知道就随便用一个,随着时间的推移会发现它的利与弊,然后再进行调整。
12. 重构的目的
重构的目的是:改善现有代码的设计,也就是说把代码变得更好。如果你不希望代码变得更好,或者代码变好了对你也没啥好处(你是临时工或外包),或者你觉得现有代码就挺好的,那其实也没有必要花时间去重构,还不如去上上网,扯扯蛋!
但,如果你是一个优秀的猿嫒,或者想成为一个优秀的猿嫒,最好还是重构一下吧!闲着也是闲着,何不锻炼一下自己呢?虽然改善的是代码,但更多的还是自己的技能啊!
13. 去哪里重构
也许你的项目的环境不允许你随意改代码,或者你对项目没兴趣,或者感觉项目已经无药可救了,没关系可以到开源世界来!现在开源世界有一种叫做Refactotum的活动,它是一群开发者约好一个时间,然后把一个开源项目(或其部分源码)下载下来,运行其单元测试,然后重构,重构提交代码。