我们应当改变我们的设计习惯
以往我们在设计一个系统时,总是喜欢大布局。全面地整理系统需求,全面地分析系统功能,再将系统整体地划分成数个模块,分别去设计、开发、测试。这样一个过程往往会持续数月,花费大量的工作量。但是,不到最后开发完成出来,谁都不知道会不会存在问题。最终的结局就成了一场赌博,不成功则失败,这就是“大布局”的弊病。
同时,任何人,即使是那些资深的高级工程师,也不可能一开始就保证自己的设计是正确的,总有或这或那考虑不周的地方。我们以往在开始设计时常常顾虑重重,总是担心会有什么考虑不周的地方,导致后来开发与维护十分困难。这种顾虑基于的一个前提就是,当最初的软件被设计好以后再进行调整,其成本与风险会很高。不错,这种状况确实存在,但问题是,当我们经过精心设计以后,我们的问题解决了吗?有时候是解决了,但更多的时候却是没有,因为我们不是先知,我们无法准确预知未来软件的变化。如果我们的预测发生了,我们当然可以得到一个不错的结果,但如果我们的预测失败了,之前的设计不仅不能给我们带来收益,甚至可能带来负担,因为它可能会导致我们的程序复杂了,甚至是运行效率的降低。因此,越来越多的人开始质疑这种方法。
随着敏捷的出现,人们开始改变了这种做法。如果我们有一种方法,使得日后对既有软件的修改,其成本与风险都没有那么高,那么我们为什么还要在设计之初搜索枯肠地去做那么多设计呢?问题的关键就是降低修改成本与风险的方法,而这个方法就是系统重构。
过去,我们软件修改其成本与风险高的根本原因在于,我们既在增加新的功能,又存在修改既有代码出错的风险。要增加新的功能,我们很难不修改既有的代码,这是在所难免的。实际上,要保持好的软件设计,修改既有代码的设计,使其适应新的需求,是保持软件质量非常重要的手段。但是,修改既有代码的设计风险却是巨大的,因为它可能造成既有功能出错,这是客户无论如何不能忍受的。在这样的情况下,正确验证对既有代码修改的正确性成为关键。系统重构与以往的软件修改不一样,它分为两个步骤:
- 先在不修改原有系统功能的基础上修改既有代码的设计,一方面使既有代码可以适应新的需求,另一方面因为既有功能没有变化,采用原有测试可以轻松验证这些修改的正确与否;
- 在已经经过重构的、可以适应新需求的基础上增加新功能,使得新功能与老功能合理解耦,从而达到OCP原则的要求。
这就是系统重构中“两顶帽子”的设计方式。别小看这一点儿变化,却使软件开发变得完全不一样了。首先,修改既有代码与添加新功能分离,使得修改既有代码的验证变得简单,就是使用以往的测试用例就可以了,因为这时软件功能还没有被修改(建立自动化测试,这是个问题,我们在后面讨论)。如果我们对既有功能建立了自动化测试则会使工作非常高效。当既有代码被有针对性地修改以后,又使得新功能的添加变得十分简便。
可以说“两顶帽子”的设计方式完全改变了我们开发软件的方式与思路。当你开始一个软件开发时,不用再战战兢兢、担心软件设计是否完备、是否可以适应日后软件的变化。因为有了系统重构,我们只需要活在当下,设计今天的程序,让明天的变化见鬼去吧。这里不是说我们完全不去预测未来,而是仅仅只对当下能够预测的未来做出设计。我们对于未来还不确信前就不要那么急急匆匆地做出决定,因为你不是先知。
同时,过去我们不敢过多地修改原有的代码,因为害怕原有功能出问题。正因为如此,即使原有设计已经不能适应新的需求了,我们依然不敢做出我们的调整。这样带来的恶果就是进入一种恶性循环:程序维护越来越困难,每增加一个功能,即使很简单的功能,都要付出巨大的成本。现在有了系统重构,修改既有代码的风险被降低了,我们应当有这样的意识,不断去优化我们的代码。软件复杂度不同,应当做出的设计也是不一样的。我们的软件系统总是一个从简单到复杂的发展过程,这是软件工业时代的特点,所以使得重构成为了一种必然。大胆地、适时地进行重构,才能真正使我们的软件系统始终保持一种高质量。
因此,重构应当成为一种习惯。它走下了神坛,不再是系统架构师或者软件大牛的专利,它应当是屌丝的最爱,成为普通程序员甚至初学者走向优秀程序员的捷径。当添加新功能而发现既有代码凌乱而难懂时,先重构改善既有代码;当开发新功能而发现既有代码可以复用时,先重构再复用;当新增新需求而发现既有代码不适应该需求时,先重构以适应新需求;当新功能需要既有功能能够扩展而既有功能不能够时,先重构把扩展点做出来再说。当你具备了所有这些意识,你就具备了一个优秀程序员的素质,可以编写出优美的软件代码。记住,大牛都是从屌丝开始。
同时,我们在完成设计后,在编码开发时应当采用“小步快跑”的开发模式。与全面布局的大设计不同,“小步快跑”采用的是一个一个连续不断的小设计。小步快跑体现出了敏捷软件开发的特点——简单与快速反馈。还是那句话,不要想得太多了,活在今天的格子里,因为你永远不可能预知今后会发生什么。所以,做今天的设计,解决今天的问题,完成今天的重构,让明天见鬼去吧。要知道,简单对于我们是多么的重要。当我们的大脑开始思考各种复杂的问题时,就开始充血,然后就是梦游,最后的结果就是顾此失彼。既然如此,我们为何不选择一种更加简单的生活方式呢?
现在我们开始开发了,不要想太多,也不要做所谓的大设计,你应当做小设计。起初不要考虑那么多,实现一个很小的功能,测试成功,跑起来,可以看到系统。然后重构,再往里面添加一点儿东西,测试,运行,看到系统。如此往复,你设计的系统就是小树苗一样一点儿一点儿地成长,最后长成参天大树。在成长过程中,每看到一些杂草,一堆乱枝,修剪一番,就像看到软件中不合理的设计一样。因为有了重构,我们可以大胆地对不合理的设计“零容忍”,及时进行优化。同时,当设计开发中需要复用既有代码,或者添加扩展点时,又是因为有了重构,大胆地调整既有代码以实现复用与扩展。慢慢地,它成为了我们设计的习惯,生命中的一个部分。
另外一个问题就是及时反馈,落实到此地就是及时测试。只有测试通过了,此次重构才算成功,我们才能继续往下重构,否则我们必须还原。从这里我们不难看出,重构的周期是多么的重要。在我以往的重构工作中,一次重构的周期也就在10分钟到1小时。重构的周期越长,说明你考虑的问题越复杂,最终出错的概率也就越大。所以,我们一定要习惯“小步快跑”的工作方式,让自己只活在当下。
话说到这里,你可能开始有些急了,别卖关子了,说点儿具体的好不好。设计应该怎么做,开发应该怎么做,测试又应该怎么做?也许还真得上示例才真正能够说清楚。好吧,让我们用一个示例来看看,到底我们应该怎样设计开发,又应该怎样做测试。(续)
相关文档:
遗留系统:IT攻城狮永远的痛
需求变更是罪恶之源吗?
系统重构是个什么玩意儿
我们应当改变我们的设计习惯
小步快跑是这样玩的(上)
小步快跑是这样玩的(下)
代码复用应该这样做(1)
代码复用应该这样做(2)
代码复用应该这样做(3)
做好代码复用不简单(1)
特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!