在这一部分的 Java 建模中,Granville引领您进入介于建模和方法之间的区域,同时看一下通过用例建模所收集的需求。他特别着重讨论了用户接口、系统接口和用例描述之间的关系。尽管现在正试图在用例中包括用户接口逻辑,但这通常被认为是不好的形式。接着,Grancille用序列图和系统接口告诉您具体原因。请点击文章顶部或底部的 讨论,参与讨论论坛,与本文作者和其他读者分享您对本文的想法。
需求收集是任何成功的软件开发周期中不可缺少的一步。虽然有众多的需求收集方法,但是最普通的方法是用例建模。在 先前的两个专栏中,我们已经完成了一部分将序列图同用例建模关联起来的工作。这次我将更多地谈论方法之后的理论,并且也增加一些您的建模词汇。
这次讨论中,我更关心的是阐明用户接口、系统接口和用例描述之间的关系。因为所建立的大多数系统将被设计成人机交互式的,所以将用例描述设计成以用户接口开始运行是很诱人的。但是在用例中包括用户接口逻辑通常被认为是不好的形式。这种说法的一个简单解释是,用户接口提供一个系统透视图的系统概貌。用例总是以参与者(或用户)透视图被描述。
为了真正理解为什么我们在用例描述中不包括 UI 逻辑,我想无论如何我们不得不采用亲身实践的方法。我们将使用我的 第一个专栏中的贷款申请的示例,并且您将看到用例是如何随着尺寸的增大而变得复杂的。特别地,我们要注意在用例建模中透视图的角色。随着我们的深入,您将看到透视图是怎样被用来为您工作的,或者,如果被不正确地应用,它是如何阻碍您工作的。
什么是用例模型?
一个用例模型由一张图表和一组阐明该用例的描述组成。一个用例是在一个系统中的一组可能的交互,它的参与者朝着同一个被定义的目标进行。这些描述描述了系统中该用例的功能性;这张图表提供了这些描述的可视化路标。UML 规定了建立用例图表的标准,但并不是为了编写用例描述的。结果产生了许多编写用例描述的方法,这些方法有时是互相竞争的。
最流行的编写用例描述的方法体现着 Ivar Jacobson (用例建模的发明者)的思想。Jacobson 的方法涉及一系列进入和退出的准则,分别被称作 前置条件和 后置条件,和一个称为 事件流的核心准则。这个事件流描述了一系列参与者(用户或外部系统)和被制定的系统之间的交互。这个事件流代表一个经过系统的通向成功输出的单一路径。这是用例描述的核心部分,但不是全部。
事件流的交替和例外
除描述中心事件流之外,用例描述必须说明那些发生在普通事件流之外的交互。例如录像带租用用例的主要事件流(在简单情况下)可以如下表示:
- 录像带租赁店店员扫描顾客的会员卡。
- 系统取得会员名和他目前的租赁状况。一个“允许租赁”状态表示这个顾客可以租用录像带。
- 录像带租赁店店员扫描每盘被租借的录像带。
- 系统通过扫描每盘录像带,将可出租的录像带加入到用户可见的列表中,并显示当前的可出租的录像带列表。
- 录像带租赁店店员输入应收取的钱的数量(如果是现金)或者扫描信用卡。
- 系统标记这盘录像带为已在某段时间被出租并且打印这笔交易的收据。
但是如果顾客在上次租借中欠了逾期费怎么办?在她能再次租借她所选的录像带之前,她需付清所欠的逾期费。逾期费的交互表现为一个 交替流或 例外流。事件流的交替和例外是很正常的。在某些情况下,他们可以被纠正以重新开始正常的事件流,在其他情况下,他们则达不到目标。在我们的示例中,如果顾客付了逾期费和这次的租金,那她就达到了继续租借录像带的目的了。
用例建模中的事务处理
伴随着它的交替和例外,事件流是由一系列的事务处理组成。 事务处理是由参与者发起,并且当系统等候来自参与者的触发信号时完成的交互(注意完成事务处理的参与者不一定就是发起该事务处理的参与者)。事务处理允许我们把用例分割成更小的元素,并在每个决定点上将逻辑分组。 决定点是在描述中参与者必须作出决定或者提供额外信息的那个点。
所有的事务处理是由一个参与者和一个系统交互组成。您将极少需要计划一个没有启动的系统,即使这个启动仅仅以时间为基础。当建立用例模型的时候,您必须确保每个启动被某种类型的系统响应访问到。这个调用和响应对于用例来说是完整的。
序列图中的事务处理
事务处理在序列图中是很容易识别的。在我的第一个专栏中,我介绍了只有两个事务处理的方案。当申请者请求新的贷款申请时,第一个事务处理被启动。这个事务处理以系统等待申请者填写请求和提交请求为结束。当申请者在线提交了贷款申请时,第二个事务处理被启动。它以系统请求商业资信咨询机构的信用报告为结束。
图 1 让我们再看一下建立用来描述这个方案的序列图 -- 提交贷款请求和它的两个事务处理。这个图表建立了从开始到结束的两个事务处理的模型。您将会想起,这是一个一般的序列图,它允许我们在以后添加更多的方案(从而加入更多的事务处理)。至少就这个开发循环的分析阶段而言,当我们添加方案时,我们将完成这个用例。然而,当我们转向设计的时候,我们可能发现我们需要添加更多的事务处理。例如,如果我们选择作屏幕到屏幕的确认(代替当前在提交时的单一确认),我们必须在每一步确认时添加一个事务处理。
现在看一下图 1,仔细注意事务处理的描述。以识别信息的方向从参与者到出现在最接近顶部的一个类或者实例为开始。正如您在下面的图表中看到,第一个事务处理通常是从左边开始。沿着箭头的顺序直到您到达了另外一个参与者或者顺序结束。当顺序结束时,它返回到最初的参与者。这就是一个事务处理。在图 1 中您应该能看到两个完整的事务处理。
象其他大多数用例那样,提交贷款请求用例使用多重的事务处理。迄今为止,我们只列出了其中的两个图表,但是一个普通的用例使用大约 3 到 15 个事务处理。
为了理解透视图和事务处理的关系,我们可以看一下当两个系统通讯时事务处理是如何表现的。一些软件系统实际上是一系列互连的较小系统。这些较小系统相互合作提供整个系统的功能性。每个较小系统只提供整个系统功能的一个子集。他们通过一组协议和机器接口进行通讯,这将把我们的用例模型提高一个全新的复杂程度。
互连系统的系统建模
当考虑到互连系统时,这对建立由较小互连系统组成的大系统有意义。您可以交换一个系统并且用其它的系统替换它。您也可以独立地建立每一个系统。并且您可以用许多站点或厂商来完成整个系统。
这样一个系统的最好示例是典型的电话网络。电话网络的一部分提供拨入通道,另一部分传送声音或数据,还有另一部分提供帐单服务,以及有许多其它部分开展象呼叫转移和语音邮件这样的服务。电话网络也许是由互连系统组成的系统的一个最大的示例,并且它的连续工作也证明了这种系统的有效性。同样的,懂得如何构思和建立这样的系统模型是十分重要的。
一个相似的示例
我们将使用一个相似的贷款处理申请来建立一个由互连系统组成的系统的模型。到现在,我们已经建立了提交贷款请求用例的模型,但是这个用例实际上只是一个较大的贷款处理系统的一部分。贷款提交系统和其进行交互的商业资信咨询机构是两个必须合作以提供必要的数据来处理贷款请求的系统。在现实生活中,还会涉及额外的系统。然而,我们的示例仅仅讨论两个系统。图 2 显示了两个系统、贷款提交和商业资信咨询机构系统以及我们将在这个专栏的余下部分着重讨论的交互。(注意,图 2 不是一个 UML 图表;它是一个真实世界的图表,目的是简单地图示这两个系统和它们之间的交互。)
正如我们前面图表说明的那样,图 3 从贷款提交系统的透视图中显示了提交贷款请求用例的子集。这张图说明了我们第二次事务处理的结束是在申请者提交贷款请求时开始,在贷款提交系统 ( CreditChecker
) 给代表商业资信咨询机构 ( CreditBureau
) 的参与者发出请求时结束。
现在,让我们注意来自于商业资信咨询机构系统的透视图的这个请求。我们将以代表请求一份信用报告的一个外部系统 ( CreditInstitution
) 的参与者开始。从商业资信咨询机构的透视图来看,当参与者请求报告时事务处理开始并且在商业资信咨询机构系统 ( CreditReporter
) 返回这个报告时结束,就如图 4 中的图表所示。
回到贷款提交系统,我们现在能扩展提交贷款请求用例以包括报告的接收。当 CreditReporter
返回报告时,一个新的事务处理开始,并且报告被加入到与申请相联系的信息中。然后我们可以通知贷款官员(一个新的参与者)新的申请已到达。这将是我们的贷款提交方案中的第三个事务处理。为了创建整个用例,我们需要再加几个事务处理,但是我们将那个练习留到以后来做。
逆向透视图逻辑
上面的图表说明了互连系统的系统建模中的两个重要方面。第一,从信息顺序透视图我们可以容易地将贷款提交和商业资信咨询机构系统作为单一的系统来进行建模。图 5 显示了这两个系统是如何在一个单一的图表中被建模成一个系统的。
这项技术在我们开发循环的分析阶段中一直起作用。然而,当我们进入设计阶段时,两个系统中的通讯机制建模的必要性使得单一的图表过于复杂。除非我们使用一种象 CORBA (请参阅 参考资料)这样的通讯技术,否则我们一旦进入设计阶段,就必须分别为这两个系统建模。要分离它们,我们将插入一些参与者(就如图 3 和图 4 中所示)来分别互相代表两个系统。
第二个观察更加微妙,也是我们这周讨论的中心:我们能对这些服务形成一个概念上的理解,这些服务必须通过系统通过观察其它系统的需要来提供的。换句话说,我们可以通过查看其它系统的用例中的事务处理信息的子集来为每个系统确定概念上的接口。
接着考虑贷款提交系统的第二和第三个事务处理。尤其得让我们查看第二个事务处理的 系统部分和第三个事务处理的 参与者部分。如果从概念上研究我们的商业资信咨询机构,参与者部分 ( CreditReporter
) 是贷款提交系统的系统部分的一个子集。也就是说,贷款提交系统用例表示“系统向商业资信咨询机构发送一个请求要求信用报告的一个副本。”
另一方面,商业资信咨询机构系统用例表示“这个信用机构参与者请求信用报告的一个副本。”这样,商业资信咨询机构事务处理的参与者部分是贷款提交系统的第二个事务处理的系统部分的一个子集。这对于第三个事务处理也是一样的,商业资信咨询机构事务处理的系统部分是第三个事务处理的参与者部分的一个子集。换句话说,事务处理的部分可以在两个系统间逆转。
在两个系统间概念上的逆转事务处理提供了在两个互连系统间的概念上的粘合。例如,我们知道我们需要一个进入商业资信咨询机构的接口,以提供来自于贷款提交系统接口的信用报告,贷款提交系统接口同样请求这些报告。
怎样把这个应用到用户接口
我们并不是经常建立互连系统。然而,我们确实为我们所建立的大多数系统创建用户接口。我们通过逆转它们的用例事务处理将两个系统间的机器接口概念化。如果我们用一个用户接口替换一个机器接口,这也是对的。
作为这篇文章的示例所显示的那样,我们为我们的系统所建立的用户接口将成为我们从相应的参与者透视图得出的用例描述的逆转。这就是为什么不能依靠用户接口透视图来编写出我们的用例。这儿的逻辑是创建概念上的用户接口,但是事务处理将不得不在应用之前被逆转。
也就是说,如果您已经在编写基于 UI 的用例并能够以这种方法提供的话,那就坚持下去。您的提供能力比那些制定标准的头头们制定的标准远远重要。然而,如果使用这种方法陷入混乱,那么,您现在知道为什么了。
下次我们将着眼于在灵活处理中捕获请求的不同机制,以及何时和为什么您应该使用那些特色、用户经历和在一个开发项目中的用例。下次见!