代价?
也许有人说,我的设计极为灵活,我已经预计了客户可能提出的要求,并设计几种应对的方式,到时候客户提出来,呵呵,我已经解决了。这样的想法不错,至少比僵硬的设计强,但是谁可以保证设计者可以预知以后的需求变化?而同时为了达到这种灵活(万能/多能?)的设计,设计将变得复杂,而且可能那些多余的设计从来不会被用到?复杂的设计将增加实现的难度和提高成本,并有可能带来潜在的Bug ,使得系统难以维护。
设计的思想应该有一些小小的转变,那就是,设计确实要灵活,但是要体现在可扩展性上面,也就是说,设计可以简单,但是一定要易于转变,需要给出便于改变的接口,这一点很重要。
例如,现在有一个类叫做TCPConnection ,来代表计算机网络通信中典型的TCP 连接,对于这个连接而言,它可能处于以下几种状态:Established (连接已建立),Listening (正在侦听),Closed (连接关闭)。一个连接对象需要从其他的对象接受请求,至于它的反应则决定于连接对象所处的状态,对于(打开连接的请求),如果是在连接关闭状态,则进行Open (),处于其他状态则不做反应;同样,如果在连接建立和侦听状态,可以进行Close (),在连接建立状态可以进行Acknowledge (),即接收数据。
对于这样的状况,最不可取的设计应该是用一系列的Switch 语句(甚至If/else 语句)进行Hard 设计,对于以后每一次需求改变,都需要改变源代码,接踵而来的系统一致性、文档更新等工作将使开发人员不可避免地陷入一场灾难,这样的后果将导致原来就不合理的设计变得更加支离破碎,系统维护的代价将越来越大;就算没有需求变更发生,这些设计的可重用性也会极差。稍好一些的设计是预先估计并设置TCPConnection 类所有可能的状态,并预先加入设计,这种需要付出更多的设计、开发、维护的代价,而且也很难达到完美的效果,所以不多说了。
下面介绍一种经典的设计思路,这种设计可以充分体现“为(系统)将来改变预留接口”的可扩展性(Extensible-Design )思想,并且很好的实现了这一思想。在这里,我们引入一个抽象类TCPState 来代表TCPConnection 类的状态,给出具体各种状态的通用操作接口,并派生出不同的子类(实现具体的操作)
去实现TCPConnection 类的不同状态,例如派生出TCPEstablished 类来实现TCPConnection 类的连接建立状态。结构图示如下:
只需要在TCPConnection 类中包含一个TCPState 的状态引用,并在TCPConnection 的状态改变时更新为当前的状态引用,例如在连接关闭时进行Open (),状态引用就应该从TCPClosed 变成TCPEstablished ,这样就实现了原来的要求。
但这个设计思路的意义远不止于此。我们可以看到,抽象类TCPState 已经为TCPConnection 类将来可能的状态留出接口,只需要不断派生具体的不同状态子类就可以实现将来的状态变更,并且无须影响原有的设计,也无须加入多余的代码来实现现在还不需要的功能,所以这是一个优美的、可扩展的设计思路,非常清晰,易于维护,相信可以给我们在做软件设计时带来一些启发。
可见,在面对需求变更时,除了客观上可以通过人员培训、代价分析等管理方式进行有效的需求管理外,从分析和设计的角度可以通过采用合理的分析和设计方法,还有改变我们设计的意识,可以做到对需求变更的灵活应对,至少可以在一定程度上降低维护代价和提高用户满意度。
软件需求的管理和控制是非常专业的学问,作者在这里结合自己的实践提出一些粗浅的认识,只是想起到一个抛砖引玉的作用,希望大家可以一起来面对和想办法解决我们在系统开发过程中的实际问题,我想那样才是我真正想达到的目的。
项目经理胜任力免费测评PMQ上线啦!快来测测你排多少名吧~
http://www.leadge.com/pmqhd/index.html