开车的朋友一定深有体会,驾驶汽车其实就是在不断矫正汽车行驶方向的一个过程。在整个驾驶过程中,你必须全神贯注地紧盯前方,通过方向盘不断矫正方向,否则即使行驶在直线路段也可能偏离车道。那些疲劳驾驶的司机,因为进入睡眠状态,无法再矫正方向,车辆就会越来越偏离航向。这种情况下,即使数秒钟的小盹,也能造成车毁人亡的严重后果。
重构与驾车虽然属于完全不同的领域,但其道理是相同的。我们在运用重构方法修改代码的过程中也是经常会犯错的(是人就会犯错)。犯错就如同偏离了航向一样,因此我们需要不断去矫正。既然是矫正,就必须有一个正确的标准,以及判断是否正确的方法。驾车过程中正确的标准是车辆是否正确行驶在自己的车道上,判断是否正确的方法则是司机朋友的目测。同样地,重构过程中正确的标准是我们的软件是否保持重构前的外部行为,判断的方法则是测试,不论是手工测试还是自动化测试。
说起来这个道理很简单,但问题是你不可能随时都在测试,随时都在矫正,这是不可能的。因此,我们必须要有一个周期,即间隔多长时间测试并矫正一次。周期越长,出大问题的几率就越大,就如同那个睡着了的司机;周期越短,我们所需要付出的成本就越高,因为每进行一次测试都是需要我们付出成本的。周期的长短是需要我们去不断权衡的。
然而,与驾车不同,重构的测试周期还要取决于完成一次重构并提交代码的周期。因为软件重构总是这样一个过程,首先修改一部分代码,使程序处于一个错误而无法编译运行的状态,然后完全其它所有相关代码的修改,使程序恢复编译可运行的状态。在这个中间状态中,我们是无法测试的,或者说测试是无意义的。只有当相关代码都修改完成,程序恢复到可运行状态时,测试才变得有意义。每完成这样一个过程,我们称之为“完成一次重构”。而完成一次重构所花费的时间,是决定我们测试周期的关键因素。
既然如此,我们完成一次重构到底要花费多少时间呢?这听起来像是在进行数学推导,但其中的道理就是这样。决定一次重构花费的时间,是由我们的设计决定的。小设计,我们对代码的修改量少,我们完成重构的时间就短;大设计,我们对代码的修改量大,我们完成重构的时间必然长。完成重构时间长,测试的周期就长,我们发现错误的时间就晚,可能给我们造成的损失必然就大。
大布局为什么我们伤不起?漫长的业务整理,漫长的设计与开发,持续数月之久。在这数月中我们一直都无法评估自己是否正确。当我们辛苦数月之后才被告知我们犯错了,一切都为时已晚,项目不得不滑入了失败的深渊。这就是前面那个故事系统改造失败的根本原因。
我们说,大布局不可以,但大设计同样不可能。我开始要重构了,我们思考了很多问题,运用了很多重构手法,来完成一次重构。这样的重构,代码修改量必然多,重构周期必然长,出错的风险就大。因此,我们的重构应当是一个一个的小设计。运用一个重构手法,解决一个问题,完成一次重构,测试通过,再运用一个重构手法,解决另一个问题,完成另一次重构……如此往复,这就是我在前面所说的“小步快跑”的开发模式。
但是一个我必须要澄清的概念就是,判断是否是大设计的衡量标准并不是代码行数。举一个简单例子,我们将一段上千行的代码从一个函数,原封不动地挪到另一个函数中,而这段代码本身改动很小,这属不属于大设计呢?显然不属于,因为我们真正修改的代码量不多。后面我们将看到,重构过程会经常进行这种代码的搬移。
小步快跑让我们每次重构的时候只关注一个问题,运用一个重构手法去解决这一个问题。这样就使我们每次在修改问题时不会想得太多太远。但是,小步快跑并不是要我们完全没有远期规划,不是这样的。你可以有远期规划,但这种规划不要做得太早,过早做出这些规划往往容易顾此失彼。先工作一段时间,做一些基础的工作,让我们对系统的整体有了一个比较全面地认识,然后再规划,这样将得到更好的效果。
同时,它要求我们对远期的规划不要过细。越近期的计划越细,越远期的规划越粗。一些人之所以急急匆匆地去做细致的远期规划,是因为担心今天做出来的东西,到了今后发现不对,得改,所以今天多想想,多规划一些。但问题是,今天规划的就正确吗?如果是当然很好,而现实常常是否。不正确的规划却常常适得其反,让我们陷入一种困境,一种既不能解决当前问题,又不得不为错误设计埋单的困境之中。因此重构的思想却完全打破了这种思维习惯。重构不惧怕修改,因为它让明天的代码修改是安全的,而不再是走钢丝。它认为,今天的任务就是改今天的,即使到了明天会被认为是错误的,也是明天再改去。
大话重构连载首页:http://blog.csdn.net/mooodo/article/details/32083021
特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!
大话重构连载10:小设计而不是大布局