关于refactoring思考(六)

2008-04-09 04:08:17来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

Refactoring和软件设计
Refactoring是一种重要的设计辅助工具。特别地,他能够使得传统的up-front设计更简单,也可以改良现有软件的设计。本文阐述了在应用refactoring环境下设计应当具有的特点以及refactoring和OO社团最受人瞩目之一的设计模式之间的关系。

简化设计

为什么设计应当是简单的?
传统的软件方法偏向于进行一次性的Upfront设计,我们知道这很难。

软件方法学的设计者通常喜欢用建筑打比方。他们说,如果你要建造一座大厦,那么在你画完所有的施工图和建筑规范之前,你从来不应该开始真正施工。

我喜欢这样的比喻,因为建筑学的发展经历了几千年甚至可能是上万年,而软件行业,最多也只有5,60年的时间,我们需要从建筑中学习很多东西。就如模式语言的提出者,著名的建筑学家,Christopher Alexander ,给了设计模式很大的启发。

但是,如果我们只是从建筑学到东西,恐怕还是远远不够。我还不知道哪一栋大厦的外形能够发生变化,或者哪一个大商场在发现自动扶梯位置不太合适时,能够自由移动到一个更加合适的地方(我附近一家大超市的自动扶梯真的让我想到了这一点)。也许要部分达到这样的目的还是有可能的,但肯定要花费极高的代价。但是如果我们去构造一个企业的应用系统,那么我们必须准备好企业发展的同时需要对我们的软件做出改变。

软件的核心就是可变。这种变化不仅仅在于需求的变化,还在于人们理解的变化,而软件这种人活动的产物,必须要随之发生改变。

增量迭代的开发方法学是对软件变化的一种响应。但是正如我们前面所讲,增量迭代模型有可能对系统的设计提出比传统的Waterfall更复杂的要求。你需要预期所有可能的情形,然后才能保证你是可以增量的,也是能够迭代的。

这种两难的境地来源于一个基本的假设:
削减软件成本的最主要方法是减少返工的成本以及它的可能性。

传统的软件发法学认为,如果我能够尽量早地冻结需求,那么我修改设计的可能性和成本就越小,如果我的设计能够尽早地冻结,那么我修改实现的可能性和成本就越小。要想早早冻结设计,它必须是非常详尽而复杂的。

我把这种思路称为"改变恐惧症",不仅仅因为变化是最难研究的东西(制订出条条框框则简单的多),也因为我们没有足够的经验来适应变化,更重要的是,从来没有哪个时代比今天变化更快。

现在我们从另一个角度来想问题:
我们是否可能通过增加返工的方法来削减软件的成本?

这个方法初看起来自相矛盾。但是Kent Beck说:
The key is that risk is money just as much as time is money。

既然系统在变,那么你不能肯定今天设计的东西一定能够被日后用到。而你需要等待设计完成的成本会很高,因为在你的设计完成之前,没有人可以开始工作。

设计不是独立的。别人要使用你的设计,他必须能够理解你的设计。如果你今天的设计十分复杂,那么你就增加了从今天开始的所有开销,更多的东西需要检查和测试,更多的东西需要理解,更多的东西需要解释。更重要的是,你不能估算出明日的成本。你必须估计日后将要发生什么事情,通常你不可能准确无误地做到这一点。

所以,让设计简单,我们可以通过不断地增量修改设计使得系统更加接近需求,就象我们开车,每次都做一些小小的调整,最后达到目的地。

什么才是简单的设计?
要让设计简单,是否意味着我们可以随意fix and build,写一段,然后任意修改它。但是简单并不意味着随意,当然更不意味着愚蠢。

就象我在Duplicate Code里指出的一样,Once and Only Once通常是代码最简单的形式,因为它没有重复,每一个类都有自己简单的责任,每一个方法都有自己简单的意图。

简单的设计最重要的特性就是容易适应变化.为了达到这样的目的,简单设计应当:

  1. 能够简单地被理解.这依赖于你代码的可理解性.只有可理解,下面的简单性才能达到.
  2. 能够简单地被修改和扩展.OO系统往往通过增量的方法改变或增加系统行为,所以也就是要被简单地重用,这要求我们没有重复代码.
  3. 有最少数目的类.要易于理解系统,那么系统中每一个类应当和需要解决的问题的每一个重要概念相对应,如果人工加入许多毫无意义的类或者太多与问题概念无法对应的类,系统将无法理解
  4. 有最少数目的方法.每一个方法应该有他独立的意义, 如果没有可以言述意图的名字,那么系统将难以理解.

如果你看过Kent Beck和其他Agile联盟作者的文章,或者你看过我的Duplicate Code和Long Method,你可能会觉得2和3,4存在着矛盾.因为要达到2的目的,你必须有很多小类和精干的方法,而3,4则要求你具有最少的类和方法.要理解它们之间其实一致的关键在于,你如何决定需要一个新类或者需要一个新的方法.在进行Refactoring时,如果你觉得系统的某一部分具有独立的可对应问题领域的概念时,那么你应当毫不犹豫地使用Extract Class形成一个新类.反之,某一个类无法具有其独立的应用意义,或者该类数据太多而没有行为,这个时候你应当考虑这个类是不是应该被删除.当然,这里面有些例外(如method object)同样道理,如果一个代码片断能够有独立意图的行为,那么不管它的大小,可能是一个简单的表达式,都应该有独立的方法,但如果没有这样清晰的意图,再多的代码都可以在一个方法里面.

显然,简单的设计并非我们想象的那么简单.除了我们可以不太考虑以后解决的问题外,简单,作为一种重要审美标准.不是轻易能够达到的.我们一开始的设计往往不能够达到这样的要求,可能是类的划分不合理,可能存在着重复的代码,可能行为的分配需要调整,要达到简单,你就必须Refactoring你的代码,使设计更加合理.

Refactoring如何支持简单的upfront设计?
开始,你可以作简单的设计,也许是一个类,2、3个方法,你针对这个类写出test case,实现代码,让test case通过。在实现这个类的过程中,你发现2、3个方法太长,它们之间会有很多重复代码,于是你进行refactoring,你使用extract method让方法更能揭示意图。然后,你解决另外一个问题,你写出另外一个Test case,可能和上面一个类没有多大的关系,也可能你发现这个类在以前的类上附加了一些功能。这时,你会发现直接对前面一个类进行修改比较困难,你对前面一个类进行refactoring,让它更加容易加入。如果随着新功能的加入,发现前面一个类其实包含了两个应用概念,你可能需要对这个类进行重构,把它拆分为两个类。你进行refactoring,让所有的Test cast都能通过测试。你接着又选取一个需求,增加新的test case,然后实现它。这时候你发现这个类和原来的某个类具有很多类似的地方,你可能需要把这两个类进行进一步的抽象,通过refactoring形成一个新的超类。。。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:关于refactoring思考(四)

下一篇:个体软件过程(PersonalSoftwareProcess,PSP(续1)