当我们从用例图转入到序列图和类图时,马上就会注意到已知的参与者的影响。我们最初的序列图(在 UML 工作簿以前的部分中开发的)包含了已知的参与者。在这个层次的分析上,我们把系统与它的参与者之间的交互描述为一系列消息。因此,我们不是在处理个别的人或系统;相反地,我们把那些与系统交互的人或系统的细节封装到抽象的实体中,我们称它为参与者。图 3 图示了贷款申请用例的序列图的一部分。想查看完整的序列图,请参阅 UML 工作簿以前的部分。
图 3. 贷款申请用例的序列图的一部分
当我们从序列图转到类图时,我们的系统分析就更精细了。因为在这个层次上,参与者是与需求和行为联系在一起,所以我们不再处理参与者的问题。相反,参与者背后的抽象概念可能将被提出来。在这个层次上出现的大多数“参与者”都将至少由一个属性标识;否则它们就是系统在分析阶段时所不感兴趣的参与者。任何级别的标识 ― 哪怕只是一个属性 ― 就能把一个参与者变成已知的参与者。而且,已知的参与者在类图中已不再是一个参与者;它成为了一个类。
这个转变可能就像创建一个类来表示参与者那样简单,如图 4 所示。系统还可以将我们的参与者“看作”是更复杂的元素。在这个例子中,将会得到几种抽象。镜像映象模式的用武之地是后一种情况。镜像映象模式取得外部实体,然后将它转变成系统的一部分。
在我们的贷款申请示例中,通过将已知的参与者转换成类而得到的类有两个。申请人和征信所都必须有标识性的特征。(如果我们的系统不要求这两个实体的标识性特征,那将为诈骗行为大开方便之门。)
图 4. 一个利用了镜像映象模式的类图
在我们简单的模型中,存在一个参与者与所添加的类之间的一一映射。然而,参与者通常表示“更深层的”交互,可能产生多个类和更复杂的交互。一个参与者可能需要添加许多类,而其中没有一个类使用了原始参与者的名称。
类图除表示已知的参与者之外,还常常表示服务器角色和接收器角色。这些角色可以是已知的或未知的,但为了系统能够使用由它们提供的服务,必须有一个或多个类来表示所提供的服务。通过 TCP/IP 示例,我们看到了这一点。
匿名参与者
诸如启动器或代理这样的匿名参与者也会使一些类被添加进来。但这种添加与设计而不是分析有关。我们注入这些类是由于与实际的系统实现有关的一些原因,而不是要反映业务约束。例如,我们可能会添加想与模型分离开来的用户接口逻辑,或者我们可能会出于性能方面的原因而添加某些类。
在 EJB 开发中, 会话虚包(Session Facade)模式常被用来使网络流量最小化和确保事务一致性。在 Web 服务的开发中,这一模式也有极重要的作用,该模式还是匿名参与者在系统设计中的用法的典型示例。会话虚包模式用对会话 bean 的单个调用来代替对实体 bean 的多个调用。新的会话 bean 代表客户机对服务器上的实体 bean 进行调用。
为了说明会话虚包模式,我们来考虑这样一个用例,用户能将贷款支付的帐目记入她的支票帐户的借方。如果我们用实体 bean 来实现这个付款用例,则一个简单的事务需要跨网络进行四个调用,如图 5 所示。此时您可能会回想起来,在序列图中,斜向箭头表明消息有较漫长的响应时间(这是跨网络发送消息而不是直接将消息发送到对象的结果)。此外,有可能其中某个事务永远也不会实际完成。
图 5 说明了实体 bean 将如何管理付款用例。
图 5. 用实体 bean 的办法来实现偿还贷款
对于我们的用例,单单实体 bean 显然不会是一种好的实现。性能就很成问题,而无法完成最后一个步骤(完成支付)可能会是一个更大的问题。通过在这个方案中加入一个会话 bean,会话虚包解决了这些问题。会话 bean 充当参与者的本地代理。
用会话 bean 进行建模
从逻辑上说,会话 bean 封装了它所代表的参与者所希望的操作。这样,会话 bean 就为我们与实体 bean 的交互提供了一个 虚包。会话 bean 让我们不用在网络间拖动数据几次,而是通过服务器上的一个事务就可以实现我们的目标。而且,注入会话 bean,就确保了用户事务的原子性,从而付款将被安全记入贷方。例如,会话 bean 会回滚无法完成的支付的任何借项。这就保证了我们用户的钱不会凭空消失。
会话 bean 还能代表参与者进行多种操作。会话 bean 的行为与参与者在用例模型内汇集起来的行为一致。因此,如果一个申请人参与者发起了一个申请贷款用例和一个接受贷款用例,则这两个用例的工作流将被收集到申请人会话 bean 中。申请人会话 bean 可以申请一项贷款,然后在另一个事务中接受它。
图 6 说明了引入会话 bean 给我们的付款用例带来的变化。
图 6. 用会话虚包的办法实现付款
正如我们已经看到的,会话 bean 被指定给某个参与者,利用它对该参与者的特有了解,会话 bean 既方便了参与者的事务又增强了系统性能。会话虚包模式可用于已知的和未知的参与者。这种模式不太用于服务器角色和接收器角色的参与者。它更多是为了启动器或代理角色的参与者而被实现。显然,付款用例中的客户是启动器角色。
结束语
对于将用例图中的参与者转换成类图中有效的抽象,镜像映象模式和会话虚包模式是很有用的,这样最终将能够更清楚地转换成代码。已知的参与者通常在系统的逻辑中能有某种形式的体现;匿名参与者也一样。
从图到代码的转换,其更重要的含义是 可跟踪性。通过使用诸如镜像映象和会话虚包之类的模式,我们就可以跟踪类的创建过程,以反映出外部实体的标识或它所提供的服务。我们可以通过其逆过程理解使这些类得以产生的元素。这些转换的目标是使抽象更好,代码更易于理解和维护。