敏捷设计
如果敏捷性(Agility)是指以微小增量的方式构建软件,那么究竟如何去设计软件呢?又如何去确保软件具有灵活性、可维护性以及可重用性的良好结构呢?
在敏捷团队中,全局视图和软件一起演化。在每次迭代中,团队改进系统设计,使设计尽可能的适合当前系统。团队不会花费许多时间去预测未来的需求和需要,也不会试图在今天就构建一些基础结构去支撑那些他们认为明天才会需要的特性。他们更愿意关注当前的系统结构,并使它尽可能的好。
那么怎么才能保证全局视图和软件一起演化呢?在软件出现下面任何一种气味时,就表明软件正在腐化。敏捷团队的成员就会采取一些动作来阻止软件的腐化。下面列举的就是常见的设计的臭味——腐化软件的气味[1]。
1. 僵化性(Rigidity):很难对系统进行改动,因为每个改动都会迫使许多对系统其他部分的其他改动。
2.脆弱性(Fragility):对系统的改动会导致系统中和改动的地方在概念上无关的许多地方出现问题。
3.牢固性(Immobility):很难解开系统的纠结,使之成为一些可在其他系统中重用的组件。
4.粘滞性(Viscosity):做正确的事情比做错误的事情要困难。
5.不必要的复杂性(Needless Complexity):设计中包含有不具任何直接好处的基础结构。
6.不必要的重复(Needless Repetition):设计中包含有重复的结构,而该重复的结构本可以使用单一的抽象进行统一。
7.晦涩性(Opacity):很难阅读、理解。没有很好的表现出意图。
是什么激发了软件的腐化呢?在非敏捷环境中,由于需求没有按照初始设计预见的方式进行变化,从而导致了设计的退化。通常,改动都很急迫,并且进行改动的开发人员对于原始的设计思路并不熟悉。因而,虽然对设计的改动可以工作,但是它却以某种方式违反了原始的设计。随着改动的不断进行,这些违反渐渐地的积累,设计开始出现臭味。
然而,我们不能因为设计的退化而责怪需求的变化。作为软件开发人员,我们对于需求变化有非常好的了解。事实上,我们中的大多数人都认识到需求是项目中最不稳定的要素。如果我们的设计由于持续、大量的需求变化而失败,那就表明我们的设计和实践本身是有缺陷的。我们必须要设法找到一种方法,使得设计对于这种变化具有弹性,并且应用一些实践来防止设计腐化。
与传统的软件开发方法惧怕需求变化相反,敏捷团队依靠变化来获取活力。团队几乎不进行预先设计,因此,不需要一个成熟的初始设计。他们更愿意保持系统设计尽可能的干净、简单,并使用许多单元测试和验收测试作为支援。这保持了设计的灵活性、易于理解性。团队利用这种灵活性,持续的改进设计,以便于每次迭代结束所生成的系统都具有最适合于那次迭代中需求的设计。
上面的描述非常美好,读者不仅会问:敏捷开发人员如何知道要做什么的呢?
答案是:
(1)、他们遵循敏捷实践去发现问题;
(2)、他们应用设计原则去诊断问题;
(3)、他们应用适当的设计模式去解决问题。
软件开发的这三个方面的相互作用就是设计。
总结来说:敏捷设计是一个过程,不是一个事件。它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性的过程。它致力于保持系统设计在任何时间都尽可能得简单、干净以及富有表现力[1]。
设计原则
设计原则有助于开发人员消除设计中的臭味,并为当前的特性集构建出最好的设计。值得强调的是:这些设计原则是数十年软件工程经验来之不易的成果。它们不是某一个人的成果,而是许许多多软件开发人员和研究人员思想和著作的结晶。虽然在此把它们表述为面向对象设计的原则,但是事实上它们只是软件工程中一直存在的原则的特例而已。
这些原则如下:
1.单一职责原则(The Single Responsibility Principle,简称SRP):就一个类而言,应该仅有一个引起它变化的原因[1]。在SRP中,我们把职责定义为“变化的原因()”。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责。软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。事实上,我们将要论述的其余原则都会以这样或那样的方式回到这个问题上。
2.开放封闭原则(The Open-Close Principle,简称OCP):软件实体(类、模块、函数等等)应该是可以扩展的,但是不可以修改的。遵循开放封闭原则设计出的模块具有两个主要的特征。它们是:(1)、对于扩展是开放的。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。换句话说,我们可以改变模块的功能。(2)、对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者Java的.jar文件,都无需改动。
3.Liskov替换原则(The Liskov Substitution Principle,简称LSP):子类型必须能够替换掉它们的基类型。OCP原则是OOD中很多说法的核心。LSP是使OCP成为可能的主要原则之一。正式子类型的可替换性才使得使用基类类型的模块在无需修改的情况下就可以扩展。这种可替换性必须是开发人员可以隐式依赖的东西。
4.依赖倒置原则(The Dependency Inversion Principle,简称DIP):(1)、高层模块不应该依赖于底层模块。二者都应该依赖于抽象。(2)、抽象不应该依赖于细节。细节应该依赖于抽象。使用传统的过程化设计所创建出来的依赖关系结构,策略是依赖于细节的。面向对象的程序设计倒置了依赖关系结构,使得细节和策略都依赖于抽象,并且常常是客户拥有服务接口。事实上,这种依赖关系正式好的面向对象设计的标志所在。
5.接口隔离原则(The Interface Segregation Interface,简称ISP):不应该强迫客户依赖它们不用的方法[3]。如果强迫客户程序依赖于那些它们不适用的方法,那么这些客户程序就面临着由于这些未使用方法的改变所带来的变更。这就无意中导致了所有客户程序之间的耦合。我们希望尽可能地避免这种耦合,因此我们希望分离接口。
总结
敏捷软件开发方法正是认识到软件开发的这一本质特征而提出的革新性开发方法。使用敏捷开发方法会给我们带来巨大的好处。当然要完全做到也是很困难的。这不仅需要对敏捷的深刻理解,更需要敏捷团队成员的共同努力。