💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 章节十五 集成 by Kent Beck 现在,你已经拥有了七巧板的每一块:你已经了解了重构的基础,知道了重构的分类,还实践了所有这些重构。同时,你已经很擅长测试了,所以你不再畏首畏尾。 于是你可能想:「我已经知道如何重构了。」不,还没有。 前面列出的技术仅仅是一个起点,是你登堂入室之前的大门。如果没有这些技术,你根本无法对运行中的程序进行任何设计上的改动。有了这些技术,你仍然做不到,但起码可以开始尝试了。 这些技术如此精彩,可它们却仅仅是个开始,这是为什么?答案很简单:因为你还 不知道何时应该使用它们、何时不应该使用、何时开始、何时停止、何时前进、何 时等待。使重构能够成功的,不是前面各自独立的技术,而是这种节奏。 当你真正懂得这一切时,你又是怎么知道的呢?当你开始冷静下来,你就会知道,自己已经真正「得道」了。那时候你将有绝对的自信:不论别人留下的代码多么杂 乱无章,你都可以将它变好,好到足以进行后续的开发。 不过,大多数时候,「得道」的标志是:你可以自信地停止重构。在重构者的整场表演中,「停止」正是压轴大戏。一开始你为自己选择一个大目标,例如「去掉一 堆不必要的subclass」。然后你开始向着这个目标前进,每一步都走得小而坚定,每一步都有备份,保证能够回头。好的,你离目标愈来愈近,愈来愈近,现在只剩两个函数需要合并,然后就将大功告成。 就在此时,意想不到的事情发生了:你再也无法前进一步。也许是因为时间太晚, 你太疲倦;也许是因为一开始你的判断就出错,实际上不可能去掉所有subclass; 也许是因为没有足够的测试来支持你。总而言之,你的自信灰飞烟灭了,你无法再自信满满地跨出下一步。你认为自己应该没把任何东西搞乱,但也无法确定。这是该停下来的时候了。如果代码已经比重构之前好,那么就把它集成到系统中, 发布你的成果。如果代码并没有变好,就果断放弃这些无用的工作,回到起始点。 然后,为自己学到一课而高兴,为这次重构没能成功而抱憾。那么,明天怎么办? 明天,或者后天,或者下个月,甚至可能明年,灵感总会来的。为了等待进行一项重构的后一半所需的灵感,我最多曾经等过九个月。你可能会明白自己错在哪里, 也可能明白自己对在哪里,总之都能使你想清楚下一个步骤如何进行。然后,你就可以像最初一样自信地跨出这一步。也许你羞愧地想:『我太笨了,竟然这么久都没想到这一步。』大可不必,每个人都是这样的。 这有点像在悬崖峭壁上的小径行走:只要有光,你就可以前进,虽然谨慎却仍然自信;但是一旦太阳下山,你就应该停止前进;夜晚你应该睡觉,并且相信明天早晨太阳仍旧升起。 这听起来似乎有点神秘而模糊,近乎清谈玄想。从感觉上来说,的确如此,因为这 是一种全新的编程方式。当你真正理解重构之后,系统的整个设计对你来说,就像源码文件中的字符那样可以随心所欲地操控。你可以直接感受到整个设计,可以清楚看到如何将设计变得更灵活,也可以看到如何修改它:这里修改一点,于是这样表现;那里修改一点,于是那样表现。 但是,从另一个角度来说,这也并非那么地祌秘而模糊。重构是一种可以学习的技术,你可以从本书读得并学习它的各个组成。然后,只要把这些技术集成在一起并使之完善,就可以从一个全新角度看待软件开发。 正如我所说,这是一种可以学习的技术。那么,应该如何学习呢? - 随时挑一个目标。某个地方的代码开始发臭了,你就应该将问题解决掉。你 应该朝目标前进,达成目标后就停止。你之所以重构,不是为了探索真善美(至少不全是〕,而是为了让你的系统更容易被人理解,为了防止程序变得散乱。 - 没把握就停下来。朝目标前进的过程中,可能会有这样的时候:你无法证明自己所做的一切能够保持程序原本的语义。此时你就应该停下来。如果代码已经改善了一些,就发布你的成果;如果没有,就撤销所有修改。 - 学习原路返回。重构的原则不好学,而且很容易遗失准头。就连我自己,也 经常忘记这些原则。我有时会连续做两、三项甚至四项重构,而没有每次执行测试用例(test cases)。当然那是因为我完全相信,即使没有测试的帮助,我也不会出 错。于是我就放手干了。然后,「砰」的一声,某个测试失败,我却无法找到究竟哪一次修改造成了这个问题。 这时候你一定很愿意就地调试,试图从麻烦中脱身。毕竟,不管怎么说,一开始所有测试都能够正常运行,现在要让它们再次正常运行,会困难到哪里去?停!你的重构己经失控了,如果继续向前走,你根本不可能知道如何夺回控制权。你应该回到最近一个没有出错的状态,然后逐一重复刚才做过的重构项,每次重构之后一定要运行所有测试。 站着说话不腰疼,以上一切听起来似乎显而易见。当你出错的时候,使系统极大简化的一个方案也许已经近在咫尺,这时候要你停下来回到起点,不啻是最痛苦的事情。但是,现在,趁你头脑还清楚的时候,请想一想:如果你第一次重构用 了一小时,重复它只需十分钟就够了,所以如果你退回原点,十分钟之内一定能够再次达到现在的进度。但如果你继续前进,调试所需时间也许是五秒种,也许是两小时。 当然,我现在说这些,也是看人挑担不吃力,实际做起来困难得多。我个人曾经因为没有遵循这条建议,花了四个小时进行三次尝试。我失控、放弃、慢慢前进、再次失控、再重复……真是痛苦的四个小时。这不是有趣的事,所以你需要帮助。 - 二重奏。和别人一起重构,可以收到更好的效果。两人结伴,对于任何一种软件开发都有很多好处,对于重构也不例外。重构时,小心谨慎按部就班的态度是有好处的。如果两人结伴,你的搭档能够帮助你一步一步前进,你也能够帮助他 (她)。重构时,时刻留意远景目标是有好处的。如果两人结伴,你的搭档可能看到你没看到的东西,能想到你没想到的事情。重构时,明智结束是有好处的。如果你的搭档不知道你在干什么,那就意味你肯定也不知道自己在干什么,此时你就应该结束重构。最重要的是,重构时,拥有绝对自信是绝对有好处的。如果两人结伴,你的搭档能够给你温柔的鼓励,让你不致于灰心丧气。 与搭档协同工作的另一方面就是交谈。你必须讲出你所想做的事,这样你们两个才能朝着同一个方向努力。你得把你正在做的事情讲出来,这样你的搭档才有可能指出你的错误。你得把刚才做过的事情讲出来,这样下次遇到同样情况时你才能做得更好。所有这些交谈都有助于你更清楚了解如何让个别的重构项适应整个重构节 奏。 即使你已经在你的重构目标(代码〕中工作了好几年,一丝一缕了然于胸,但只要发现其中臭味,以及消除臭味的重构手法,你就有可能看到程序的另一种可能。你也许会想立刻挽起袖子,把你看到的所有问题都解决掉。不,不要这么莽撞。没有 一位经理愿意听到他的开发成员说「我们要停工三个月来清理以前的代码」。而且开发人员本来也就不应该这样做。大规模的重构只会带来灾难。 你面前的代码也许看起来混乱极了,不要着急,一点一点慢慢地解决这些问题。当你想要添加新功能时,用上几分钟时间把代码整理一下。如果首先添加一些测试能使你对整理工作更有信心,那就那么做,它们会回报你的努力。如果在添加新代码之前进行重构,那么添加新代码的风险将大大降低。重构可以使你更好理解代码的作用和工作方式,这使得新功能的添加更容易。而且重构之后代码的质量也会大大提高,下次你再有机会处理它们的时候,肯定会对目前所做的重构感到非常满意。 永远不要忘记「两顶帽子」。重构时你总会发现某些代码并不正确。你绝对相信自己的判断,因此想马上把它们改正过来。啊,顶住诱惑,别那么做。重构时你的目标之一就是保持代码的功能完全不变,既不多也不少。对于那些需要修改的东西,列个清单把它们记录下来(通常我在计算器旁边放一张索引卡),需要添加或修改 的测试用例(test cases)、需要进行的其他重构、需要撰写的文档、需要画的图…… 都暂时记在卡上。这样就不会忘掉这些需要完成的工作。千万别让这些工作打乱你 手上的工作。重构完成之后,再去做这些事情不迟。