本章要求考生掌握以下几个方面的知识点。
(1)UML的基本概念与作用
(2)用例图的表示与应用
(3)类图与对象图的表示与应用
(4)序列图的表示与应用
(5)活动图的表示与应用
(6)通信图的表示与应用
(7)组件图的表示与应用
(8)部署图的表示与应用
(9)状态图的表示与应用
本章的考点主要集中在这几种图的应用:用例图、类图与对象图、顺序图、活动图、状态图。
目录
一、UML基础知识
UML是一种与开发方法无关的建模语言,其应用十分广泛。本节将从它的起源、概念、组成部分等方面展开论述,最后将对各种常用的UML图进行详细读。
1. UML的起源
前面的章节已经提到过软件开发方法有三种:结构化方法、面向对象方法、原型法。其中能应 用于软件全生命周期的是:结构化方法与面向对象方法,原型法一般只用于需求分析阶段。
面向对象方法是在结构化设计方法出现很多问题的情况下应运而生的。从结构化设计的方法中,我们不难发现,结构化设计方法求解问题的基本策略是从功能的角度审视问题域。它将应用程 序看成实现某些特定任务的功能模块,其中子过程是实现某项具体操作的底层功能模块。在每个功 能模块中,用数据结构描述待处理数据的组织形式,用算法描述具体的操作过程。面对日趋复杂的 应用系统,这种开发思路在以下几个方面逐渐暴露了一些弱点:审视问题域的视角、抽象级别、封 装体、可重用性。这样就催生了一批面向对象方法,形成百家争鸣的局面,后来由Booch方法、OOSE、OMT三大主流OOA技术的创始人通过融合与整理,形成了新的标准——UML(统一建模语言)。目前,UML已经纳为国际标准,是软件系统建模的主要规范之一。
2. UML的组成
关于UML的组成,有很多人存在误解,误认为:“UML由一系列的UML图组成”,这种观点是错误的。UML由构造块、公共机制、规则三个部分组成,如图 “UML结构示意图” 所示。
(1)构造块
构造块犹如建房子时的砖瓦,包括事物构造块、关系和图。
事物构造块:包括结构构造块(类、接口、协作、用例、活动类、构件、节点等)、行为构造 块(交互、状态机)、分组构造块(包)、注释构造块。
关系:包括关联关系(包括表示整体-部分关系的聚合、组合关系)、依赖关系、泛化关系(表 示一般/特列关系)、实现关系。
图:在UML 2.x中包括14种不同的图,分为表示系统静态结构的静态模型(包括对象图、类图、构件图、部署图、复合结构图、包图、制品图);以及表示系统动态结构的动态模型(包括用例图、顺序图、协作图、状态图、活动图、定时图、交互概观图)。
(2)规则
规则是支配基本构造块如何放在一起的规则。这些规则可以用于:
命名:也就是为事物、关系和图起名字;从语义的有效性而言,只要求是由字符、数字、下划 线组成的唯一串;另外也要求是唯一的。
范围:使名字具有特定含义的语境。
可见性:用来表示编程语言中的public、private、protected等修饰符。
完整性:事物如何正确、一致地相互联系。
执行:运行或模拟动态模型的含义是什么。
UML对系统体系结构的定义是系统的组织结构,包括系统分解的组成部分、它们的关联性、交互、机制和指导原则,这些提供系统设计的信息。而具体来说,就是指5个系统视图,分别是逻辑视 图、进程视图、实现视图、部署视图和用例视图。
逻辑视图:以问题域的语汇组成的类和对象集合。
进程视图:可执行线程和进程作为活动类的建模,它是逻辑视图的一次执行实例,描绘了所设 计的并发与同步结构。
实现视图:对组成基于系统的物理代码的文件和构件进行建模。
部署视图:把构件部署到一组物理的、可计算的节点上,表示软件到硬件的映射及分布结构。
用例视图:最基本的需求分析模型。
(3)公共机制
公共机制是指达到特定目标的公共UML方法,主要包括规格说明、修饰、公共分类和扩展机制四种。
规格说明:规格说明是元素语义的文本描述,它是模型真正的“肉”。
修饰:UML为每一个模型元素设置了一个简单的记号,还可以通过修饰来表达更多的信息。公共分类:包括类元与实体(类元表示概念,而实体表示具体的实体)、接口和实现(接口用
来定义契约,而实现就是具体的内容)两组公共分类。
扩展机制:包括约束(添加新规则来扩展元素的语义)、构造型(用于定义新的UML建模元 素)、标记值(添加新的特殊信息来扩展模型元素的规格说明)。
3. 图
二、用例图
用例图在所有UML图中,重要程度是最高的。几乎在每次考试中都会考用例图,这里对用例图进行详细论述。
在了解用例图的基本情况前,我们有必要先理解什么是用例。Ivar Jacobson是这样描述的:“用例实例是在系统中执行的一系列动作,这些动作将生成特定参与者可见的价值结果。一个用例定义一组用例实例。”
首先,从定义中得知用例是由一组用例实例组成的,用例实例也就是常说的“使用场景”,就是用户使用系统的一个实际的、特定的场景。其次,我们可以知道,用例应该给参与者带来可见的 价值,这一点很关键。最后,我们得知,用例是在系统中的。通俗一点来理解,用例可以看成系统的功能,例如在结构化方法中,有登录这个功能,在使用UML建模时,“登录”将被构造为一个用例。
1. 用例图的概念
用例图(也可称用例建模)描述的是外部执行者(Actor)所理解的系统功能。用例图用于需求分析阶段,它的建立是系统开发者和用户反复讨论的结果,表明了开发者和用户对需求规格达成的共识。
在UML中,用例表示为一个椭圆。图 “用例图示例” 显示了一个图书管理系统的用例图。其中,“新增书籍信息”、“查询书籍信息”、“修改书籍信息”、“登记外借情况”、“查询外借情况”、“统计金额与册数”等都是用例的实例。
用例分析技术为软件需求规格化提供了一个基本的元素,而且该元素是可验证、可度量的。用 例可以作为项目计划、进度控制、测试等环节的基础。而且用例还可以使开发团队与客户之间的交 流更加顺畅。
2. 用例图的建立
用例图的建立通常要经历三个阶段:识别参与者、合并需求获得用例、细化用例描述。
(1)识别参与者
参与者(Actor)是同系统交互的所有事物,它是指代表某一种特定功能的角色,因此参与者都 是虚拟的概念。在UML中,用一个小人表示参与者。该角色不仅可以由人承担,还可以是其他系统、硬件设备、甚至是时钟。
其他系统:当你的系统需要与其他系统交互时,如在开发ATM柜员机系统时,银行后台系统就是一个参与者。
硬件设备:如果你的系统需要与硬件设备交互时,如在开发IC卡门禁系统时,IC卡读写器就是 一个参与者。
时钟:当你的系统需要定时触发时,时钟就是一个参与者,如在开发Foxmail这样的电子邮件应 用软件中的“定时自动接收”功能时,就需要引入时钟作为参与者。
要注意的是,参与者一定在系统之外,不是系统的一部分。通常可以通过以下问题来整理思路:谁使用这个系统?谁安装这个系统?谁启动这个系统?谁维护这个系统?谁关闭这个系统?哪 些其他的系统使用这个系统?谁从这个系统获取信息?谁为这个系统提供信息?是否有事情自动在 预计的时间发生?
(2)合并需求获得用例
用例是对系统行为的动态描述,它可以促进设计人员、开发人员与用户的沟通,理解正确的需 求,还可以划分系统与外部实体的界限,是系统设计的起点。在识别出参与者之后,你可以使用下 列问题帮助你识别用例:每个参与者的任务是什么?有参与者将要创建、存储、修改、删除或读取 系统中的信息吗?什么用例会创建、存储、修改、删除或读取这个信息吗?参与者需要通知系统外
部的突然变化吗?需要通知参与者系统中正在发生的事情吗?什么用例将支持和维护系统?所有的 功能需求都对应到用例中了吗?系统需要何种输入输出?输入从何处来?输出到何处?当前运行系 统的主要问题是什么?
通常情况下,我们将在参与者都找到之后通过合并需求来获得用例。也就是仔细地检查参与 者,为每一个参与者确定用例。而其中的依据主要可以来源于已经获取得到的“特征表”。
将特征分配给相应的参与者:首先,要将这些捕获到的特征,分配给与其相关的参与者,以便 可以针对每一个参与者进行工作,而无遗漏。
进行合并操作:在合并之前,我们首先还要明确为什么要合并,知道了合并的目的,也就会让 我们选择正确的合并操作。一个用例就是一个对参与者来说可见的价值结果,因此合并的根据就是 使得其能够组合成为一个可见的价值结果。合并后,将产生用例,而用例的命名应该注意采用“动词(短语)+名词(短语)”的形式,而且最好能够对其进行编号,这也是实现跟踪管理的重要技巧,通过编号可以将用户的需求落实到特定的用例中去。
绘制成用例图:最后我们就将识别到的参与者,以及合并生成的用例通过用例图(的形式整理 出来。在此,经常需要使用包含和扩展关系来描述用例之间的关系,该内容在后面会详细叙述。
(3)细化用例描述
经历了前两步,用例图已经完成,大家或许认为用例建模已“大功告成”,但事实并非如此。仅有用例图是不够的,因为这个图只能勾勒出一个大致的系统功能轮阔,系统很多细节信息都没有 明确地表示出来。所以对于每个用例而言,我们还需要编写它的事件流。
就像类对应于对象一样,一个用例的实例就是一个使用场景,用例就是对使用场景进行抽象的 总结,形成一组事件流。整个事件流的描述主线如图 “事件流模型” 所示。
- 前置条件:指在用例启动时,参与者(Actor)与系统应置于什么状态,这个状态应该是系统能够检测到的、可观测的;
- 后置条件:用例结束时,系统应置于什么状态,这个状态也应该是系统能够检测得到的、可观 测的;
- 基本事件流:基本事件流是对用例中常规、预期路径的描述,也被称为Happy day场景,这时大部分时间所遇到的场景;它将体现系统的核心价值;
- 扩展事件流:主要是对一些异常情况、选择分支进行描述。
进行细化用例描述时,通常有两个步骤,首先将事件流的基本框架完成(类似于写书时,先写 目录)。然后再对每个阶段要完成的具体工作描述出来。以图13-2所述的图书管理系统为例,若要完成“新增书籍信息”用例的事件流,可以先完成基本框架,结果如下所示。
然后对这基本框架进行细化,结果如下所示。
3. 包含、扩展与泛化
用例图中常见的关系有:包含、扩展与泛化,其中包含与扩展是用例图中特有的关系,而泛化 关系不仅用于用例图,同时也适用于其它图,如类图。
用例之间的包含和扩展关系是分解和组织用例的有效工具,表面上看它们有许多相似之处,因 此很多初学者经常容易混淆。下面将详细介绍这些关系的特点以及区别。
(1)包含关系
当可以从两个或两个以上的用例中提取公共行为时,应该使用包含关系来表示它们。其中这个 提取出来的公共用例称为抽象用例,而把原始用例称为基本用例或基础用例。例如,图 “包含关系的例子” 中的“登记外借信息”和“查询外借信息”两个用例都需要登录,为此,可以定义一个抽象用例“用户登录”。用例“登记外借信息”和“查询外借信息”与用例“用户登录”之间的关系就是包含关系,如图13-4所示。其中“<<include>>”是包含关系的构造型,箭头指向抽象用例。
当多个用例需要使用同一段事件流时,抽象成为公共用例,可以避免在多个用例中重复地描述 这段事件流,也可以防止这段事件流在不同用例中的描述出现不一致。当需要修改这段公共的需求时,也只要修改一个用例,避免同时修改多个用例而产生的不一致性和重复性工作。另外,当某个用例的事件流过于复杂时,为了简化用例的描述,也可以将某一段事件流抽象成为一个被包含的用例。
(2)扩展关系
如果一个用例明显地混合了两种或两种以上的不同场景,即根据情况可能发生多种分支,则可以将这个用例分为一个基本用例和一个或多个扩展用例,这样使描述可能更加清晰。例如,图 “用例图示例” 中的图书管理员进行“查询书箱信息”操作时,如果发现书箱信息有误,他可以使用“修改书箱信息”用例来完成错误的修改。所以用例“查询书籍信息”和“修改书籍信息”之间的关系就是扩展关系,如图 “扩展关系的例子” 所示。其中“<<extend>>”是扩展关系的构造型,箭头指向基本用例。
(3)泛化关系
当多个用例共同拥有一种类似的结构和行为的时候,可以将它们的共性抽象成为父用例,其他的用例作为泛化关系中的子用例。在用例的泛化关系中,子用例是父用例的一种特殊形式,子用例继承了父用例所有的结构、行为和关系。例如,图书管理系统中,用户注册时有多种方式,可以是“现场注册”,也可以是“网上注册”。所以“用户注册”用例就是“现场注册”用例和“网上注册”用例的泛化,如图 “泛化关系的例子” 所示。其中三角箭头指向父用例。
从UML事物关系的本质上来看,包含关系和扩展关系都属于依赖关系。对包含关系而言,抽象 用例中的事件流是一定插入到基本用例中去的,并且插入点只有一个。扩展用例的事件流往往可以 抽象为基本用例的备选事件流,在扩展关系中,可以根据一定的条件来决定是否将扩展用例的事件 流插入到基本用例的事件流中,并且插入点可以有多个。在实际应用中,很少使用泛化关系,子用 例的特殊行为都可以作为父用例中的备选事件流而存在。
在实际工作中,要谨慎选用这些关系。从上面的介绍可以看出,包含、扩展和泛化关系都会增加用例的个数,从而增加用例模型的复杂度。另外,一般都是在用例模型完成之后才对它进行调整,在用例模型建立之初不必急于抽象用例之间的关系。