UML建模能力验证与PlantUML工程实践指南
2026/9/23 2:21:13 网站建设 项目流程

简介:本资源是面向软件工程专业学生及UML初学者的系统分析与设计复习备考资料,聚焦UML核心建模概念与典型试题解析,助力快速掌握类图、交互图、关系建模、高内聚低耦合等关键考点。压缩包为单个132KB的Word文档(.doc),内容完整覆盖12道高频考题及详解,包括关联多重度判定、组合关系识别、客户-订单基数表达、顺序图与协作图对比、UML四类关系辨析、领域模型定义、可见性含义等,并附GRASP设计原则、FURPS需求分类、统一过程阶段等延伸知识点。所有题目均配标准答案与原理说明,部分含图示要求与SSD图绘制指引,逻辑清晰、讲解透彻,便于自学巩固与考前冲刺。目前已有404人学习下载,适合作为课程复习、期末备考或软考中级系统分析师的基础训练材料。

1. 这不是选择题集,而是一套可执行的UML建模能力验证工具

很多备考者拿到《UML系统分析与设计复习试题》第一反应是“背答案、刷题型”,但真正吃透这份材料的人会发现:它本质是一套带标准答案的UML建模能力验证工具包。里面每道题都对应一个真实建模动作——画类图要能推导出多重度约束,写SSD图必须匹配系统操作契约,辨析协作图与顺序图差异时得在Visio或PlantUML里实际拖拽对象并编号消息。尤其第21题要求画出POS收款台的SSD图,背后隐含的是对“系统边界”这一关键建模原则的实操检验:你不能把POS内部模块画进SSD,否则就违反了“不对系统内部结构与功能进行描述”(题51)这一铁律。这份试题覆盖了从领域建模(题15/44)、用例驱动(题47/67)、GRASP职责分配(题24)到UP迭代阶段划分(题22/41)的完整链条,适合两类人:一是刚学完UML理论想验证建模直觉是否准确的初学者;二是已参与过中型项目、需快速校准UML表达规范性的工程师——比如你在画用例图时是否下意识把“支付成功”当作独立用例?题38和题54的答案会立刻告诉你:它只是“付款”用例的一个扩展场景,而非独立用例。

2. 关联多重度与类图构建:从选择题B到可运行的PlantUML代码

2.1 多重度的本质是约束表达,不是数字游戏

题1中选项B“一个类的实类能够与另一个类的多个实类相关联”看似简单,但若只停留在字面理解,会在实际建模中犯致命错误。多重度(Multiplicity)本质是对关联关系施加的实例数量约束,它必须与业务规则严格对齐。例如题3中“一个客户提交0个或多个订单”,对应订单类到客户类的关联线应标注1(每个订单必属唯一客户),而客户类到订单类的关联线标注0..*(客户可无订单)。这种约束直接影响数据库外键设计和API返回结构——若后端接口返回客户对象时未嵌套orders: []数组,就违背了0..*语义。

2.2 用PlantUML实现题2的组合关系图

题2要求画出“类A由类B的一个实类和类C的1个或多个实类构成”的类图。这明确指向组合(Composition)关系,其UML语义是:B和C的生命周期完全依赖于A,A销毁时B/C实例必须同步销毁。PlantUML代码如下:

@startuml class A { +String name } class B { +int id } class C { +String code } A *-- "1" B : contains A *-- "1..*" C : contains @enduml

提示*--表示组合关系(实心菱形),"1""1..*"是多重度标注。注意contains标签是可选的,但强烈建议添加以明确语义。若误用--(普通关联)或o--(聚合),则失去生命周期约束含义。

2.3 验证多重度的三个实操检查点

当完成类图后,必须通过以下检查验证多重度正确性:

检查项正确示例(题3)常见错误
方向性客户→订单标0..*,订单→客户标1反向标注导致ORM映射失败
边界值覆盖0..*包含0(无订单)、1(单订单)、n(多订单)写成1..*遗漏新注册客户场景
业务动词匹配“提交订单”对应客户主动发起,“归属客户”对应订单被动绑定用“拥有”替代“提交”引发权限设计偏差

2.4 从试题到代码:生成MyBatis-Plus实体类

题3的多重度约束可直接转化为Java实体类。以Lombok+MyBatis-Plus为例:

// Customer.java @Data public class Customer { private Long id; private String name; // @TableField(exist = false) 标记非数据库字段 @TableField(exist = false) private List<Order> orders; // 对应0..*,List可为空 } // Order.java @Data public class Order { private Long id; private BigDecimal totalAmount; @TableField(value = "customer_id") private Long customerId; // 对应1,必须有值 // 外键约束在数据库层强制,Java层用@NotNull校验 @NotNull(message = "订单必须关联客户") public void setCustomerId(Long customerId) { this.customerId = customerId; } }

参数说明@TableField(exist = false)告诉MyBatis-Plus该字段不映射数据库列,仅用于DTO传输;@NotNull在Controller层拦截空customerId请求,双重保障1的约束落地。若跳过此步,前端传{customerId:null}将导致数据不一致。

3. 交互图实战:用VS Code插件生成可验证的顺序图与协作图

3.1 顺序图与协作图的核心差异在时空建模维度

题4和题72指出:顺序图按时间轴布局,协作图按空间拓扑布局。这不仅是绘图风格差异,更决定调试效率。例如题21的POS场景中,makeNewSale()enterItem()endSale()makePayment()四步操作,若用顺序图(Sequence Diagram)表示:

@startuml actor Cashier participant "POS System" as POS participant "Sale" as Sale participant "Payment" as Payment Cashier -> POS: makeNewSale() POS -> Sale: create new Sale POS <-- Sale: return Sale ID Cashier -> POS: enterItem(itemID,quantity) POS -> Sale: addItem(itemID,quantity) POS <-- Sale: update total Cashier -> POS: endSale() POS -> Sale: calculateTotal() POS <-- Sale: return amount Cashier -> POS: makePayment(amount) POS -> Payment: process payment POS <-- Payment: return receipt @enduml

逻辑说明->表示同步调用,-->表示返回消息。关键点在于return Sale ID等返回消息必须显式绘制,否则无法验证Sale对象是否被正确创建。PlantUML会自动按垂直时间轴排列生命线,消息序号隐含在垂直位置。

3.2 协作图转换:用消息编号替代时间轴

题45明确“协作图通过消息编号表示时间顺序”,因此同一POS场景的协作图需手动编号:

@startuml actor Cashier [POS System] as POS [Sale] as Sale [Payment] as Payment Cashier --> POS: 1: makeNewSale() POS --> Sale: 2: create new Sale POS <-- Sale: 3: return Sale ID Cashier --> POS: 4: enterItem(itemID,quantity) POS --> Sale: 5: addItem(itemID,quantity) POS <-- Sale: 6: update total Cashier --> POS: 7: endSale() POS --> Sale: 8: calculateTotal() POS <-- Sale: 9: return amount Cashier --> POS: 10: makePayment(amount) POS --> Payment: 11: process payment POS <-- Payment: 12: return receipt @enduml

参数说明1:2:等前缀是消息编号,协作图中对象水平排列,连接线体现空间关系。对比顺序图,此处POSSale的连线更强调“POS持有Sale引用”的设计决策,而非调用时序。

3.3 VS Code插件实操:一键双向转换与语法校验

安装PlantUML Preview插件后,可实时预览上述代码。关键技巧:

  • 语法校验:在.puml文件中右键 →PlantUML: Check Syntax,检测--><--配对错误;
  • 格式化Ctrl+Shift+PPlantUML: Format Document,自动对齐消息箭头;
  • 导出验证:右键 →PlantUML: Export Current Diagram生成PNG,用像素级比对确认消息编号连续性(题45要求)。

注意:若导出图中消息编号跳跃(如出现1:,3:缺失2:),说明PlantUML解析失败,需检查-->后是否有空格或特殊字符。

4. GRASP模式与UP阶段:从试题答案到架构决策日志

4.1 GRASP模式是职责分配的决策日志模板

题24列出的5个GRASP模式(信息专家、创建者、低耦合、高内聚、控制器)不是抽象概念,而是架构决策日志的标准条目。例如题20识别出“顾客、POS收款台、收款员、销售、商品标识、商品、商品说明”等概念类后,需用GRASP回答“谁负责处理付款事件?”:

| 决策点 | 信息专家 | 创建者 | 控制器 | 依据 | |--------|----------|--------|--------|------| | 付款事件处理者 | Payment类(持有金额计算逻辑) | Sale类(创建Payment实例) | POS System类(接收makePayment()调用) | 题24控制器模式定义:“谁来负责处理一个输入系统事件?” |

逻辑说明:控制器模式要求将系统事件(如makePayment)交由代表“用例场景”的对象处理,而非业务实体(如Sale)。这避免Sale类因承担UI事件而违反高内聚原则(题6/83)。

4.2 UP四阶段对应需求交付的物理里程碑

题22/60/76要求记忆初始、细化、构造、提交四阶段,但真正价值在于将试题答案转化为项目看板列

  • 初始阶段:在Jira创建Vision Doc任务,输出包含“问题说明、高层目标、风险承担者”(题74)的Confluence文档;
  • 细化阶段:用题41“定义大多数的需求和范围”作为验收标准,要求所有用例图(题17/38)通过评审;
  • 构造阶段:以题61“实现、测试”为Sprint目标,每次迭代交付可演示的SSD图(题21)及对应代码;
  • 提交阶段:按题60“beta测试、部署”设置发布检查清单,其中必须包含题51“不对系统内部结构描述”的SSD图复核。

4.3 用例图验证:三步法过滤无效用例

题12要求画电话用户UseCase图,常犯错误是将“使用电话卡”“对方付款”画成平行用例。正确做法是:

  1. 识别主用例打电话是核心用例(题47定义:“参与者使用系统完成某个业务过程”);
  2. 提取扩展点:题12中“使用电话卡”和“对方付款”是打电话的两种扩展场景(Extend关系);
  3. 验证Actor一致性:题38强调“用例图描述系统与外部系统及用户之间的交互”,因此电话用户必须是唯一Actor,电话卡系统对方付费平台应作为外部系统出现在部署图中,而非Actor。
@startuml actor "电话用户" as User usecase "打电话" as Call usecase "使用电话卡" as Card usecase "对方付款" as Pay User --> Call Call --> Card : <<extend>> Call --> Pay : <<extend>> @enduml

参数说明<<extend>>表示扩展关系,虚线箭头从主用例指向扩展用例。若误用<<include>>,则意味着“打电话”必须包含“使用电话卡”,违背业务现实(用户可选择预付费或后付费)。

5. 领域模型与设计模式:从名词短语到可编译的Spring Boot代码

5.1 领域模型构建:用题18策略提取真实世界概念

题18给出“概念类类别表”和“标识名词短语”两种策略。以题20的POS场景为例:

  • 名词短语提取:原文“顾客带着购买的商品或服务来到POS收款台”中提取顾客商品服务POS收款台
  • 类别表过滤:对照“人员、地点、事物、事件、角色”类别表,剔除服务(属于事物范畴,已由商品覆盖),保留顾客(人员)、POS收款台(地点)、商品(事物)、销售(事件);
  • 最终模型CustomerPosTerminalProductSale四个类,符合题44“领域模型表示真实世界的概念类”。

5.2 设计模式落地:用Spring Boot实现题35的策略模式

题35要求理解策略模式解决“算法频繁变更”,POS场景中calculateTotal()方法需支持不同计价规则(满减、折扣、积分抵扣)。Spring Boot实现如下:

// 计价策略接口 public interface PricingStrategy { BigDecimal calculateTotal(List<Product> items); } // 满减策略 @Component("fullReduction") public class FullReductionStrategy implements PricingStrategy { @Override public BigDecimal calculateTotal(List<Product> items) { BigDecimal sum = items.stream() .map(Product::getPrice) .reduce(BigDecimal.ZERO, BigDecimal::add); return sum.compareTo(new BigDecimal("100")) >= 0 ? sum.subtract(new BigDecimal("20")) : sum; } } // 折扣策略 @Component("discount") public class DiscountStrategy implements PricingStrategy { @Override public BigDecimal calculateTotal(List<Product> items) { return items.stream() .map(p -> p.getPrice().multiply(new BigDecimal("0.9"))) .reduce(BigDecimal.ZERO, BigDecimal::add); } } // 策略上下文 @Service public class PricingContext { private final Map<String, PricingStrategy> strategies; public PricingContext(List<PricingStrategy> strategyList) { this.strategies = strategyList.stream() .collect(Collectors.toMap( s -> AnnotationUtils.findAnnotation(s.getClass(), Component.class).value(), Function.identity() )); } public BigDecimal calculate(String strategyName, List<Product> items) { return strategies.getOrDefault(strategyName, strategies.get("fullReduction")).calculateTotal(items); } }

逻辑说明@Component("fullReduction")将策略Bean注册到Spring容器,PricingContext通过名称获取策略,完美匹配题35“为每个算法分别定义具有公共接口的类”。若硬编码if-else判断策略类型,则违反开闭原则(题22提及)。

5.3 高内聚验证:用SonarQube扫描类职责集中度

题6/83定义高内聚为“高度相关职责的类且工作量不过大”。在IntelliJ中安装SonarLint插件,对Sale类扫描:

  • 关键指标Cyclomatic Complexity(圈复杂度)≤10,Lines of Code≤100;
  • 违规示例:若Sale类包含generateReceipt()sendSMS()updateInventory()方法,则圈复杂度飙升,需拆分为ReceiptGeneratorSMSServiceInventoryManager
  • 修复验证:拆分后Sale仅保留addItem()calculateTotal()等核心销售逻辑,符合“职责单一”要求。

提示:SonarQube规则java:S110(类的继承树过深)和java:S107(方法参数过多)是高内聚的量化佐证,需纳入CI流水线强制检查。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询