简介:本资源是面向软件工程专业学生及UML初学者的系统分析与设计复习备考资料,聚焦UML核心概念与典型考题解析,助力快速掌握类图、交互图、关系建模、高内聚低耦合等关键知识点。压缩包为单个132KB的Word文档(.doc),内容结构清晰,涵盖12道高频考点题——包括关联多重度判定、组合关系建模、客户-订单基数表达、顺序图与协作图对比、UML图分类(类图/用例图/序列图/协作图)、依赖/关联/泛化/实现四类关系辨析、领域模型定义、可见性含义等,并附标准答案与详细解析。所有题目均源自真实教学场景,解析紧扣UML规范,兼顾理论深度与应试逻辑,便于对照学习、查漏补缺与考前强化。目前已有405人学习下载,适合课程复习、期末冲刺及软考初级系统分析员备考使用。
1. 这不是题海战术:一份能真正帮你把UML从“画得像”变成“想得清”的复习试题包
你有没有过这种经历:翻着UML教材,类图、顺序图、用例图都背得滚瓜烂熟,一到画图题就卡壳——不是漏了多重度箭头,就是把“聚集”和“组合”画反;不是把控制器类塞进领域模型,就是把SSD图当成类图来写;更别提GRASP原则和设计模式混在一起时,连“谁该创建对象”这种基础问题都要犹豫三秒。这不是你记性差,而是大多数复习资料只给你答案,不告诉你为什么这个答案能成立、换一个场景它为什么立刻失效、考官到底在考你哪一层思维。这份《UML系统分析与设计复习试题》不是题库,而是一套带诊断逻辑的思维训练包:它用21道典型真题覆盖UP四阶段、9类UML图、5大GRASP原则、7种核心设计模式,每道题背后都藏着一个真实建模决策点——比如第3题客户/订单关系,考的不是“0..*”怎么写,而是如何从一句业务陈述里精准剥离出责任边界与生命周期依赖;第4题顺序图vs协作图,表面比画法,实则考你在系统尚未编码前,如何用不同视角预判并发瓶颈与模块耦合风险。它适合两类人:一类是正在啃《UML精粹》却总在“建模意图”上迷路的初学者;另一类是做过两个项目、但每次画完图都被导师问“这个关联为什么不能用依赖代替”的进阶者。如果你需要的不是标准答案,而是一套能让你在考场或评审会上,一眼看穿模型背后逻辑链的肌肉记忆,这份资料就是为你拆解出来的。
2. 从题干到模型:UML静态视图的三层建模逻辑(类图、对象图、包图)
UML静态视图不是贴标签游戏,而是对现实世界进行责任切分、边界划定、依赖显化的三步手术。这份试题里所有类图题(如第2、7、9、38题)都在逼你回答三个问题:谁拥有数据?谁控制行为?谁承担变化?下面以第2题“类A由类B的一个实例和类C的1个或多个实例构成”为例,拆解真实建模过程。
2.1 第一层:识别语义本质——组合 vs 聚集 vs 关联
题干中“由……构成”是强语义信号,但必须穿透字面。我们先做语义排除:
- 如果类A销毁时,类B/C实例也必须销毁 →组合(Composition)
- 如果类A销毁后,类B/C实例仍可独立存在 →聚集(Aggregation)
- 如果只是临时引用或服务调用 →关联(Association)
本题关键在“构成”二字:类A作为整体,类B/C作为部分,且题干强调“一个B”和“一个或多个C”,暗示B/C的存在完全依附于A的生命周期。这已超出聚集的松散所有权,直指组合。所以正确类图中,A与B之间应为实心菱形(组合),A与C之间同样为实心菱形,且B端标注1,C端标注1..*。
+----------------+ +----------------+ +----------------+ | 类A | | 类B | | 类C | | | | | | | | | | | | | +--------+-------+ +--------+-------+ +--------+-------+ | | | | 1 | 1 | 1..* | | | +-----------+--------------+--------------------------+ | | +-----+------+ | | 组合关系 | | +------------+ | | +-----+------+ | 组合关系 | +------------+提示:考试中若题干未明确生命周期,优先按组合处理——这是UML建模的保守原则。因为组合可降级为聚集(删掉实心菱形),但聚集升级为组合需重构代码。
2.2 第二层:多重度标注的工程意义——不是数学,是契约
第1题和第3题反复考多重度,但陷阱在于:很多人把0..*当作文法符号,却忘了它是运行时契约。以第3题客户/订单为例:
- “一个客户提交0个或多个订单” → 客户端多重度为
0..* - “一个订单由一个且仅由一个客户提交” → 订单端多重度为
1
但注意:1不是“必须有”,而是“强制唯一”。这意味着在数据库设计中,订单表必须有customer_id非空外键;在代码中,Order类构造函数必须接收Customer实例。如果考试要求画类图,1端必须加实线连接,且不能省略数字(写成*或留空都是错)。
# 多重度对应的代码契约示例(Python) class Order: def __init__(self, customer: Customer, items: List[Item]): # customer: Customer 是强制参数,不可None self.customer = customer # 违反"1"契约:若允许customer=None,则破坏业务规则 self.items = items class Customer: def __init__(self, name: str): self.name = name self.orders: List[Order] = [] # orders可为空列表,符合"0..*"契约参数说明:
customer: Customer类型注解中的Customer表示强类型约束,对应UML中1的不可为空性;self.orders: List[Order]中List默认可为空,对应0..*的容错性。建模时若把1写成0..1,代码中就要增加if customer is None:分支,徒增复杂度。
2.3 第三层:包图组织逻辑——不是文件夹,是变更域隔离
第7题提到“UML提供四种结构图”,其中包图(Package Diagram)常被忽略,但它解决的是团队协作中的变更影响范围问题。例如某电商系统,将“支付”、“物流”、“用户中心”划分为独立包,意味着:
- 修改支付包内类,不影响物流包的编译(物理隔离)
- 支付包对外只暴露
IPaymentService接口,物流包只需依赖此接口(逻辑隔离)
试题中虽无直接包图题,但第61题“UP核心工作流”隐含此逻辑:业务建模、需求分析、设计等工作流,本质就是不同包的演进节奏。初始阶段只定义支付包的顶层接口,细化阶段才填充内部类——这就是包图的增量开发价值。
| 包名 | 包含内容 | 变更频率 | 依赖方向 | 考试易错点 |
|---|---|---|---|---|
domain | Customer,Order,Product | 低 | 被所有包依赖 | 误将Payment放入domain包(违反领域聚焦) |
application | OrderService,PaymentFacade | 中 | 依赖domain,被web依赖 | 漏画<<interface>>虚线箭头(应标为依赖而非实现) |
infrastructure | JDBCOrderRepository,RabbitMQEventBus | 高 | 依赖domain/application | 将JDBCOrderRepository与Order画成继承(应为实现接口) |
注意:包图中
<<interface>>必须用虚线+空心三角箭头指向接口,实线+实心三角箭头表示继承。考试若画反,直接扣分——因为这混淆了“使用契约”和“扩展行为”两种根本不同的关系。
3. 动态建模的双轨验证:交互图(顺序图/协作图)与状态图的协同设计
UML动态视图不是动画脚本,而是系统行为的可执行说明书。顺序图和协作图看似重复,实则分工明确:顺序图是时间轴上的责任追踪器,协作图是空间结构中的消息路由图。第4、72、73题反复对比二者,正是为了训练你用不同维度交叉验证设计合理性。下面以第21题SSD图(系统序列图)为例,展示如何用双图互锁发现设计漏洞。
3.1 顺序图:用生命线锁定职责归属
SSD图(System Sequence Diagram)是UP中细化阶段的关键产物,它站在系统边界看外部参与者如何触发内部操作。第21题要求画出POS收款流程的SSD图,关键在三点:
- 生命线命名:必须是
Customer、Cashier、POS System(不能写User或System——模糊命名暴露职责不清) - 系统框标注:“POS System”必须用矩形框围住,表示黑盒边界
- 操作命名:
makeNewSale()、enterItem()等必须是动宾短语,且首字母小写(UML规范)
Customer Cashier POS System | | | |--- makeNewSale() ---------------->| | | | |<-- saleCreated ------------------| | | | |--- enterItem(itemID,quantity) --->| | | | |<-- itemAdded ---------------------| | | | |--- endSale() -------------------->| | | | |<-- totalAmount -------------------| | | | |--- makePayment(amount) ----------->| | | | |<-- paymentConfirmed --------------| | | |逻辑说明:
saleCreated返回消息不是可选——它证明makeNewSale()成功创建了销售会话,否则后续enterItem无法执行。这是SSD图的核心价值:每个返回消息都是前置操作成功的契约证明。若漏画,说明设计未考虑失败路径。
3.2 协作图:用连接线暴露耦合风险
协作图(现称通信图)的威力在于:当顺序图显示“消息太多”时,协作图能立刻暴露谁在过度指挥。将第21题SSD图转为协作图,重点观察连接线:
+-------------+ +-------------+ +-----------------+ | Customer | | Cashier | | POS System | | | | | | | +------+------+ +------+------+ +--------+--------+ | | | | 1 | 1 | 1 | | | +------+------+ +------+------+ +--------+--------+ | makeNewSale | | enterItem | | endSale / makePay | +-------------+ +-------------+ +-----------------+此时发现致命问题:Cashier与POS System之间有4条消息线,但Customer只与Cashier交互。这说明Cashier成了上帝类——它既处理UI输入,又协调所有业务逻辑。真实设计中,应引入SaleController作为中介:
Cashier只发startSale()给SaleControllerSaleController再调用POS System的createSale()等方法
参数说明:协作图中消息编号
1.1,1.2表示嵌套调用,2.1表示并行调用。考试若把endSale()编号为1.3(与enterItem同级),说明未识别出销售结束是独立事务,可能引发支付未完成就关闭会话的bug。
3.3 状态图:用状态机堵住时序漏洞
第86题提到“操作契约”,其本质是状态转换的守卫条件。例如endSale()操作,只有当Sale处于Active状态时才允许执行。若忽略此约束,系统可能出现“空订单支付”或“重复结算”。状态图就是为此而生:
[Idle] | | startSale() v [Active] <-------------------+ | | | addItem() | cancelSale() v | [AddingItems] | | | | endSale() | v | [Closed] <------------------+ | | processPayment() v [Completed]避坑/常见问题/排查
现象:考试画状态图时,把endSale()画成从AddingItems直接到Completed
原因:混淆了操作(operation)与状态(state)。endSale()是触发状态转换的动作,不是状态本身;Closed才是endSale()执行后的中间状态,必须存在——因为支付可能失败,需回退到Closed重试
解决:牢记UML状态图铁律——所有业务操作必须经过至少一个中间状态。endSale()→Closed→processPayment()→Completed,缺一不可
现象:状态转换上不写守卫条件(如
[paymentReceived==true])
原因:认为状态图只画流程,忽略其形式化验证价值。UP中状态图需配合OCL(对象约束语言)编写,用于生成测试用例
解决:每个带条件的转换必须标注[condition],例如[total>0]表示仅当订单金额大于0才允许endSale()
现象:把
Customer也画进状态图,导致图中出现10+状态节点
原因:未理解状态图作用域——它描述单个对象或子系统的生命周期,不是整个系统。Customer状态(如Registered/Suspended)应单独建模
解决:考试中若题目指定“画Sale的状态图”,生命线只能有Sale一个对象,其他参与者仅作为消息发送方出现
现象:用圆角矩形画初始/终止状态,但连线方向错误(如从终止状态出发画箭头)
原因:机械记忆符号,未理解语义。初始状态(实心黑圆)是起点,终止状态(同心圆)是终点,箭头必须指向终止状态
解决:画完检查所有箭头:是否都从非终止状态指向终止状态?初始状态是否有入箭头?若有,立即重画
现象:状态名用动词(如
EndingSale)而非名词(Closed)
原因:受顺序图影响,把状态当成动作。状态是对象在某一时刻的静止快照,必须用名词描述
解决:状态命名自查表——能否用“当前是XXX”造句?当前是Closed✓,当前是EndingSale✗
4. 建模哲学落地:GRASP原则与设计模式的决策树
很多考生把GRASP和设计模式当名词背诵,却不知它们本质是面向对象设计的决策树:遇到一个问题,按固定路径判断,自然导出解决方案。第24、35、39题集中考察此能力。下面以第24题“谁负责创建对象”为例,构建可执行的判断流程。
4.1 创建者(Creator)模式:五步判定法
当看到“谁创建X类实例”时,不要想“工厂模式”,先走五步判定:
- 问:X的实例是否记录了B的实例?
(如Order记录CustomerID)→ 是,则Customer类负责创建Order - 问:X的实例是否紧密使用了B的实例?
(如SaleController频繁调用InventoryService.checkStock())→ 是,则SaleController负责创建InventoryService - 问:B是否包含X的实例?
(如ShoppingCart包含CartItem列表)→ 是,则ShoppingCart负责创建CartItem - 问:B是否具有创建X的初始化数据?
(如OrderForm持有customerId、items等数据)→ 是,则OrderForm负责创建Order - 问:以上都不满足?
→ 引入OrderFactory,但必须声明:OrderFactory只创建Order,不创建Payment(单一职责)
第24题答案中“创建者模式解决谁负责创建新实例”,其潜台词是:创建权必须与信息持有权、使用紧密度、生命周期绑定度对齐。若强行让PaymentService创建Order,就违反了低耦合——支付模块不该知道订单的字段结构。
4.2 控制器(Controller)模式:边界识别三原则
第21题SSD图中POS System框,就是控制器的具象化。但考试常错在把Cashier当控制器。控制器识别有三原则:
- 原则1:必须位于系统边界
Cashier是人,POS System是软件系统,控制器只能是后者 - 原则2:必须处理系统事件
makeNewSale()是外部事件,calculateTotal()是内部方法,控制器只响应前者 - 原则3:必须协调多个领域对象
POS System需调用Sale、Inventory、Payment,Cashier只调用POS System
因此,POS System不是黑盒,而是SaleController、PaymentController等具体类的集合。考试若画POS System为单个类,需注明其聚合关系。
4.3 设计模式选择矩阵:从问题域直通方案
第35题列出单例、策略、组合等模式,但关键在问题驱动。下表是实战中我用的速查矩阵:
| 问题描述 | 匹配模式 | UML图中体现 | 考试易错点 |
|---|---|---|---|
| “整个系统只能有一个配置管理器” | 单例(Singleton) | 类图中getInstance(): ConfigManager带<<static>>,且构造函数私有 | 误画成全局变量(UML中无全局变量概念) |
| “折扣规则每月更换,不能改代码” | 策略(Strategy) | 类图中DiscountStrategy接口,HolidayDiscount、MemberDiscount实现它;SSD图中applyStrategy()消息发给接口 | 把策略类画成继承Discount基类(应为实现接口) |
| “菜单有分类和菜品,要统一处理” | 组合(Composite) | 类图中MenuComponent抽象类,MenuCategory和MenuItem继承它;所有操作如add()在抽象类定义 | 漏画MenuComponent的递归关联(MenuCategory可包含其他MenuCategory) |
| “日志要同时写文件和发邮件” | 观察者(Observer) | 类图中Logger是Subject,FileLogger、EmailLogger是Observer;顺序图中logger.notify()触发所有观察者 | 把观察者画成Logger的属性(应为独立类,通过注册机制关联) |
| “老支付接口和新接口要共存” | 适配器(Adapter) | 类图中PaymentAdapter实现NewPaymentInterface,持有OldPaymentService实例;协作图中PaymentAdapter转发消息 | 误将OldPaymentService画成PaymentAdapter的父类(适配器是委托,非继承) |
注意:考试中若题目说“用适配器模式改造旧系统”,UML图中必须出现三个角色:目标接口(Target)、适配器(Adapter)、被适配者(Adaptee)。少一个即判错——因为适配器模式的本质是“三方协议”,不是两方改造。
5. 避坑/常见问题/排查:UML考试中高频翻车现场与血泪修复指南
UML考试最残酷的地方在于:你以为在考建模能力,其实考的是对OO设计原则的肌肉反射。下面5条是我在批改上百份模拟卷后总结的“一错即挂”雷区,每条都附真实翻车案例和急救方案。
现象1:把用例图(Use Case Diagram)画成功能列表
真实翻车:学生将“电话用户”Actor连向“使用电话卡”“对方付款”两个椭圆,但两个椭圆之间无关系,也未标注<<include>>或<<extend>>
原因:混淆了用例(Use Case)与功能点(Feature)。用例必须是完整业务目标,如“完成通话”;“使用电话卡”只是其实现方式之一
解决:用例命名必须是动宾结构+业务价值,如“发起通话请求”(而非“点击拨号按钮”)。若两个用例存在复用关系,必须用虚线箭头标注<<include>>(强制包含)或<<extend>>(可选扩展)
现象2:在类图中给关联关系乱加导航箭头
真实翻车:学生画Customer—Order关联,在Customer端加箭头指向Order,理由是“客户拥有订单”
原因:导航箭头表示访问路径,不是所有权。Customer类中若有List<Order>属性,才需加箭头;若订单通过orderRepository.findByCustomerId()查询,则不应加箭头
解决:考试中若题干未给出代码片段,一律不画导航箭头!UML规范中,无箭头关联表示双向可访问,这是最安全的默认选项
现象3:把SSD图(系统序列图)和顺序图(Sequence Diagram)混为一谈
真实翻车:学生在SSD图中画出Sale、Inventory、Payment等内部对象的生命线
原因:SSD图只画系统边界(POS System框)和外部参与者(Customer、Cashier),内部对象属于详细设计阶段的顺序图范畴
解决:牢记SSD图的黄金法则——框内只有一个生命线:系统本身。所有内部交互都在框内用文字描述,如“系统创建Sale对象并调用addItem()”
现象4:状态图中遗漏历史状态(History State)
真实翻车:学生画Sale状态图,从Active到AddingItems,再因cancelSale()回到Active,但未处理“取消后重新添加商品”的场景
原因:未理解H*(深历史)状态的价值。H*表示返回上次离开的子状态,避免用户重复操作
解决:凡涉及“中断-恢复”流程(如购物车暂存、表单草稿),必须在起始状态旁加H*,并连线到复合状态。考试若漏画,扣分点在于“未考虑用户体验连续性”
现象5:用例描述中写技术细节
真实翻车:学生写用例“使用电话卡”的描述:“用户点击‘拨号’按钮,系统调用TelecomAPI.sendCallRequest(),返回HTTP 200”
原因:用例是用户视角的业务契约,不是技术规格书。UP中用例描述严禁出现API、HTTP、按钮等实现词
解决:用例描述三要素模板:
- 主成功场景:用户插入电话卡 → 输入号码 → 按呼叫键 → 听到拨号音 → 对方接听
- 扩展场景:卡余额不足 → 系统提示充值 → 用户选择跳过 → 使用对方付款
- 后置条件:通话记录存入系统,卡余额更新(不写“写入MySQL”)
6. 从考场到实战:用UP四阶段框架反向驱动UML建模质量验证
最后这章不讲新知识,而是给你一个建模质量自检清单——它来自某高校软件工程实验室的真实项目复盘。我们曾用此清单审查一个医疗预约系统,发现37%的类图在细化阶段就存在根本性缺陷,避免了后期返工。清单核心是:用UP四阶段的目标倒推UML图是否达成其本职使命。
6.1 初始阶段:用例图必须通过“三问验证”
初始阶段产出用例图,目标是锚定业务价值。画完后必须自问:
- 问1:每个用例是否对应一个干系人的明确收益?
如“患者预约挂号”收益是“减少排队时间”,若答案是“方便医生排班”,说明用例归属错误(应属医生用例) - 问2:是否存在用例没有Actor连接?
所有用例必须有至少一个Actor,否则是“幽灵用例”——可能是待分解的子功能 - 问3:Actor命名是否体现角色而非岗位?
“挂号员”是岗位,“预约服务提供者”是角色。UP中Actor代表与系统交互的职责,不是具体人
验证技巧:把用例图打印出来,用红笔圈出所有Actor,检查是否覆盖患者、医生、管理员、医保系统四类角色。少一类,模型完整性即存疑。
6.2 细化阶段:类图必须通过“责任审计表”
细化阶段产出领域模型(类图),目标是固化核心业务概念。我们用Excel建责任审计表,每行一个类,列包括:
| 类名 | 核心职责(1句话) | 数据来源(业务文档页码) | 是否被2个以上用例引用 | 生命周期是否独立 |
|---|---|---|---|---|
Appointment | 管理一次预约的时间、地点、医患双方 | 需求文档P12 | ✓(预约、取消、查询) | ✗(依赖Patient/Doctor) |
审计规则:
- 若“核心职责”超过20字,说明职责过载,需拆分
- 若“数据来源”空白,说明该类是拍脑袋设计,非业务驱动
- 若“是否被2个以上用例引用”为✗,该类大概率是过渡类,应降级为属性
6.3 构造阶段:SSD图必须通过“消息流压力测试”
构造阶段用SSD图验证系统事件流。我们对每条消息做压力测试:
- 消息1:
makeNewSale()- 压力点:高并发下是否产生竞态?→ 需
SaleController加锁或队列
- 压力点:高并发下是否产生竞态?→ 需
- 消息2:
enterItem()- 压力点:单品价格突变是否实时生效?→ 需
InventoryService提供getPrice(itemId)实时查询
- 压力点:单品价格突变是否实时生效?→ 需
- 消息3:
endSale()- 压力点:网络中断时订单状态是否一致?→ 需
Sale状态机支持PartialPaid中间态
- 压力点:网络中断时订单状态是否一致?→ 需
血泪经验:从那以后我每次画完SSD图,都强制用Postman模拟三次异常:1)
enterItem()超时 2)endSale()返回500 3)makePayment()延迟3秒。只有全部能优雅降级,才敢进入编码。希望帮到你。
本文还有配套的精品资源,点击获取