Granville Miller 在其新专栏的第一部分中介绍了“统一建模语言”(UML) 的一个构件:序列图。在整个设计过程中都会用到序列图,此图用于演示系统执行时参与者与对象之间的内部交互。让我们跟着 Granville 一起创建其中一个图,我们将使用一个贷款处理应用程序作为示例。
统一建模语言 (UML) 是用于建立面向对象系统模型的标准标记法。在 1995 到 1997 年之间,UML 登上了面向对象编程社区的舞台,而且在 1997 年后期受到了对象管理组织 (OMG) 的认可。虽然它最初颇受争议 -- 因为它是在一片支持和反对声中提出的 -- 但 UML 以后却成为系统标记法的行业标准。UML 的当前版本是 1.4,而且它将不断发展以满足面向对象开发人员的需要。(有关 UML 历史的详细信息,请参阅 参考资料。)
UML 可能难以学习,主要因为它试图为相当广泛的情况提供建模标记法。每种建模标记法都采用一种图,而目前在 UML 规范中有九种图。幸好,学习 UML 可以是一个渐进过程;每次可以只学习一种图,首次试用时不必包含图中非常复杂的东西。
在本专栏中,我将教您逐步学习基于 Java 应用程序开发的 UML 设计和标记法。我将以逻辑(可能是另人愉快的)方式介绍 UML 框架和其它建模技术的基础知识,您将通过建立现实示例的模型来学习实际操作。在第一部分中,我们使用贷款处理应用程序作为示例,从建立序列图开始。请注意,假设您熟悉 Java 语言,而且掌握了面向对象方法和术语的基础知识。本专栏将简要说明面向对象的概念,但不会进行深入讨论。
关于参与者角色
|
UML 没有排斥任何特殊的软件开发方法或过程;它只不过标准化了标记法的格式。然而,许多开发方法都合并了 UML。Rational 统一过程 (RUP) 就是这样一种方法;另一种方法是功能驱动的开发 (FDD)。由于其直观性和通用性,UML 序列图已成为这些过程的前端建模活动的一部分。序列图用于建立以下内容的模型:
- 用例方案
- 框架中的协议
- 子系统
- 类
- 方法逻辑
以下是上述每个功能的简要说明。
用例方案
对于示例应用程序,我们将使用序列图来建立单个用例方案的模型。用例是由参与者与应用程序交互为实现某个指定目标而执行的单个任务。参与者可以是与应用程序交互、在应用程序外部的任何最终用户、组织或系统。(要了解四种参与者角色,请参阅 关于参与者角色;如需关于用例方案的深入讨论,请参阅 参考资料。)
框架中的协议
协议位于框架及其称作 集合体的可交换组件之间。了解框架必需的交互有助于开发新的集合体。序列图通常用于记录这些交互。
子系统
大的项目被分解成更小且易管理的部分,称作 子系统。子系统之间的接口关于将它们集成成为更大的整体(即,系统)是至关重要的。序列图用于指定子系统边界上的类之间的交互。
类
某些类(如 Socket
和 InetAddress
)需要一个复杂的方法调用序列,以便正确交互。这些序列组成了用于与这样的某个类或一组类交互的协议。序列图可用于描述类或一组相互作用的类的使用,因而描述了交互所需的协议。
方法逻辑 序列图在记录方法逻辑方面非常优秀。实际上,只要给出了 Java 方法,某些 CASE 工具会自动生成一个序列图。序列图可用于设计未来方法或记录现有方法的流程。
关于示例应用程序
我们将在示例贷款处理应用程序的帮助下学习序列图。因为本专栏的重点在于建模,而不是方法,我们要直接讨论建立图,所以我们不会注重于应用程序的细节。我们为贷款处理应用程序绘制的基本功能如下:
用例:提交贷款请求
- 申请人通过因特网完成并向银行提交贷款申请。
- 系统确认贷款申请的信息,检查它是否正确以及信息是否尽可能完整。
- 系统向外部商业资信咨询机构转发贷款请求,以获取该申请人的信用报告。
- 系统根据所返回的信用报告计算申请人的信用积分。
入门
创建序列图的第一步是确定该图是否表示与外部或内部实体的交互。如果正在建立用例方案的模型,那么序列图通常表示与外部实体的交互。如果正在建立框架中协议的模型,那么此图也许表示内部或外部交互。子系统图、类图和个别方法逻辑图通常只表示内部实体。无论是哪种情况,将要建模的交互类型决定了序列图中的第一个(最左边的)元素。
与外部实体的交互表示参与者是交互的一部分。内部交互可能由参与者启动(如果子系统用例是交互的基础),但它更可能是由叫作 Sender
的普通类启动的。如果参与者启动了交互,那么参与者将归入启动程序类别,这是四种常见参与者角色之一(有关详细信息,请参阅 关于参与者角色)。
我们将关注为贷款处理应用程序(以上概述的 提交贷款请求用例)的方案建立图。请注意当申请人完成在线贷款申请并在因特网上提交它时序列图的变化。在这个方案中,申请人在系统外部,因此用参与者来表示他。我们首先将参与者 Applicant
添加到序列图,如图 1 所示。
添加参与者
一旦交互的启动程序就位,下一步就是添加它在方案的过程中将与之交互的对象。这些对象的名称应该反映出类或实例的行为。(类或实例的选择给出了与序列图截然不同的含义,但我将把这两者之间差异保留到下次讨论。)
对于示例方案,我们将添加两个类: LoanApplication
和 LoanRequest
。当申请贷款时,需要提交贷款申请。它包含了关于申请人和期望贷款金额的信息。贷款请求是银行向商业资信咨询机构发送的关于接受贷款申请的表单。它包含了来自贷款申请的一些信息,以及获取关于申请人的信用历史记录信息的请求。图 2 中显示了将这两个类添加到序列图中。
连接各点 -- 虚线
对于大多数软件开发人员来说,序列图是直观的。它们将对象和参与者(横轴)映射到时间(纵轴)。消息连接了对象,当消息发生时,它们沿着纵轴从一个对象移动到另一个对象。这些消息被连接到从对象或参与者底部的中间延伸出的竖直虚线。这条线被称作 生命线。
在横轴上,我们用被称作 调用箭头或 消息箭头的箭头来表示消息。消息箭头从发送方(尾)指向接收方(头)。这些箭头用于捕捉系统的动态行为。调用通常始于左边,向右边移动。即,交互中的初始箭头通常来自左边。当创建类的新实例时,我们绘制的箭头都指向类自身,而不是生命线。我们方案中的第一步是创建新的贷款申请,因此我们在 Applicant
和 LoanApplication
之间画出箭头。由于用 Java 创建新实例涉及到调用构造器,因此我们将这个箭头标上构造器名称,可能的话,还要标上变量。
我们仍处于软件开发生命周期中的分析阶段,因此我们要仅可能多地包含分析信息。我们的一个商务分析员提到我们把创建新贷款申请的操作称作“完成贷款申请”。如果我们要在构造过程中使这个序列图保持为真,那么可以将 complete
实现成公用方法,它将调用 LoanApplication
构造器,如图 3 所示。
建立某个活动的图
但类或实例接收到消息时,它会在接收对象的生命线上创建一个框;这个框称作 活动。活动表示接收方的方法中的控制权流向。当消息导致创建了一个对象时,第一个活动表示构造器的逻辑。随后的消息将导致创建新的活动。
但接收到消息时,接收对象可以依次将消息发送到它自身或其它对象。这由箭头的尾巴显示,它表示箭头源于活动,而终止于新的活动。但对象调用自身时,新的活动将放到旧活动之上。
在此方案中,申请人与贷款申请交互两次,第一次是完成它,而第二次是提交它。当 LoanApplication
接收到 submit 消息时,它会通过将 validate 消息发送到自身来验证它自己。如果有效,则它会创建要发送到商业资信咨询机构的新 LoanRequest
。图 4 显示了 LoanApplication
的验证过程。
箭头文件:表示时间流逝
我们使用斜向箭头来表示消息发送和接收之间的真实时间的流逝。这种标记法用于显示非基本调用。非基本调用的示例是经由 CORBA 或 RMI 进行的方法调用,或是在网络上发送的消息。
在此示例中,商业资信咨询机构是一个外部系统,它是具有服务器角色的参与者(有关详细信息,请参阅 关于参与者角色)。服务器通常不生成消息,但会有发送给它们的消息 -- 在本例中是由信用检查器发送的信用报告的请求。信用检查器代表了商业资信咨询机构。它跟踪请求并将请求转发给商业资信咨询机构,跟踪并接收响应,此外就是建立贷款处理应用程序和商业资信咨询机构之间的连接。商业资信咨询机构将接收请求,并根据它自己的时间表处理请求。我们使用斜向箭头表示这段时间的流逝,如以下的图 5 所示。
在活动的结尾,返回到调用程序的结果是隐式的。然而,在某些情况下,您可能希望返回是显式的。显式返回调用由虚线箭头表示,该箭头的尾巴是接收方,头是发送方。显式返回箭头通常由调用返回的值标记。对于此示例,我们已经在 CreditBureau
和 CreditChecker
之间添加了显式箭头。这个箭头可以是带标签的 CreditReport
,因为它是从 requestCreditReport
方法返回的对象。
下一步
我在本文开头已经提到过,序列图用于描述系统运行时的内部行为。在本文中,我已经介绍了通过建立对象间交互的模型来构建序列图的第一步。在下一部分中,我将介绍两种序列图(常规和实例),并使用根据简单的 Java 方法绘制的示例说明条件逻辑在序列图中的角色。到时候见!