3、可视化需求调研,引导各种客户挖掘他们的需求
有的客户因为自己缺乏计算机知识,无法提出完整准确、隐含的或潜在的需求。但若这些需求不能满足将导致用户的不满。因此需求调研分析人员应善于想用户所想,不但要确定明确的需求,还要善于用启发的方式与用户探讨隐含的或潜在的需求,并结合各种调研分析技术挖掘超出客户期望的令人兴奋的需求。这就要求需求调研分析员要尽快完整地熟悉相关业务,从而能够站在用户的立场看待软件需求,想用户所想,做好业务与计算机之间的桥梁。利用可视化需求调研的方法可以很好地启发用户深入挖掘潜在的需求。可视化需求调研就是使用图表等工具来启发引导用户清楚地叙述需求,并且使需求更加全面完善。
对于高层领导,可以提供系统总体框架图;对于业务管理人员,可以用业务流程图来描述新旧系统的业务流程;对于客户中的技术人员,可以用数据流图、实体关系图或UML中的各种图形对系统进行各种角度的描述;而对于业务管理人员、客户中的技术人员、以及各层次各流程中的用户,画出用户界面图来进行需求挖掘,是个比较有效的沟通方式。
这里特别说明一下用户界面的重要性。用户界面的设计按理来说是软件设计的责任,当然客户自己对界面有特别提出要求的除外。但是,如果把它提前到需求调研时(紧接着原有系统调研分析和系统模型完成之后)与客户进行讨论,则可以大大改善需求调研的效果。因此不少需求分析的著作把用户界面说成是“设计层”的需求之一。因为这时客户对于将来的系统还没有一个形象上的概念,或者有一个模糊的预想的概念需要表述、验证、明晰化、完善化。以笔者的经验,画出用户界面草图与客户进行讨论,可以大大激发他们提供更为准确全面的需求。原来收集资料,描述业务,说明系统模型到了山穷水尽的时候,这种方法可以达到柳暗花明又一村的效果。在《微软项目:求生法则》的第8章“需求开发”中,从头到尾都是围绕着“使用者接口”(USER INTERFACE也可以翻译成“用户界面”)进行讨论,如“建立简单的使用者接口雏形”、“不断修订使用者接口雏型,直到使用者对软件感到兴趣盎然为止”、“完全扩展使用者接口”,同时还要“区分一份非使用者接口需求文件”,等等。因此,所谓需求就是“当你按下各种相关按钮(或输入各种相关命令)时系统做什么”,所谓设计就是“当你按下各种相关按钮(或输入各种相关命令)时系统怎么做”。虽然在英语中“接口”与“界面”实际是同一个单词,但“接口”的含义似乎比“界面”来得广泛,如功能之间的接口、与其他软件的接口、与其他硬件的接口等等。需求的最终目的实际上是完整准确地描述系统需要的各种接口或“界面”,及它们的相互关系或与外部环境的关系,如界面中的某个按钮按下去时,可能产生新的界面、新的按钮、或者调用其他软件硬件完成某些功能。自顶向下,把这些界面及涉及到的数据描述清楚,就是一份不错的需求。
怎么做”。虽然在英语中“接口”与“界面”实际是同一个单词,但“接口”的含义似乎比“界面”来得广泛,如功能之间的接口、与其他软件的接口、与其他硬件的接口等等。需求的最终目的实际上是完整准确地描述系统需要的各种接口或“界面”,及它们的相互关系或与外部环境的关系,如界面中的某个按钮按下去时,可能产生新的界面、新的按钮、或者调用其他软件硬件完成某些功能。自顶向下,把这些界面及涉及到的数据描述清楚,就是一份不错的需求。
4、与其他项目小组成员共同协作、持续完善系统需求
需求文档完成之后,并不是万事大吉,把它扔给后面的设计人员就了事了。作为项目干系人之内的项目组其他成员,对需求的有效性也起到某种程度的验证作用。虽然软件项目的生命周期按照各种开发模型有不同阶段的划分,但每个阶段的结束不是简单地把阶段工作成果塞给下一阶段的成员就可以了。特别是高科技的软件开发项目,上一阶段的工作成果往往要通过多次的沟通才能更为清晰地被下一阶段成员接受,其有效性、合理性也要被下一阶段的工作所检验,通过检验有时也有必要对上一阶段的工作结果进行相应的调整,需求更是如此。因此,无论是同一阶段不同人员之间,或是不同阶段人员之间都应根据需要相互协作,相互配合,共同完成软件开发任务。
二、在软件工程的方位内,尽量减少需求变更的影响
系统的各模块应该设计成送耦合的,采用OO 技术可以建立易于改变和加强可重用性的软件系统。对于OO 技术,我想现在已经不是什么陌生的概念:
1 封装(Encapsulation )可以把问题影响的范围缩小,外部的变化要求对系统的影响可以限定到某个类层次或某些类层次中,从而改变系统的一部分相对简单;
2 继承(Inheritance )可以使改变基于原有技术基础,很大程度上减少重复开发工作;
3 多态(Polymorphism )的应用可以使开发和设计人员在相对统一的接口下更改系统的实现细节,从而改变系统的行为;
4 而且由于对OO 的类体系结构业界有非常清楚明晰的描述方式,就是目前规范的描述语言-UML ,非常易于被开发组的理解并达成共识,促进开发组成员之间的合作以及加强软件开发工作的可延续性;
可见本身即是一种增强软件可维护性、健壮性以及保持设计稳定性的一种分析和设计方法,本身可以在一定程度上快速对需求变更进行反应,并可相对减少需求变更需要的成本。(OO 的意义在于分析和设计软件系统的思考方式,以及建立对象库以后的软件重用将给软件系统的开发带来质的改变,但是在建立OO 开发体系之前的过程,一定会是一段荆棘遍布的路,需要付出加倍的努力以及达成思想的转变。这里还有一个误区需要澄清的是很多人以为用了C++,PB ,VB ,DELPHI 就是面向对象的开发了,其实只是用了一些面向对象的工具,骨子里仍然是结构化的分析和设计方法,套上一层OOP 的外壳而已。)
可扩展性设计(Extensible-Design )
其次,从我们可以控制的软件设计来说,怎样进行合适的设计才能最大程度减少需求变更带来的代价?
也许有人说,我的设计极为灵活,我已经预计了客户可能提出的要求,并设计几种应对的方式,到时候客户提出来,呵呵,我已经解决了。这样的想法不错,至少比僵硬的设计强,但是谁可以保证设计者可以预知以后的需求变化?而同时为了达到这种灵活(功能较多/多能?)的设计,设计将变得复杂,而且可能那些多余的设计从来不会被用到?复杂的设计将增加实现的难度和提高成本,并有可能带来潜在的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 类将来可能的状态留出接口,只需要不断派生具体的不同状态子类就可以实现将来的状态变更,并且无须影响原有的设计,也无须加入多余的代码来实现现在还不需要的功能,所以这是一个优美的、可扩展的设计思路,非常清晰,易于维护,相信可以给我们在做软件设计时带来一些启发。
系统应该采用Open 的技术标准采用SOA 的开发架构,是进一步降低系统耦合度的措施在经典软件工程理论中,不管是瀑布方法还是原型方法,都是从需求分析做起,一步一步构建起形形色色的软件系统。但是,需求变更像一个挥之不去的阴影,时刻伴随着系统左右。每一个实际应用系统的开发者都饱尝了在系统进入开发阶段、测试阶段,甚至上线阶段遭遇应接不暇的需求变更的极端痛苦。客户将变更的需求视为bug(错误)是测试上线阶段的主要问题。