在软考系统分析师(以下简称系分)的上午综合知识考试中,面向对象分析与设计是历年的固定命题板块,而用例图(Use Case Diagram)则是这块版图中出现频率最高、同时也是考生最容易失分的知识点。不少考生把用例图简单理解为"画几个小人连几条线",结果一遇到"包含关系与扩展关系如何区分""时钟能不能作为参与者"这类题目就束手无策。本文将从用例图的正式定义出发,深挖其底层建模逻辑,逐层拆解参与者识别与包含、扩展、泛化三大关系的判定标准,并结合历年真题的命题思路,把这一高频考点彻底讲透。
用例图是统一建模语言(Unified Modeling Language,UML)中用于刻画系统功能需求的一种行为图。它由用例图的提出者伊瓦尔·雅各布森(Ivar Jacobson)在对象工厂方法中首创,后被统一建模语言正式吸收为十三种标准图之一。按照官方定义,用例图从系统外部使用者(即参与者)的角度出发,描述参与者与系统之间的交互行为,以及系统能够为其提供的一系列可观察的功能(即用例)。它的核心价值不在于描述系统内部如何实现这些功能,而在于回答一个最根本的问题:这个系统到底为谁、提供哪些有价值的功能。
要准确理解用例图,必须首先厘清它的四个基本构成元素。第一个元素是参与者(Actor),它代表与系统进行交互的外部角色。参与者可以是一个人,可以是一个外部硬件设备,也可以是另一个系统,甚至可以是一段定时触发的时钟信号。这里要特别强调"角色"二字:参与者描述的是某类交互者所扮演的角色,而不是某一个具体的人。同一个物理意义上的人,如果在系统中同时扮演了顾客和系统管理员两个角色,那么在用例图中就应当被建模为两个不同的参与者。
第二个元素是用例(Use Case),它描述系统对外提供的一项完整、可观察、有价值的功能。用例的命名通常采用动宾结构的短语,例如"下订单""查询余额""登录系统"。一个用例并不是某一步操作,而是一组有序交互场景的集合——同一个用例可能包含一个正常的主流程场景和若干备选流程场景。第三个元素是系统边界(System Boundary),它用一个矩形框把用例圈定在系统内部,把参与者置于系统外部,从而清晰划分系统的职责范围。第四个元素是关系(Relationship),用于表达参与者与用例之间、用例与用例之间的关联,包括关联关系、包含关系、扩展关系和泛化关系四类。
在统一建模语言十三种图的分类体系中,用例图与活动图、状态图、序列图、协作图等共同属于行为图,但它与后者的本质区别在于抽象层次:用例图站在需求层描述"系统能做什么",而不关心"系统怎么做"。在统一过程(Rational Unified Process)以及各类面向对象的开发方法中,用例图往往作为需求分析阶段的第一张图出现,是后续类图、序列图、活动图等设计模型的重要输入。正因为它在整个建模链条中处于源头位置,用例图画得准不准,直接决定了后续分析的走向。
在具体建模实践中,识别参与者需要遵循一套系统化的追问路径。第一步是列出所有可能从系统获益或向系统提供信息的实体,第二步是从中筛除位于系统边界之内的实现组件,第三步是把功能角色相同或相近的实体归并为同一个参与者。这一步的难点在于对"角色"抽象度的把握:过于具体会把"张经理""李主管"分别建模成参与者,过于笼统又会把所有用户合并成一个"用户",掩盖了不同角色在权限与操作上的差异。恰当的做法是以"在系统中承担的职责"为标准来切分,职责相同即合并,职责不同则分立。
用例的命名同样有章可循。用例名应当采用"动词加名词"的动宾短语,且动词要体现参与者的意图而非系统的内部动作,名词要指向业务对象而非技术构件。例如"查询余额"优于"读取账户数据","提交订单"优于"写入订单表"。这一命名规范背后是需求表达的可理解性原则——用例的读者首先是业务人员与最终用户,其次才是开发者,因此用例语言必须停留在业务术语层面,不能滑向实现术语层面。
用例图之所以能成为需求捕获的标准工具,是因为它背后贯穿着一套严密的建模哲学。这套哲学可以用三个关键词概括:边界、价值、复用。边界回答了"系统包含什么、不包含什么"的问题,价值回答了"每个用例为什么要存在"的问题,复用回答了"如何在多个用例之间共享公共行为"的问题。下面分别从参与者、用例和关系三个维度,剖析这套哲学的具体落点。
用例图的第一个底层原理,是"由外向内"的需求观。传统的结构化方法习惯于从系统的功能模块出发自顶向下分解,而用例方法反其道而行之,它强制建模者先站在系统边界之外,去追问"谁会用这个系统"以及"使用者在系统上期望完成什么"。这种视角转换的价值在于,它把需求的出发点和验证标准都锚定在了使用者身上,从而避免开发团队陷入"闭门造车"式的功能堆砌。正因如此,参与者的本质属性是"外部的",它永远位于系统边界之外,系统边界之内的一切都不能作为参与者出现。
用例图的第二个底层原理,是"黑盒观"。用例描述功能时,只描述其对外可见的行为和结果,不描述内部的实现细节。这一约束带来了两条建模纪律:其一,用例必须具备完整性,即它应当对应一个从触发到结束、能够给参与者带来明确价值的完整流程,而不是流程中孤立的某一步;其二,用例必须具备可观察性,即这个流程的结果应当是参与者能够感知到的。把"输入账号""输入密码"分别单列成用例,就是把一个完整用例拆成了内部步骤,这恰恰违背了黑盒原则。理解了这两条纪律,就能把握住用例粒度划分的根本尺度。
用例图的第三个底层原理,是"复用与分离关注点"。当多个用例之间存在公共的行为片段时,如果每处都重复描述,模型就会迅速膨胀且难以维护;而如果硬性合并,又会牺牲流程的可读性。为此,统一建模语言引入包含关系(include)来抽取公共行为,引入扩展关系(extend)来分离可选的、条件性的行为,引入泛化关系(generalization)来抽象参与者和用例之间的共性。这三类关系的共同目标,是在保持模型清晰的前提下实现最大程度的复用,其思想根源与结构化设计中"模块化、高内聚、低耦合"一脉相承。
包含关系(include)用于表达一个用例必须包含另一个用例的行为。其语义是:当基用例(Base Use Case)执行到某一点时,被包含的用例(Inclusion Use Case)必然被完整执行,这是强制性的、不可省略的。在图形上,包含关系用一条从基用例指向被包含用例的虚线箭头表示,箭头旁标注构造型"include"。最典型的场景是"检查权限":无论是"课程学习"还是"课程考试",在执行之前都必须先完成权限校验,因此"检查权限"作为一个公共用例被两个基用例所包含。包含关系的本质是公共子流程的抽取,它把一个用例中被多个场景共用的步骤提炼成独立用例,供多个基用例复用,从而消除了模型的重复描述。
需要特别区分的是,包含关系与结构化编程中的"子程序调用"虽然形似,但语义层次不同。包含关系描述的是需求层面的行为复用,被包含的用例本身依然是完整的、可观察的功能片段;而子程序调用描述的是实现层面的代码复用。命题人常常利用这一差异设置干扰项,例如把"数据库访问"这类明显属于实现层的操作伪装成被包含用例,考生一旦混淆了需求与实现的层次,就会误判。同理,把"发送验证码"这种虽然完整但与业务价值关联薄弱的步骤单列为被包含用例,也值得警惕,判断标准仍应回到"是否给参与者带来可感知的价值"这条根本线上。
包含关系的价值不仅在于消除重复,还在于降低维护成本。当权限校验规则发生变化时,只需修改"检查权限"这一个用例,所有包含它的基用例便自动获得了更新,这正是复用带来的可维护性红利。而一旦建模者把权限校验分别写进了课程学习、课程考试等多个用例中,任何一次规则调整都意味着多处同步修改,遗漏任何一处都会引发模型不一致。因此,包含关系不仅是建模风格的偏好,更是工程上的一种必然选择。
扩展关系(extend)用于表达一个用例在特定条件下可以扩展出额外的行为。其语义与包含关系恰好相反:扩展用例(Extension Use Case)是否执行是不确定的,只有当扩展点(Extension Point)处满足特定条件时,扩展行为才会被触发;而基用例本身即使不执行扩展行为,也能独立完成自己的主流程。在图形上,扩展关系用一条从扩展用例
本篇完!