打开获取需求的大门——用例图绘制指南
1.前言
1.1.简介
使用UML绘制用例图是表现系统需求的一种方式,是分析获取需求的一种有效手段。用例图是了解系统的第一个关口,通过用例图可以知道系统有哪些角色,这角色通过系统能做什么事情。在用例图中,会体现与系统交互的参与者、功能模块,以及系统工作的基本流程等。站在客户的角度上看,用例图是他们业务领域的逻辑化表达方式;站在软件供应商的角度上看,用例图是系统蓝图和开发的依据,这说明用例图在软件制作的期间很好的起到了承上启下的作用。尽管它通常不会展现细节方面,但它是一个可以用于沟通复杂想法的好方式。
1.2.主要元素
1.3.元素符号
1.4.目的
本篇希望作为一篇面向实用性的指南,其目的是为了能够帮助初学者,快速掌握用例图的基础知识和绘制方法,以便于用于实践中,在实践中不断的感悟。另外的一个目的则是作为查阅手册,如果在实际的工作中遇到了比较模糊的地方,可以通过本篇文章快速查阅回顾知识点,以便解决实际的问题。接下来,本篇将根据用例图的主要元素逐一展开讲解,讲解每个元素时,会采用理论和示例并行的讲解方式,以便于在每个元素的使用场景上,能够加深对元素概念的理解。
2.系统边界
2.1.概述
在UML的用例图中,我们通常将系统边界视作我们构建的软件系统,它可以是网站、软件组件、业务流程、App等应用程序,亦或是你要开发的任何东西。系统边界作为用例图中的元素可以用一个矩形来表示,其名称位于矩形内的顶部。系统边界使用矩形元素,实际上仅仅是一个表现形式,它在有些应用场景中甚至是无形的。从概念上来说它属于一种分析方法,通过边界可以决定看待系统的抽象层次和视角,进而排除边界外大量的杂音来降低复杂程度,从而界定系统的范围,知道系统能干什么,系统不能干什么。
确定一个边界,就好比如你决定采用什么方式来介绍一个事物一样。人体的器官大约有315个,如果不设定边界,那么你介绍器官的时候就会毫无条理、天马行空。介绍内脏器官(心、肝)的时候一下就跳到介绍外部器官(耳、鼻),到最后听你介绍的人根本搞不清楚你讲的重点。因此,我们需要通过设定边界,将我们所讲的事物确定在同一个领域范围,并且事物的粒度要保持相同体量。还是回到人体器官上来,如果设定的边界是内脏器官,此时我们的视角必然在身体内部,抽象出来的事物都属于该边界内同粒度的器官(心、肝、肺等);如果设定的边界是外部器官,那么我们的视角必然在身体外部,抽象出来的事物都属于该边界内同粒度的器官(眼、耳、鼻、嘴等);
没有边界的地方将会是混沌的,例如你去一所学校去找人,如果学校没有设定边界,那么你找人就像是“大海捞针”,因为所有人都交织在一起。如果学校根据不同领域设定边界,那么学校内部就会规划为:教室、办公室、图书馆等等区域。那么此时你找人就不会在茫茫人海中寻找,而是可以针对性有范围的找,比如找学生可以去教室,找老师可以去办公室。一个全新的软件项目对我们来说,也是混沌的,需要我们设定边界,才能从混沌走向清晰。
总的来说,通过边界的划分会影响到我们观察的事物,从而就决定了抽象层次和视角,使得我们分析事物的粒度可以保持统一,并且让系统的作用范围更加清晰。边界和面向对象同样都属于软件设计领域中的内功,我们并能奢望一朝一夕就将其掌握,我们先做到一个初步的认识,然后在日后的实践中不断去感悟和学习。
2.2.示例背景
本文用于讲解用例图使用的应用场景,是来自日常通勤的共享单车。本文将使用共享单车的软件系统作为示例,以此来展开用例图的绘制,我会根据用例图中元素的使用特点,选择其中常用的功能(扫码用车、锁车、付款、退押金)作为素材。在绘制之前,希望大家脑补一下你使用共享单车通勤的场景,这有助于理解其中的业务需求,以便我们有针对性的绘制相应的用例图。由于这是根据现实生活中已经存在的应用,来反向推导出与之对应的用例图的情况,所以我们可以直接将共享单车的软件视为系统边界,并将其命名为“共享单车骑行系统”。
3.参与者
3.1.概述
参与者在UML用例图中是由一个“火柴人”来表示。UML官方对其的定义是:“参与者是在系统之外与系统交互的某人或某事物”。参与者不是系统的组成部分,所以它处于系统的外部。参与者通常是一个使用系统的人,但有时也可以是一个外部系统或外部因素、时间等外部事物。参与者对系统有着明确的目标/愿望,并且希望通过与系统交互所产生的结果,可以它的实现目标/愿望。由于参与者必须与系统进行交互,因此可以从这个角度去分析获取那些候选的参与者。
具体来说,识别参与者可以参考以下几个思路:
- 系统会被哪些部门使用。这些使用系统的部门用户,可以根据共同的职责提炼出一个参与者。
- 谁向系统提供信息、使用或删除信息。对系统信息进行管理的人员也会作为参与者与系统交互。
- 谁与系统的需求有关联。因关联被动参与到需求的事物,也可能会作为参与者使用到系统中的相关功能。
- 谁对系统进行维护。日常的维护业务也需要参与者与系统进行交互来完成。
- 与外部系统是否有交互。这些外部系统往往会成为参与者。
- 时间参与者,如一些具备定时功能的组件,它会激活哪些系统定期的、自动执行的业务。
在参与者当中还会分为主要和次要的参与者,主要参与者是主动发起了对系统的使用,通常绘制在边界的左侧;次要参与者与系统的交互是被动的,通常绘制在边界的右侧。例如对于某公司的工资管理系统,公司财务负责人会主动发起工资的发放操作,但实际的资金转移需要银行来介入完成,这其中财务负责人就是主要参与者,而银行由于工资的发放从而介入系统的交互,所以银行属于次要参与者。
参与者代表的是特定类型,而不是代表的具体的人或物,参与者相当于根据人或物共同职责提炼出的某类角色。所以在为参与者命名时需要注意,不要定义为某个具体的人或物(张三、发财银行),而是应当定义具有代表性的名称(客户、银行)。
3.2.示例
当我们去分析一个系统时,不应直接从系统本身具备的功能开始思考,我们应当先思考系统会为谁提供哪些价值,有哪些参与者会与这个系统进行交互来实现目标,因为系统的价值就体现在为参与者实现目标上。对于本示例的“共享单车骑行系统”而言,它可以提供单车的使用为人们带来交通的便利。作为使用者,他能够通过系统得到单车的使用。在这个分析中,很明显,骑行者是对该系统有着明确目标的人,并且他属于系统外部。所以可以将骑行者定义为“共享单车骑行系统”的参与者。
为了引出主要和次要参与者的使用场景,我们来思考下退押金这个业务。共享单车公司通过缴纳押金的手段保证单车在一定程度的安全,作为骑行者而言,在没有单车使用需求的情况下,可以要求公司退押金。由于“共享单车骑行系统”中没有包含公司的资金业务,所以该系统想要实现骑行者退押金的目标,必须依靠公司另外的财务系统。当骑行者发起退押金的操作时,“共享单车骑行系统”会向外部的财务系统发送申请,押金最终会由财务系统打到骑行者的银行卡上。
对于这个退押金业务场景而言,骑行者是主动发起对系统的使用,而财务系统与系统的交互是被动的。只有骑行者做了一些事之后,财务系统才会参与到系统中来采取相应行动。综上所述,该业务其中的主要参与者是骑行者,次要参与者是财务系统。下面将这两个参与者补充到本示例的用例图中。
4.用例
4.1.概述
在系统的角度上看,用例是它对外向参与者提供的价值。在参与者的角度上看,用例是参与者对系统寄予的目标。参与者通过系统内部提供的用例与之交互,从而根据交互产生的结果达到参与者的目标。分析用例的主要作用是捕捉功能性需求,一个系统的功能性需求就是由参与者对系统各种各样的目标构成的,当全部参与者的所有目标,都能够通过系统提供的用例来达到时,那么这个系统就被确定下来了。用例的提出和定义都是从参与者的角度来考虑的,通过分析参与者使用系统实现的目标来获取相应的用例。
具体来说,识别用例的思路可以参考以下几点:
- 参与者的日常工作处理的业务流程有哪些?可以从这些流程中概况出用例(流程处理什么事)。
- 参与者在业务中承担哪些职责、起到什么作用?业务中承担的职责或起到的作用可能存在用例。
- 参与者是否会生成、使用或删除与系统相关的信息?系统需要提供相应的用例给参与者进行管理和维护。
- 参与者是否需要把外部变更通知给系统?通常系统的过程也需要用例支持。
- 系统是否需要把内部事情通知给参与者?通知参与者的过程就是系统用例的行为。
- 是否存在进行系统维护的用例?相关的维护用例也会在系统中存在。
确定了用例后就需要定义用例的名称,通常用例的名称的结果应该是:<状语>动词+<形容词>宾语。其中状语和形容词可以根据情况修饰,而动宾则是必须的主体结构。名称的结构也可以作为衡量用例准确性的标准,通常参与者和用例名称在一起是“主谓宾”的结构,例如会员订购商品。
在分析用例是还需要注意粒度的问题,应当避免出现以下几种过分细化的情况:
- 不要把完成一个用例需要的步骤当成单个用例;
- 不要把实现用例的手段分解成多个用例;
- 不建议把常规维护性操作(增删改查)拆分成单个用例;
4.2.示例
在基本概述用例之后,接下来我们将针对本示例“共享单车骑行系统”的应用场景,分析并获取其中的用例。有效识别用例最基本的就是站在参与者的角度来考虑,你可以思考下你作为骑行者对一款“共享单车骑行系统”会有哪些期望或目标。
根据日常的使用场景并结合作为示例的素材考虑,骑行者比较普遍的目标一般包含:扫码用车、锁车、付款、退押金。另外需要强调的是,系统能够为参与者实现目标是系统的基本原则,参与者的目标也不能天马行空,参与者的目标应当是系统力所能及的,在系统边界内的。接下来,我们将以上提到的骑行者的目标作为用例,并将其补充到当前示例的用例图中。用例对应的符合是椭圆形,并且用例需要包含在系统边界的矩形之中。
5.关系
5.1.关联
在识别并绘制参与者和用例之后,在图中它们还处于相互独立的状态,实际上参与者和用例之间还存在着某种关系,称之为关联。这里的关联和类之间的关联有所不同,它表示参与者和用例之间存在信息交互,象征两者之间有基本的交流或者互动。关联采用一条“实线”表示,这条线可以在指向用例的方向带上箭头,也可以不带箭头。这个箭头并不代表数据流或业务流的方向,因为参与者和用例之间是双向交互信息的,所以这里的箭头表示由通信的主动方(参与者)启动被动方(用例)。为了避免箭头带来的逻辑混淆,建议不带箭头。
接下来,我们将针对“共享单车骑行系统”用例图中的参与者和用例,通过绘制实线建立关联关系。在绘制中请注意,在本文讲解参与者的段落中,关于退押金的业务场景,我们分析出了主要和次要的参与者。所以对于这个情况,次要的参与者(财务系统)也需要和参与的用例(退押金)建立关联关系。
请记住,所有的参与者,都必须至少与一个用例产生关联并与之交互,不存在没有参与者的用例,也不存在没有用例的参与者,否则请检查用例和参与者的分析结果。下面将本示例中参与者和用例之间的关联关系,将其补充到用例图中。
5.2.包含
简介:
包含关系提供了从多个用例中提取公共部分的能力,把公共部分作为单独的用例,系统可以通过包含关系对其进行引用。因此,包含关系的提出一般是基于用例行为复用的考虑,这意味着被包含的用例往往被多个基本用例引用。对其的使用标准总结为:如果在两个或更多的基本用例中存在类似的行为,并且这些类似的行为又可独立地构成一个用例,那么就可以把这些行为提炼出来构成一个被包含的用例。包含用例与衍生它的多个基本用例之间就属于包含关系,包含关系在用例图中的表示:使用一条带箭头的虚线,并在虚线附近加上<<include>>,箭头的方向由基本用例指向包含用例。
基用例和包含用例:
包含用例的执行并不是由参与者直接发起的,而是由基础用例执行时触发的,基本用例一旦执行,对应的包含用例就必须会执行。另外,基本用例想要完整的执行,就必须执行它所包含的用例。从这点可以看出,包含用例对基本用例而言相当于一种“必须品”,没有包含用例,基本用例是不完整的。而对于包含用例而言,如果没有基本用例,包含用例是不能单独存在的。两者的关系就像是“有你才有我”一样。
示例:
让我们回到“共享单车骑行系统”中来思考其中是否存在包含关系。基于包含关系是对行为复用的考量,我发现可以在付款和退押金这个两个用例中,找到公共的部分,并且属于必须执行的行为。 付款时,系统必须会检查你上一次的付款情况,避免过度透支使用;退押金时,系统也会检查你的付款情况,避免出现错误的免单情况;因此,这两个用例中就可以抽取公共行为“检查未付款”,而这个公共行为即可构成一个单独的被包含用例。下面将用例补充到当前示例的用例图中,并建立包含关系。
5.3.扩展
简介
扩展表示的意思是:在基本用例的基础上,思考基本用例在处理自身行为之外,还能做什么有易于业务需求的事情,如果存在,那么就把这个事情委托给其他用例(扩展用例),表示该事情被扩展了,以此在基本用例和扩展用例之间就形成了扩展关系。在扩展关系中,基本用例自身是独立完整的,不受扩展用例影响,不需要扩展用例也能够向参与者提供价值。但是扩展用例的存在将由基本用例决定。扩展关系在用例图中,是用一条带箭头的虚线并在线上加<<extend>>来表示的,箭头方向指向基本用例,表示扩展用例是根据基本用例进行扩展的。
扩展点
为了表面扩展用例在基本用例中是基于什么情况扩展的,需要在基本用例中定义扩展点,扩展用例只能在它与之对应的扩展点上进行扩展。扩展点是指在基本用例中定义的特定条件,每个扩展用例都至少与一个扩展点相关联。当基本用例满足扩展点的特定条件后,就会触发相应的扩展用例的执行,从而为基本用例提供附加行为。需要注意,有些工具可能在绘图上并不会将扩展点显示出来,但无论显示与否,只要存在扩展关系,基本用例中就存在与之对应的扩展点。
示例
我们将扩展关系的特点投射到本示例的“共享单车骑行系统”中来,思考分析下哪些用例可以进行扩展。我个人作为共享单车的常用者,我觉得每当骑行到达目的地离开单车后,总是会担心自己因为没有将单车锁上,导致无限扣费的情况。我觉得要是在锁车后要是能够向我发送一条确认信息,我将会安心许多。基于这个痛点,实际上我们可以在锁车的用例上进行扩展,扩展一个在锁车后向骑行者发送锁车消息的用例,并且发送锁车消息的扩展用例还不会影响到锁车,仅作为一个辅助性质。下面我们将这个扩展用例补充到示例的用例图中看看效果吧。
5.4.包含和扩展的对比
包含关系和扩展关系是在用例建模中比较常用的关系,也是初学者容易混淆的两种关系。从表面上来看,它们都是使用虚线箭头作为表达关系的符合,只是箭头方向和虚线上的标识不同而已。所以,如果我们想要清晰、正确的使用这两种关系,就必须通过对比分析,来理解这两种关系的使用场合和所解决的问题。下面通过表格对这两种关系进行了详细的对比,在实际应用中可以进行参考,并结合具体情况选择合适的关系。
5.5.用例的泛化
简介
泛化关系相当于面向对象三大特征之一的继承,该关系可以用于在用例之间或参与者之间。用例和参与者之间的泛化都表面了一种继承层次,通过继承层次,继承派生出的子项可以获得上层项中全部属性和行为,并参与上层项中的各种关系。因此,通过泛化关系可以在用例图中达到更大范围、高层次的需求复用。
在用例之间的泛化关系当中,父类称为泛化用例,子类被称为特化用例。泛化用例中描述通用行为,而在特化用例中继承这些通用行为,并在适当地方进行特化。在实际中,泛化用例往往作为抽象用例,这代表它不会产生任何具体的场景实例,仅作为抽象标准(类似接口),参与者只会通过执行特化用例来实现目标。
在实际中,包含关系在用例之间的关系中应用的最多,再者是继承关系,而泛化关系用的最少。因为对于客户而言,由于它们往往缺少面向对象的知识,所以难以理解其中的概念。如果仅仅为了将用例之间的可复用部分或用例的可扩展部分描述出来,那么使用包含关系和扩展关系就足够了,使用用例的泛化很可能增加用例图的理解成本。
示例
在本示例“共享单车骑行系统”中,我们可以发现付款用例实际上是很宽泛的,因为付款的方式有很多,可以是:账户余额、支付宝、微信、花呗等等方式。所以付款用例代表了一般情况,我们可以将其作为抽象用例,从它身上特化出具体的付款用例。泛化关系是用一条带空心箭头的直线表示的,箭头的方向由特化用例执行泛化用例。下面我们将这个用例的泛化补充到当前实例的用例图中。
5.6.参与者的泛化
在上文介绍用例泛化的基础上,在来介绍下参与者之间的泛化是如何运用的。与用例的泛化一样,参与者之间的泛化也是属于一种继承层次,同样是满足于更高层次的需求复用,只不过参与者的泛化在实际中使用的更多些。
我们针对本示例的“共享单车骑行系统”,思考这样一个情况:如果系统要在普通用户的基础上推出一个VIP用户,并且VIP用户具备普通用户所有的行为能力,那么在绘图时,VIP用户是不是要将普通用户的用例和关系都重复画一遍呢?如果是,那么后面如果有N个高层级用户加入,是不是都需要将普通用户所有的用例都要重复画一遍呢?显然这种重复性会让用例图的重复度和复杂度会大大提示,从而不堪入目。
对于上面的情况,就属于我们在参与者中使用泛化关系的场景。通过建立普通用户和VIP用户之间的泛化关系,VIP用户将基础普通用户的所有行为,所以在将VIP用户作为参与者绘制到图中时,就无需重复的绘制他和普通用户之间存在的共同用例和关系。
在实际的应用中,我们也应当对所有参与者进行共同性的分析,共同性体现为,多个参与者存在相同的用例。如果存在共同性,我们应当抽象出一个“父类”参与者,其参与者都直接或间接基础这个“父类”参与者。参与者之间的泛化关系也是使用带空心箭头的直线表示的,下面我们将VIP用户和它特有的用例补充到图中。
6.实践建议
- 在客户能准确全面的基础上,用例越精简越好。
- 用例应使用客户的语言,需保证客户能看懂,而不应参与技术性的描述。
- 在表达用例时要注意轻重之分,对于重点难点用例可通过注释加以详述,对于“常识”的用例则可简言。
- 用例的粒度应当在同一个系统边界中保持相同的体量。
- 不应盲目地从客户无边界的想法中直接获取用例,用例更多地是从客户对系统的目标中推导,且在系统边界之内。
- 用例图只是一种表现形式,掌握用例图所承载的需求分析方法才是关键。
7.结语
绘制用例图其实是件很容易的事情,只要你知道每个元素代表的符合,就能照葫芦画瓢的绘制出来。但是难点在于其中每个元素都蕴藏着很多复杂的概念,如识别参与者、分析系统边界、分析用例、定义关系等,这些往往是需要经过一定的系统学习和掌握其中的概念才能做好的事情。所以比起绘制方式而言,更重要的是分析能力和实践运用能力。
用例图的构建,对于获取需求而言,是非常有效的手段。而清晰明确的需求对于一个软件项目的重要性,毋庸置疑。所以学会用例图将会是你在获取需求道路上披荆斩棘的利器。