简介:面对UML系统分析与设计课程的复习与备考需求,这份试题资料围绕统一建模语言在系统分析与设计中的核心应用展开,适合高校学生、软件开发初学者及需要梳理建模知识的读者进行自测与查漏补缺。内容以doc文档形式提供,共1个文件,压缩包约132KB,小巧轻便,便于在电脑或移动端快速打开查阅。文档按试题与答案解析组织,覆盖关联多重度、类之间的组合与聚集、客户与订单的数量关系、顺序图与协作图的差异、高内聚度、类图/用例图/序列图的职责、对象间可见性、领域模型及GRASP模式等高频考点,并结合POS销售、电话卡用例、SSD等场景帮助理解概念类识别与交互图的画法。已有406人学习下载,适合在考前集中刷题、对照答案复盘知识盲区,也可作为课堂练习与章节复习的辅助材料。
1. 一份 UML 复习试题的实际用法:先别急着背答案
前阵子陪某开发者过 UML 系统分析与设计,他项目经验不少,结果在这份复习试题的第 3 题上卡了十分钟——客户和订单的多重度,他把读取方向弄反了。这类题不难,难在题干一句话里塞了两个方向的问法。这套复习试题的价值就在这里:它把 UML 里那些「看着会、一写就错」的点,用选择题、填空题和画图题摆到台面上,从关联多重度、聚合与组合的分界,一路推到顺序图与协作图的取舍、SSD 的落笔顺序、GRASP 职责分配、UP 四阶段与 GoF 模式。适合三类人:临考需要快速收口的、要带新人补建模基础的技术负责人、以及面试前想把 UML 术语重新对齐一遍的开发者。下面按我带人过题库的实际顺序拆,先规则后图形,最后落到怎么用错题驱动复盘。
2. 类图关系判定:六种关系与多重度的读法顺序
2.1 先定强弱再定类型:一条不会绕晕的判定链
初学者画类图最容易犯的错,不是画错符号,而是根本没确定关系类型就开始连线。UML 里常见的关系有六种,语义强弱差别很大,判定顺序应该从强到弱走,反过来走就会把关联画成依赖、把组合画成普通关联。
| 关系 | 符号形态 | 语义 | 生命周期耦合 | 题干里的典型信号 |
|---|---|---|---|---|
| 泛化 | 实线 + 空心三角,指向父类 | is-a,子类可替换父类 | 无 | 「是一种」「属于……的一类」 |
| 实现 | 虚线 + 空心三角,指向接口 | 类承诺接口契约 | 无 | 「实现了……接口」「遵循……规范」 |
| 组合 | 实线 + 实心菱形,菱形在整体端 | 强整体-部分 | 强,同生共死 | 「由……构成」「包含……作为其组成部分」 |
| 聚合 | 实线 + 空心菱形,菱形在整体端 | 弱整体-部分 | 弱,部分可独立 | 「拥有」「聚集」「管理一组」 |
| 关联 | 实线,可带角色名与多重度 | 长期结构连接 | 无 | 「一个 X 对应多个 Y」「属于」 |
| 依赖 | 虚线 + 开放箭头,指向被依赖方 | 临时使用 | 无 | 「使用参数」「调用方法」「作为局部变量」 |
我一般的判定顺序是四步:先问「是不是 is-a」,是就落在泛化或实现;不是再问「是不是整体-部分」,是就落在聚合或组合;还不是,问「是不是长期存在的结构连接」,是就画关联并补多重度;剩下的、只出现在方法签名或局部变量里的临时使用,才画依赖。这个顺序的关键在于:依赖是最弱的关系,一旦先想依赖,后面就很难往回收。
有一个细节值得单独说:试题原文里出现过「实类」这个词,那是「实例」的笔误,不要被它带偏,题干问的始终是实例数量关系。
2.2 多重度标在哪一端:手指法比背定义管用
多重度是这份题库里出现频率最高、也最容易出错的知识点。定义本身不复杂:多重度描述的是一个类的一个实例能关联到另一个类的多少个实例。问题出在「从哪端读」。
规则只有一条:多重度标在对方那一端,描述的是本端一个实例对应的对方实例数量。
拿试题里的客户-订单场景验证。业务陈述是「一个客户提交 0 个或多个订单;一个订单由一个且仅由一个客户提交」,画出来是:
客户 1 ———————— 0..* 订单读法练习:把手指按在「客户」这个类上,眼睛看订单那一端的数字0..*,读作「一个客户对应 0 个或多个订单」。反过来,手指按在「订单」上,看客户那一端的数字1,读作「一个订单对应恰好 1 个客户」。
试题的两问正好考这个双向读法:对应每个订单实例,存在几个客户实例?手指按订单,看客户端是1,答案是 B。对应每个客户实例,存在几个订单实例?手指按客户,看订单端是0..*,答案是 D。很多人第二问答对、第一问答错,就是因为只记了「一对多」,没记方向。
多重度的常见写法整理成表更省事:
| 写法 | 含义 | 典型场景 |
|---|---|---|
1 | 恰好一个 | 订单必须属于一个客户 |
0..1 | 零或一个 | 员工可有可无的一张门禁卡 |
0..*或* | 零到多个 | 客户可能还没下过单 |
1..* | 至少一个 | 一个订单至少有一条订单行 |
2..4 | 区间 | 会议室容纳人数这类约束 |
我建议每画一条关联,都在旁边小声念一遍「一个 X 对应几个 Y」,念不顺就是画反了。这个习惯比背定义有用得多,也是我带人时唯一强制要求的动作。
2.3 聚合与组合的分界:一次生命周期测试就够了
聚合和组合在图形上只差菱形是空心还是实心,语义差得却很远。判断方法不用记定义,做一个测试:整体被销毁时,部分还能不能独立存在?
能独立存在,是聚合。比如团队与成员:团队解散了,成员还是成员,还能加入别的团队,部分可以被多个整体共享。不能独立存在,是组合。比如订单与订单行:订单删除,订单行没有独立意义,也不该被另一张订单引用。
试题第 2 题给的是「类 A 由类 B 的一个实例和类 C 的 1 个或多个实例构成」,标准答案落在组合。严格说,「构成」这个词只说明了结构,没说明生命周期,聚合也能自圆其说。考试场景下按组合答,因为「由……构成」强调的是强整体-部分。但真实项目里我会再追问一句:这个部分会不会被别处复用?会,就降级成聚合;不会,才用组合。这个区别在代码里体现得很直接:组合通常意味着整体负责创建和销毁部分,聚合通常只是持有一个外部传入的引用。
提示:组合端的菱形画在整体一侧,不是部分一侧。空心和实心画反,比关系类型选错更常见。
2.4 从题干到图形的落地步骤
拿到一道画图题,我按固定四步走,避免边想边画导致返工:
- 圈名词。把题干里所有名词和名词短语圈出来,这是候选类,先不管对错。
- 定关系。两两过一遍 2.1 的判定链,确定是泛化、实现、组合、聚合、关联还是依赖。
- 补多重度。每条关联两端都写,写不出来说明业务陈述里缺信息,标个问号回头补。
- 复查方向。用 2.2 的手指法逐条念一遍,重点查「1 对多」和「多对 1」有没有写反。
这四步里第三步最容易漏。很多人画完类图只标了一端的多重度,另一端空着,阅卷时直接扣分。规则很简单:关联线只要画了,两端就都得有数字,哪怕写*也比空着强。
3. 动态视图落笔:顺序图、协作图与 SSD 的三条分界线
3.1 顺序图与协作图:时间轴和结构轴怎么选
顺序图和协作图(新版 UML 里协作图改称通信图,试题沿用旧称)描述的是同一件事——对象之间按什么顺序、通过什么连接传递消息,差别在于布图的主轴。
顺序图的主轴是时间,纵向从上往下排,横向摆对象。它的核心元素是对象框、生命线、激活条和消息箭头。消息分三类:同步消息用实心箭头,发出后等返回;异步消息用开放箭头,发出即走不等;返回消息用虚线加开放箭头。消息密集时,顺序图的时间关系一眼就能看出来,代价是横向占位大,对象一多就得压缩。
协作图的主轴是空间,对象按结构关系摆放,链路上标消息,消息靠序号排先后。序号支持嵌套,比如1之后是1.1、1.2,再回到2,这样能表达调用层次。协作图加对象方便,不用重排时间轴;但消息一多,序号会变得难读,时间顺序得靠人工顺着编号捋。
选择依据可以归纳成三条:
| 场景 | 建议 | 理由 |
|---|---|---|
| 消息条数多、要强调先后与并发 | 顺序图 | 时间顺序直观,不用顺着编号找 |
| 对象多、链路关系复杂、分支少 | 协作图 | 空间组织清晰,增删对象成本低 |
| 需要交付给非技术人员看流程 | 顺序图 | 阅读门槛低,横向对照方便 |
两者可以互转。协作图转顺序图:按消息编号从小到大排列,遇嵌套编号就展开成调用层次。顺序图转协作图:给每条消息补层级编号,再按对象之间的连接关系重新布局。这个转换练习在复习时值得做两遍,做完对「时间」和「结构」两个维度的理解会明显不一样。
3.2 SSD 四步落笔:从系统事件到系统操作
SSD 是系统顺序图,用来描述外部参与者与系统之间的一次交互过程。它和普通顺序图最大的区别是:系统是一个黑盒,只有 Actor 和 :System 两个角色。试题第 51 题问「画 SSD 时应该如何对待所涉及的系统」,正确答案是不描述系统内部结构与功能,就是在考这个约束。
落笔顺序我固定成四步:
- 抽系统事件。从用例场景里找那些「由外部参与者发起、系统必须做出响应」的动作,比如收款员启动销售、输入商品标识、结束销售、顾客付款。
- 定系统操作。每个系统事件对应一个系统操作名,写成方法调用的形式,
makeNewSale()、enterItem()、endSale()、makePayment()。 - 定参数。把事件携带的数据写成参数,
enterItem(itemID, quantity)里的商品标识和数量就是这么来的。 - 排时序。按发生顺序从上到下画箭头,凡是需要返回值的画虚线返回箭头,比如
endSale()返回总金额。
销售场景落成表格更清楚:
| 系统事件 | 系统操作 | 参数 | 是否返回 |
|---|---|---|---|
| 收款员启动一次销售 | makeNewSale() | 无 | 否 |
| 收款员输入商品标识与数量 | enterItem(itemID, quantity) | 商品标识、数量 | 否 |
| 销售结束,系统计算并显示总额 | endSale() | 无 | 返回总金额 |
| 顾客付款,系统处理支付 | makePayment(amount) | 支付金额 | 视需要 |
这里有个高频翻车点:SSD 里出现ProductDAO、SaleService、OrderRepository这类内部对象。只要图里冒出它们,就说明画的是设计阶段的顺序图,不是 SSD。SSD 的边界就一条:Actor 在左边,:System 在右边,中间只有消息箭头,没有内部结构。
至于测试用例怎么出,官方标准做法是把 SSD 上的每个系统事件写成一条独立用例,覆盖正常流和异常流(比如输入了不存在的商品标识)。这一点在复习时可以直接套用,比空想用例省事。
3.3 操作契约:把黑盒行为写成后置条件
SSD 画完之后还有一步,很多人会跳过——写操作契约。操作契约描述的是「一个系统操作执行完成后,领域模型里的对象状态发生了什么变化」,它站在黑盒视角,不关心内部怎么实现。
四段式结构:
| 段落 | 写什么 | 注意点 |
|---|---|---|
| 操作名 | 与 SSD 上的系统操作一致 | 参数签名要对得上 |
| 交叉引用 | 指向对应的用例 | 方便追溯需求来源 |
| 前置条件 | 执行前必须成立的条件 | 能省则省,不要写成业务规则大全 |
| 后置条件 | 执行后领域对象状态的变化 | 只写可观察的结果 |
后置条件只有三类合法写法:实例的创建与删除、属性的修改、关联的形成与解除。以enterItem(itemID, quantity)为例,后置条件写成「创建了一个 SalesLineItem 实例;该实例与当前 Sale 建立关联;其 quantity 属性被赋值为 quantity;该实例关联到 itemID 对应的 ProductDescription;Sale 的总额被重新计算」。这一串全是领域对象的状态变化,没有一句涉及数据库、缓存或者日志。
我见过不少人把后置条件写成「向数据库插入一条订单行记录」,这已经越过了领域层,落到了实现细节。判断标准很直接:后置条件里出现表名、SQL、缓存键、消息队列,就是写错了层次。这个习惯在写代码阶段很有用——照着后置条件写单元测试,断言几乎可以直接翻译过去。
4. 领域建模与职责分配:概念类识别加 GRASP 五原则
4.1 概念类的两个识别入口:类别表与名词短语
领域模型是一组表示真实世界概念类的模型,试题第 15 题和第 44 题反复考这个定义,重点在于它不是软件部件的模型。这个概念区分不清,后面画出来的东西就会变成数据库表结构或者类的实现草案。
识别概念类有两个入口,题库里都提到了:类别表法和名词短语法。
类别表法是从业务领域的常见类别清单往下扫,看看当前系统里有没有对应物。常见类别包括:人(客户、员工)、组织(部门、供应商)、地点(门店、库位)、事件与交易(销售、付款、退货)、物品与资源(商品、库存)、规则与策略(折扣规则、计价规则)、单据与记录(订单、发票)、外部系统。这个方法的优点是防止漏,缺点是容易多,看到什么都想建一个类。
名词短语法是从用例文本里把名词和名词短语圈出来,再逐个筛。筛选规则有四条:去掉系统本身(「系统记录商品」里的系统不进模型)、去掉属性(价格、数量、颜色这些是属性不是类)、去掉同义词(客户和顾客留一个)、去掉不在建模范围内的外部实体。这个方法的优点是贴合业务,缺点是要有可读的用例文本,文本质量差就抽不出东西。
两个方法通常合用:先用类别表铺底,再用名词短语校一遍,多出来的删、少了的补。以试题第 20 题的 POS 场景为例,圈出来的候选有顾客、POS 收款台、收款员、销售、商品标识、商品、商品说明。其中「商品标识」是商品的属性而不是独立的类,可以并掉;收款台如果描述的是硬件,一般不进领域模型,除非业务上需要跟踪终端。剩下顾客、收款员、销售、商品、商品说明五个,基本就是这一场景的核心概念类。
领域模型的构建顺序也有讲究:先列候选类,再画类之间的关联,然后加多重度,最后加属性。属性一定放最后,因为很多被误当成类的名词,其实加上属性之后就发现它只是别人的一个字段。
4.2 GRASP 五原则各自解决什么问题
GRASP 是一组职责分配原则,它回答的问题不是「代码怎么写」,而是「这个职责该放到哪个类上」。这套题库里完整列出了五个:信息专家、创建者、低耦合、高内聚、控制器。完整版 GRASP 一共九个,另外四个是多态、纯虚构、间接性和防止变异,复习时知道有这四个就行,重点还是前五个。
| 模式 | 它要回答的问题 | 落到类图或代码里的形态 |
|---|---|---|
| 信息专家 | 职责该分给谁 | 给拥有完成该职责所需信息的那个类 |
| 创建者 | 谁负责创建某个类的新实例 | 聚合、包含或密切记录对方的类 |
| 低耦合 | 怎么降低依赖、提升复用 | 少建关联、面向接口、不跨层引用 |
| 高内聚 | 怎么把复杂度控制在可管理范围 | 职责聚焦,不相关职责拆出去 |
| 控制器 | 谁来接收系统事件 | UI 之后加一个用例控制器或会话控制器 |
信息专家的判定最直接:要计算订单总额,谁有订单行的数据?订单。那就把计算总额的职责给订单,而不是给一个外部的OrderCalculator。创建者的判定也不绕:B 聚合了 A、B 包含了 A、B 记录了 A,或者 B 拥有创建 A 所需的初始化数据,那 A 就交给 B 创建。低耦合和高内聚是一对张力,前者管类之间,后者管类内部。试题里对高内聚的定义写得很准:高内聚是对一个类中各个职责之间相关程度和集中程度的度量,它包含两层意思——不要给一个类分派太多职责,不相关的职责不要塞进同一个类。
控制器这一条在实际项目里最容易被忽略。系统事件从 UI 进来,如果直接落到领域对象上,界面和领域就绑死了。中间加一个控制器,负责接收和转发,领域层才能独立测试。控制器的粒度有两种常见做法:每个用例一个控制器,或者每个系统一个会话控制器。前者职责更清晰,后者类数量更少,我一般按用例走,因为后期改需求时影响面小。
需要单独强调一点:GRASP 和 GoF 是两个层次的东西。试题第 52 题问哪个不属于 GRASP,答案是 GoF,考的就是这个区分。GRASP 管「职责往哪放」,GoF 管「结构怎么复用」。单例、策略、组合、观察者、适配器、工厂方法、抽象工厂这些属于后者,答题时混在一起写,阅卷基本不给分。
4.3 从领域模型到类图:补齐属性、操作与可见性
领域模型和类图长得像,但不是一回事。领域模型只有概念类、关联、多重度和属性,没有操作;类图是设计产物,要补上操作、可见性、类型、导航性,还要画接口和实现关系。
从领域模型推到类图,我按这个顺序补:
- 补操作。每个系统操作最终要落到某个类的头上,比如
enterItem落到销售类,makePayment落到支付类。这一步其实就是把 SSD 上的操作分配下去。 - 补可见性。
+公开、-私有、#保护、~包内可见。属性默认私有,操作按调用方决定。 - 补类型。属性标上类型,关联标上导航性(单向还是双向),能单向就别双向,双向关联在代码里就是两个互相持有的引用,维护成本翻倍。
- 补接口与实现。凡是需要替换实现的策略、外部适配,抽成接口,用虚线空心三角连上去。
对象间的可见性这个概念在试题第 14 题里考过,它指的是一个对象能够看到或者引用另一个对象的能力。这个词听着抽象,落到代码里就是「A 的方法里能不能拿到 B 的引用」。可见性有四种常见来源:属性引用、方法参数传入、局部变量创建、全局查找。前两种是推荐做法,全局查找会带来隐藏耦合,能不写就不写。
顺序图转代码的过程也值得练一遍。题库里提到「根据系统操作规约画出顺序图和协作图,再配合其他信息编写类代码」,这条路径实际是这样的:SSD 定系统操作,操作契约定后置条件,顺序图定消息顺序和接收者,最后照着顺序图写方法体。每一步的输出都是下一步的输入,跳步就会导致方法写出来没地方放或者放错类。
5. 排查清单:这套试题里五个高频翻车点
5.1 五条踩坑记录
第一条:多重度方向读反。现象是一对多关系里,第二问答「每个客户对应 1 个订单」。原因是记了「一对多」这个结论,没记方向。解决办法是把结论改写成句子:「一个客户对应 0..* 个订单」,读的时候手指按在本端、眼睛看对方那端的数字。我要求带的人每画一条关联都把这句话写在线下面,写完再看对不对。
第二条:聚合和组合画反。现象是购物车和商品之间画了实心菱形。原因是看到「包含」两个字就画组合。解决办法是做生命周期测试:购物车清空、下单完成,商品还在不在?在,那就是聚合或者干脆是普通关联。再补一个共享测试:同一个部分会不会被多个整体引用?会,就不能用组合,因为组合的部分通常只有唯一归属。
第三条:SSD 里画出系统内部对象。现象是图里出现 DAO、Service、Repository。原因是在画顺序图而不是 SSD,把设计阶段的结构提前塞进来了。解决办法是加一条硬约束:SSD 只允许两个角色,左边 Actor,右边:System,任何内部对象出现都算错。画完先数对象框数量,超过两个就重画。
第四条:用例图里把内部功能当用例。现象是「校验密码」「写操作日志」「更新缓存」被画成用例。原因是没有从 Actor 视角出发。解决办法是对每个候选用例追问两句:哪个 Actor 会主动发起它?执行完成后 Actor 能不能观察到结果?两句都答不上来,它就不是用例,是系统内部步骤。用例不描述系统内部怎么工作,只定义系统必须做什么,这条在试题里反复出现。
第五条:GRASP 和 GoF 混答。现象是题目问 GRASP 解决了什么问题,答案里写了单例和工厂方法。原因是两个词都叫「模式」,记忆时没分层。解决办法是给两类模式各贴一个标签:GRASP 是「职责分配」,GoF 是「结构复用」。答题时先看题干问的是「谁负责」还是「怎么组织」,前者往 GRASP 靠,后者往 GoF 靠,两秒就能分开。
5.2 交卷前的三分钟自检顺序
这几条坑有一个共同点:都不是不会,而是没检查。我现在固定的自检顺序是四步,三分钟能过完:
- 所有关联线两端是否都标了多重度,有没有写反。
- 菱形是空心还是实心,实心菱形的部分有没有被别处引用。
- 交互图里对象数量对不对,SSD 是不是只有 Actor 和 System。
- 模式类题目,答的是职责分配还是结构复用。
这四步里第一步收益最高,因为多重度题在卷面上占比大,而且错一个方向整题就废了。
6. 把错题变成检查表:考前两周的复盘节奏
刷题库最大的浪费是「刷完就扔」。这套试题的价值不在于把答案背下来,而在于它暴露的规则盲区。我的做法是给每道错题打一个知识点标签,标签只设五类:多重度、关系类型、交互图、GRASP、UP 与需求。标签数量少,统计起来才有意义。
复盘节奏分三遍走:
| 轮次 | 做什么 | 判据 |
|---|---|---|
| 第一遍 | 按章节顺序刷,错题只打标签不抄答案 | 同一标签错 2 次以上,进第二轮 |
| 第二遍 | 只看错题,合上答案,把「为什么」重写一遍 | 写不出判定规则,说明还在背答案 |
| 第三遍 | 默画五张图:类图、顺序图、协作图、SSD、用例图 | 每张图能独立从题干推出来 |
第三遍最值得花时间。默画类图时,强制自己把六种关系各画一条,泛化、实现、组合、聚合、关联、依赖,画不出来说明某条关系的语义还没吃透。默画顺序图和协作图时,同一个场景画两遍再互转,转换过程里卡住的地方就是理解漏洞。
UP 那部分建议用两个锚点收口。一个是四阶段:初始阶段定范围和粗略成本,细化阶段解决高风险问题并定义大多数需求,构造阶段实现剩余部分,提交阶段做测试和部署。试题第 41 题问「定义大多数的需求和范围在哪个阶段完成」,答案是细化阶段,第 42 题问「哪项不在初始阶段完成」,答案是定义大多数需求,两题正好一正一反。另一个锚点是 FURPS:功能、可用性、可靠性、性能、可维护性,五个词按顺序记,考试时按这个顺序往下写,不会漏项。
至于设计模式,我记的是三元组:问题、解决方案、接口形态。单例的问题是怎么保证唯一实例,解法是私有构造加静态实例加静态访问方法;策略的问题是算法要能替换,解法是公共接口加多个实现加上下文持有接口引用;组合的问题是希望整体和部分一样对待,解法是叶子和容器实现同一接口;观察者的问题是状态变更要联动,解法是主题维护观察者列表并在变更时通知;适配器的问题是接口不兼容,解法是包一层做转换。按这个结构记,简答题就算忘了原文措辞,也能把骨架写全。
从那以后我每次画类图,多重度都强制念一遍「一个 X 对应几个 Y」,念不顺就说明画反了;每次画交互图,先数对象框,超过两个就停下来问自己画的是不是 SSD。这两个动作花不了半分钟,但能挡掉大部分本不该丢的分。希望帮到你。
本文还有配套的精品资源,点击获取