UML建模实战:从类图、用例图到Java代码的完整建模指南
简介围绕《UML基础、建模与设计实战》课程整理的教学资源包适合正在学习UML建模和面向对象设计的软件专业学生、初入行的系统分析师与开发人员。包内共45个文件大小4.63MB主要由13个PPT课件、12个UML工程文件、14个Java代码文件等构成。PPT课件覆盖UML概述、用例图、类图、顺序图与协作图、状态图与活动图、组件图与部署图以及RUP统一软件过程UML工程文件包含汽车租赁系统、图书管理系统、BBS论坛系统、新闻中心、数字录音机等贴近实战的建模案例Java示例则用于对照模型理解代码实现。通过学习这套资料读者可以从图形化建模逐步过渡到代码落地掌握用例分析、静态结构设计、动态行为建模及部署规划等关键技能适合用于配套课程复习、自学者案例临摹或项目前期的设计预演。目前已有483人浏览学习整体是一份短小精悍的UML入门与实战参考。1. UML基础、建模与设计实战这份资源把一张张图串成了能落地的系统建模过程在项目评审里见过太多“画完就扔”的UML图墙上贴得满满当当代码里却找不到对应设计。问题不在UML本身而在大多数教程只讲图形语法不讲建模推理。《UML基础、建模与设计实战》课件和例子刚好反过来从图书馆管理系统的需求出发一路推到用例图、类图、时序图、状态图最后落到能编译的Java代码。类图箭头含义、活动图要素、动态结构图选型这些高频考点都有覆盖。适合三类人卡在课程设计和毕设的学生、想补建模短板的开发、备考软考中级的工程师。2. 把九种图的底子打牢类图、用例图、活动图的关键要素UML 2.5里定义了十几种图但日常建模真正高频使用的其实就那几张类图、用例图、活动图动态行为再叠加状态图和时序图。课件里把这五张图作为主线反复用同一个图书馆案例演示这一点比罗列语法实用得多。2.1 类图箭头含义六种关系一张表讲透类图是信息密度最高的UML图新手最容易在箭头上栽跟头。继承、实现、依赖、关联、聚合、组合六种关系各有各的语义画错了后期代码结构直接扭曲。我把最常见的辨识方法整理成一张表关系表示法语义代码落点继承泛化实线 空心三角箭头子类是父类的特殊化is-aextends实现虚线 空心三角箭头类兑现接口契约implements依赖虚线 普通箭头一个类临时使用另一个类方法参数、局部变量、返回值关联实线 普通箭头类之间存在稳定的结构关系类中持有对方引用聚合实线 空心菱形整体与部分部分可独立存在集合字段部分从外部传入组合实线 实心菱形部分生命周期跟随整体部分在整体内部创建整体销毁则部分销毁依赖和关联是最容易混的一对。区分方法很简单依赖是“用完就丢”——Order调用PriceCalculator.calculate()算个价格PriceCalculator的实例是临时传入或方法内创建的Order不长期持有它这就是依赖。而Order类里有一个List items字段这个集合字段长期伴随订单存在这就是关联具体是聚合还是组合再看生命周期。聚合和组合的判断我一般用“删除测试”把整体删掉部分还活不活。图书馆的班级和学生删掉班级学生还属于学校这是聚合。订单和订单项删掉订单订单项在数据库里也一起没了这是组合。这个测试在后面的图书馆案例里也会反复用到。2.2 用例图参与者、用例、包含与扩展的关系用例图经常被画成功能清单这是最典型的翻车现场。合格的用例图要回答的是谁用系统干什么期望得到什么结果。用例图的四个要素要抓准参与者、用例、系统边界、关系。参与者一定是系统外部角色图书管理员和读者都是但“数据库”不是参与者。用例一定对应参与者的一个完整目标“借书”是用例“验证读者身份”在大多数情况下不是独立用例而是借书用例内部的一个步骤。include和extend是软考和面试的高频考点。include表示基用例必然包含的子用例比如“借书”用例必然包含“验证读者身份”无论走哪个分支都要先验证。extend表示在特定条件下才触发的扩展行为比如“借书”在“图书已被预约”这个扩展点上转入“处理预约”这是可选分支不必然发生。我画用例图时会先问一句这个子步骤是每次都走还是满足条件才走每次都走用include条件触发用extend。提示在一张用例图里用例数量超过二十个就要警惕是不是把功能点当用例了。登录、验证码、密码找回这些细粒度功能在业务核心流程里通常合并成一个“用户进入系统”的用例或者作为其他用例的包含关系存在。2.3 活动图要素从起点到泳道把流程画完整活动图是UML里的流程图但它的要素比普通流程图讲究。初始节点、活动、决策、合并、分叉、汇合、泳道、对象节点每一类都有明确的建模意图。普通流程图只画控制流活动图还能把对象流画出来这是它不可替代的地方。要素作用常见误区初始节点表示流程开始一张图出现多个孤立起点活动 / 动作执行步骤粒度太大一个动作包含太多逻辑决策节点条件分支和分叉节点混用合并节点分支汇合和汇合节点混用分叉 / 汇合并行任务的开始和结束用决策节点代替并行泳道划分责任主体只画流程不画角色边界对象节点活动之间流转的数据忽略数据产生和消费的位置决策和分叉的区别是新手最常踩的决策是“或”的关系选一个分支走分叉是“与”的关系两个分支并行执行。借书流程里如果“检查读者资格”和“检查图书状态”必须同时完成才能继续用分叉启动并行如果先判断读者是否超限、超限就终止流程这是决策节点。合并节点跟上方的决策节点配对表示分支重新汇合汇合节点跟分叉节点配对表示并行任务都完成后才继续。泳道则是把图书馆员、读者、系统这些责任主体分开防止动作到底归谁执行说不清。底子打牢之后下一章直接走进图书馆管理系统的完整建模流程。3. 图书馆管理系统全流程建模从需求用例到能编译的Java代码课件里主案例是图书馆管理子系统这个例子覆盖了UML建模的完整链路。涉及的领域信息很典型图书编号、书名、作者、ISBN、分类、馆藏总量、可借数量、读者编号、姓名、类型、限借数量、借还记录、预约记录、罚款记录。麻雀虽小类图、用例图、时序图、状态图全都能练到。3.1 先收集需求再画用例图顺序不能反建模第一步不是开画而是把需求描述拆成参与者、目标和业务规则。以借书场景为例需求描述大概是这样的读者出示借书证。系统验证读者身份是否有效。验证读者当前借阅数量是否超过限借数量。检查目标图书是否处于“在馆”状态。登记借出记录扣减图书可借数量。这五条就是“借书”用例的路径描述。参与者是读者和图书管理员系统边界内还有“还书”“预约”“查询图书”“罚款管理”等用例。画用例图的时候把参与者和用例用关联线连起来就行不需要把上面五条步骤画进用例图里步骤留给后面的时序图。3.2 从用例描述中抽取候选类再细化成设计类用例描述里的名词和动词是候选类和方法的来源。“读者”“图书”“借书记录”是名词候选类“验证”“检查”“登记”是动词候选手法。基于借书场景抽出来的核心类有三个BookbookId、title、author、isbn、category、totalCount、availableCount行为是borrow()和returnBook()。ReaderreaderId、name、type、limit行为是canBorrow()和borrowBook()。BorrowRecordborrowId、readerId、bookId、borrowTime、dueTime、returnTime行为是finishReturn()。类图上的关系Reader和BorrowRecord是1对多关联Book和BorrowRecord也是1对多关联。这里有个关键点BorrowRecord本质上是个关联类它把“借书”这个动作变成了一条可持久化的记录。如果只在Reader类上写一个borrowBook()方法、不引入BorrowRecord那“借了哪些书、什么时候该还”这种历史数据就没有落点后面逾期罚款也做不了。3.3 类图正向工程成Java代码每一步都有对应类图画完直接落到代码。以Book类为例public class Book { private String bookId; private String title; private String author; private String isbn; private String category; private int totalCount; private int availableCount; public Book(String bookId, String title, int totalCount) { this.bookId bookId; this.title title; this.totalCount totalCount; this.availableCount totalCount; } // 借出库存充足才扣减返回是否成功 public boolean borrow() { if (availableCount 0) { return false; } availableCount--; return true; } // 归还不超过馆藏总量时回补库存 public void returnBook() { if (availableCount totalCount) { availableCount; } } }borrow()用返回值表示库存是否够调用方再拿这个结果结合读者限借数量做二次判断。availableCount是类图里的“可借数量”属性borrow()和returnBook()就是类图里标的那两个操作一个改库存一个回库存都是从业务规则推导出来的而不是拍脑袋加的保存方法。BorrowRecord的代码同样很有代表性public class BorrowRecord { private String borrowId; private String readerId; private String bookId; private LocalDateTime borrowTime; private LocalDateTime dueTime; private LocalDateTime returnTime; // 构造时锁定借出时间和应还时间 public BorrowRecord(String borrowId, String readerId, String bookId, LocalDateTime borrowTime, LocalDateTime dueTime) { this.borrowId borrowId; this.readerId readerId; this.bookId bookId; this.borrowTime borrowTime; this.dueTime dueTime; } // 归还时写入实际还书时间 public void finishReturn() { this.returnTime LocalDateTime.now(); } }注意这里用的是readerId和bookId而不是直接持有Reader、Book对象引用。这是持久化场景里的常规做法实体类之间用ID关联跨表组装交给数据访问层。如果业务里经常要“通过记录查图书详情”再在查询层做关联映射而不是把对象引用塞进实体类里把关系搞成蜘蛛网。类图上的1对多关联落到代码就是这个效果。3.4 用时序图把借书流程走一遍验证类图是否成立类图是静态骨架靠不靠得住得看动态行为。借书用例的时序图消息序列从上到下是读者向系统发起借书请求。系统向读者校验借书证和限借数量。条件通过时系统调用Book.borrow()检查库存并扣减。扣减成功后系统创建BorrowRecord对象。系统把借书结果返回给读者。这张时序图一画类图上的问题立刻暴露出来如果Book没有borrow()方法这条消息就发不出去如果BorrowRecord没有构造参数创建它的消息就缺少参数和数据来源。建模的价值就在这一步静态图和动态图互相验证比单独画一张“漂亮的类图”有意义得多。4. 动态行为建模状态图、时序图与交互图怎么选UML里的动态结构图是一个高频检索词它包含的图不止一种常见的有状态图、时序图、协作图、活动图。很多同学画图时懒得选拿到场景就画时序图结果把状态变化流程画得一团糟。选对图的前提是先分清楚每张图的关注点。4.1 状态图把图书生命周期建模成状态机状态图描述的是单个对象在事件驱动下的状态变迁。图书馆里最典型的状态机就是图书本身。图书的状态集合可以定义成在馆AVAILABLE、借出BORROWED、预约RESERVED、下架OFF_SHELF、丢失LOST。当前状态触发事件下一状态条件在馆借出借出读者有效且库存允许在馆下架下架管理员操作借出归还在馆该书无预约借出归还预约该书存在预约者借出超期借出触发罚款状态不迁预约取消预约在馆预约者主动取消预约读者取书借出预约窗口期内取走下架重新上架在馆管理员操作状态图最容易犯的错是把“超期”当成一个状态。超期本质上不是一个稳定状态它是“借出”状态下经过一段时间后满足某个guard条件触发罚款动作。状态是对象可以长时间停留的情境事件是瞬间发生的事情这个区分是状态图建模的底线。对应到Java代码状态图通常是枚举加状态流转方法public enum BookState { AVAILABLE, BORROWED, RESERVED, OFF_SHELF, LOST }Book类里维护一个BookState state字段borrow()成功时把state从AVAILABLE迁到BORROWEDreturnBook()里根据是否存在预约决定迁到AVAILABLE还是RESERVED。状态图上的每一条迁移线在代码里都要能找到对应的状态变更语句找不到就说明图是画着玩的。4.2 时序图对象间消息顺序的完整画法时序图在3.4里已经用来验证过借书流程这里把它本身的画法说透。时序图的要素包括生命线、激活条、消息和组合片段。第一步列出参与协作的对象这些对象全部来自类图不能临时发明一个不在类图里的对象。第二步从用例描述里抽出消息序列按时间从上到下排列。第三步在出现分支的地方用alt、opt、loop这些组合片段框起来。alt表示多选一的分支opt表示可选的步骤loop表示循环。“验证读者身份”和“检查库存”如果都要执行且顺序无关可以用par表示并行。画时序图时我一般会多问一句消息箭头指向的对象它的类里有没有对应的方法如果没有两个选择要么给类补方法要么这条消息根本不成立。这一步能把很多“画完就扔”的图挡在提交之前。4.3 动态结构图选型活动图、状态图、时序图、协作图的边界UML中的动态结构图到底包含哪些答案是状态图、活动图、时序图、协作图这些交互和状态描述的统称。选型标准可以按问题类型来定图类型关注点最适合的场景活动图控制流与对象流跨角色的业务流程梳理比如借书审批状态图单个对象的状态变迁图书、订单、工单的生命周期管理时序图消息的时间顺序单个用例实现细节的验证协作图对象间的连接结构只关心对象怎么连接不关心顺序我的选择习惯是要讲清楚一件事从头到尾怎么走用活动图要确认一个实体在事件驱动下会经历哪些稳定状态用状态图要验证一个用例里对象之间怎么协作用时序图团队评审时想快速展示对象之间的引用结构用协作图。活动图和时序图的边界最容易模糊区分标准是看主角是“流程”还是“对象”。流程做主角选活动图对象交互做主角选时序图。借书流程要展示读者、图书、记录三类对象之间的协作和时序用时序图要展示从发起请求到完成借书的完整路径和分支用活动图。两张图可以同时服务于同一个用例只是视角不同。5. UML建模避坑手册五条血泪经验这部分内容不是教科书写出来的是项目里实实在在踩出来的。每一条都按“现象、原因、解决”的格式复盘。5.1 把类图画成了数据库表现象类图上一排实体类Book、Reader、BorrowRecord全部只有属性没有方法类之间全是1对多和1对1的连线整体看就是一张ER图。 原因把UML类图当数据库设计工具用了建模思维停留在表结构层面类的方法被默认放在Service层里。 解决每个类必须写出对外提供的业务操作。图书的borrow()、returnBook()读者的canBorrow()先写行为再补属性。行为找不到落点的类往往是职责分配出了问题。类图指导表设计但类图不是表设计有没有方法就是二者最直观的分界线。5.2 用例爆炸一张图三四十个用例现象登录、修改密码、找回密码、验证码校验、记住密码每个都单独画成一个用例一张用例图密密麻麻评审时根本没人认真看。 原因把系统功能点当成了用例没有从参与者的目标出发。 解决回到“参与者的一次完整目标”这个定义。登录、验证码、记住密码合并成“用户进入系统”这一个用例验证码通过include关系被包含进去。图书馆系统的用例控制在十个以内就够了借书、还书、预约图书、查询图书、缴纳罚款、维护图书、管理读者。用例图是给业务方和架构师看的目标视图不是功能列表。5.3 聚合和组合箭头画反现象订单和订单项画成聚合班级和学生画成组合人和身份证画成聚合评审时被同事当场质疑改来改去还是没把握。 原因只盯着结构的“整体-部分”外观没有用生命周期去检验。 解决统一用删除测试。删掉整体部分如果还能独立存在就是聚合删掉整体部分随之消亡就是组合。删掉订单订单项数据在库中一并消失组合删掉班级学生还在学校系统里聚合删掉人的记录身份证信息不会独立存在组合。这个测试十秒出结果比背定义可靠得多。5.4 时序图里没有对象的创建和销毁现象时序图的消息序列看起来完整但BorrowRecord是怎么new出来的、临时计算对象什么时候回收图上完全没有体现。 原因把时序图画成了方法调用堆栈只关注消息名没关注对象生命周期。 解决在时序图上显式画出create消息和destroy标记。BorrowRecord通过“创建借阅记录”这条消息诞生生命周期跨越整个借阅周期应该持久化临时计算价格的PriceCalculator对象用完即毁画出destroy标记提醒自己在代码里不要让它逃逸到长生命周期对象中。对象生命周期和消息序列放在一起看才能暴露资源泄漏或持久化缺失的问题。5.5 XMI导出乱码跨工具互导的玄学问题现象StarUML里画好的图导出XMI在另一个工具里打开箭头关系全乱中文变成问号整个文件基本报废。 原因XMI标准在各工具里的支持程度不一致编码设置也不一样。这不是哪张图画错了是工具生态的兼容性老问题。 解决团队协作时统一建模工具和版本跨工具交付只给PNG、SVG和PDF不给工程文件。讽刺的是XMI本来是为互操作设计的结果互操作成了最大的坑。别再格式转换上耗时间直接约定输出格式是最省力的方案。如果非要转换先确认两端工具的XMI版本号一致再统一UTF-8编码导完立刻抽查三个典型关系。6. 软考UML建模题的三步答题法从题干到类图的固定套路软考中级的UML建模题题型很固定给一段业务描述让补全用例图或类图。这类题的应试方法其实是固定的课件里配套的例子正好用来练手。6.1 三步法找名词、找动词、对关系第一步通读题干把名词圈出来名词对应类名和属性比如“订单”“订单项”“客户”第二步把动词圈出来动词对应方法和消息比如“下单”“支付”“取消”第三步看题干里的关系描述句“一个订单包含多个订单项”直接指向组合关系“客户可以拥有多个订单”用删除测试判断。题干最后一句往往是关系提示句先标注这句再动笔正确率会高很多。答题时用例图补全的重点放在参与者判定和include/extend上类图补全的重点放在关系和属性类型上。6.2 23种设计模式在类图上的标注方法23种UML设计模式及其代码是设计模型的进阶内容也是软考案例分析常客。类图上识别模式的技巧看到大量 构造型和虚线空心三角箭头优先考虑策略模式或抽象工厂看到主题类持有观察者接口的集合优先考虑观察者模式看到私有构造函数加静态getInstance()单例没跑。课件里的设计模式例子都配了Java代码从类图反推模式再从代码验证比单看一边记得牢。从那以后我每次建模完都强制自己走一遍“代码回读”类图里的每个方法必须在代码里找到对应实现每一条状态迁移必须找到状态变更语句找不到就回去改图。图不是用来应付评审的是设计决策的载体。这套思路值得你用这份课件里的图书管理例子从头跟一遍希望帮到你。本文还有配套的精品资源点击获取