
简介这份PDF面向软件工程课程设计、UML建模学习与考试复习人群围绕图书馆管理系统给出完整的建模方案帮助读者理解需求分析到动态建模的完整流程。资源包共1个PDF文件约1.55MB内容以图文结合方式呈现便于对照阅读与打印复习。系统覆盖读者管理、书籍管理、借阅管理与系统管理四大功能模块并展开用例图、时序图、活动图、状态图与类图五类模型用例图区分管理员与读者的操作边界时序图细化借书、还书与罚款的对象交互顺序活动图梳理借书、还书与预订的流程分支状态图刻画书籍从新加到在库、借出、预订的状态迁移类图则给出reader、admin、Title、Item等核心类的属性与操作。已有414人学习适合需要快速掌握UML建模方法、完成课程作业或备考的读者参考。1. 从一份 UML 图纸到能跑的系统图书馆管理系统的建模底稿很多同学做课程设计时拿到需求第一反应是打开 IDE 写代码结果写到一半发现借书和还书的边界条件对不上回头改表结构改完又发现权限模型没设计最后交上去的类图跟代码完全两张皮。这份《图书馆管理系统用例图、活动图、类图、时序图.pdf》解决的正是这个顺序问题——它把需求分析、动态建模、静态建模三块串成一条线先让你把业务规则想清楚再落到图上最后才谈实现。文档覆盖了读者管理、书籍管理、借阅管理、系统管理四个功能域给出了借书、还书、罚款三条核心时序以及书籍从新加到在库、借出、预订、销毁的完整状态流转。适合正在做 UML 课程设计、软件工程实验、或者准备软考中级 UML 建模部分的从业者也适合想补一补面向对象分析基本功的后端同学。它不教你怎么写 Java但能让你在动手之前把该问的问题都问一遍。2. 需求分析怎么落到用例图四个功能域与两类角色的边界2.1 从功能需求到用例的映射逻辑需求分析里列了四个功能域读者管理、书籍管理、借阅管理、系统管理。很多人画用例图时直接把这四个功能域当成四个用例这是最常见的翻车点。用例的粒度应该对齐一次完整的业务目标而不是对齐模块划分。比如书籍管理不是一个用例它至少拆成书籍注册、书籍修改、书籍注销三个借阅管理里的借书、还书、预订、续借、过期处理、丢失处理每一个都是独立用例因为它们的触发条件、前置条件、后置条件都不一样。文档里把角色分成管理员和读者两类这个划分是对的但要注意一个细节自动借还书机在需求里被提到它既不是管理员也不是读者而是一个外部系统参与者。画用例图时应该把它作为 Actor 放在右侧跟管理员和读者并列而不是塞进某个用例内部。常见做法是管理员关联登录、书籍增删改、读者增删改、借阅管理、自动借书机管理读者关联登录、借书、还书、查询、预订、逾期处理、丢失处理、自动借书机使用。注意逾期处理这个用例管理员和读者都关联但含义不同——管理员是执行罚款操作读者是缴纳罚金如果画成一个用例后面时序图会打架。2.2 用例描述表的填写规范光有图不够每个用例至少要配一张描述表否则后面画时序图时你会发现不知道谁先动。下面这张表是我按文档内容整理的借书用例描述可以直接抄进你的实验报告字段内容用例名称借书参与者读者、管理员前置条件读者已登录借书证有效未达最大借书数量无过期未还书籍主事件流1. 读者将书交给管理员2. 管理员扫描借书证3. 系统验证读者资格4. 管理员扫描书籍条码5. 系统检查书籍是否可借6. 系统创建外借记录7. 更新书籍状态为借出备选流3a. 读者资格不符提示原因并结束5a. 书籍已被预订取消预订后继续5b. 书籍为不可借状态拒绝借书后置条件书籍状态变为借出读者借阅记录新增一条借书时间被记录这张表里的备选流是重点很多人只写主流程结果活动图里没有分支答辩时老师一问如果读者借书超量了怎么办就答不上来。把备选流写全活动图的分支节点自然就有了。2.3 用例之间的关系include 和 extend 别用反文档里没有明确写用例之间的关系但这是 UML 用例图的必考项。借书和还书都包含登录系统这个步骤所以登录应该被 include 进借书和还书而不是让读者直接关联登录。预订和借书之间是 extend 关系——预订是在借书流程基础上增加的一个可选行为不是每次借书都必须预订。判断标准很简单如果 A 用例执行时 B 用例一定执行用 include如果 B 用例只在特定条件下才插入 A 用例用 extend。我见过把罚款做成还书的 include结果还书时序图里强制出现罚款步骤这就是关系用反了导致的连锁错误。3. 动态建模三件套时序图、活动图、状态图的联动画法3.1 借书时序图的对象与消息顺序时序图的核心是对象生命线和消息箭头。文档里借书时序图涉及的对象有管理员、读者、借书界面、读者管理模块、书籍管理模块、借阅管理模块、数据库。消息顺序按文档描述整理如下读者 - 管理员: 提交借书证和书籍 管理员 - 借书界面: login() 借书界面 - 读者管理模块: checkstu_card() 读者管理模块 - 数据库: 查询读者信息 数据库 -- 读者管理模块: 返回读者记录 读者管理模块 -- 借书界面: 返回验证结果 借书界面 - 读者管理模块: showinformation() 借书界面 - 借阅管理模块: borrow() 借阅管理模块 - 读者管理模块: getreaders() 读者管理模块 -- 借阅管理模块: 返回可借信息 借阅管理模块 - 书籍管理模块: gettitle() 书籍管理模块 - 数据库: 查询书目信息 数据库 -- 书籍管理模块: 返回书目记录 书籍管理模块 -- 借阅管理模块: 返回书目信息 借阅管理模块 - 书籍管理模块: getreservation() 书籍管理模块 -- 借阅管理模块: 返回预订状态 借阅管理模块 - 借阅管理模块: getnoreservation() 借阅管理模块 - 数据库: create(borrower, item) 数据库 -- 借阅管理模块: 返回创建结果 借阅管理模块 -- 借书界面: 借书成功 借书界面 -- 管理员: 显示成功信息这里有个容易忽略的点getreservation() 和 getnoreservation() 是两个不同的消息前者查书籍是否被预订后者处理没被预订或取消预订的情况。画图时不要合并成一个否则预订状态的分支逻辑就丢了。另外 create(borrower, item) 的参数是两个对象不是两个字符串这体现了面向对象的设计思路——借阅记录关联的是读者对象和书籍对象而不是读者 ID 和书籍 ID。3.2 还书与罚款时序图的差异点还书时序图比借书短但多了一个关键判断书籍是否过期。文档里的还书流程是管理员扫描书籍 → getitem() 取得书籍条目 → 判断是否过期 → 未过期则 update() 更新书目和读者信息 → 还书成功。如果过期则进入罚款时序图系统自动计算过期天数和罚款金额 → 读者缴纳罚金 → update() 更新借阅信息。这两个时序图的对象基本一致但消息不同。还书用的是 getitem() 和 update()罚款用的是计算函数和缴费确认。画的时候注意罚款时序图里不应该出现借书相关的消息还书时序图里也不应该出现罚款计算除非你用 alt 组合片段把两个分支都画出来。我一般建议分开画因为分开后每个图的消息数量控制在 10 条以内可读性最好。3.3 活动图的分支与合并节点活动图描述的是流程文档里给了借书、还书、预订三个活动图。借书活动图的分支最多扫描借书证后要判断借书数量是否达上限、是否有过期书籍扫描书籍条码后要判断是否不可借、是否被预订。这些判断在活动图里用菱形表示每个菱形有两条出边一条是条件成立一条是不成立。开始 - 扫描借书证 - [借书数量未达上限且无过期] - 扫描书籍条码 - [借书数量已达上限或有过期] - 提示原因 - 结束 扫描书籍条码 - [书籍可借且未被预订] - 更新书籍信息和借阅信息 - 记录借书时间 - 结束 - [书籍不可借] - 拒绝借书 - 结束 - [书籍已被预订] - 取消预订 - 更新书籍信息和借阅信息 - 结束注意取消预订这个动作在活动图里是一个独立的活动节点不是判断节点。很多人把是否被预订和取消预订画成一个菱形这是错的——判断只负责分流取消预订是分流后要执行的操作。还书活动图的分支在是否过期上过期则要求缴罚款缴完再更新信息未过期直接更新。预订活动图的分支在是否可预订和是否已被预订或外借上两个条件都通过才允许登录并预订。3.4 状态图书籍的五个状态与迁移条件文档里书籍状态图给出了新加、在库、借出、预订、可用五个状态。迁移关系是新加书籍 → 在库在库 → 借出外借在库 → 预订预订预订 → 借出预订后外借预订 → 可用超出预订时间或取消预订借出 → 可用归还在库 → 销毁淘汰、损坏、丢失。这里有个细节文档说处于预订状态时也可以外借所以预订 → 借出这条边是存在的不要漏掉。状态图的价值在于帮你发现隐藏的业务规则。比如在库 → 销毁这条边需求里说的是旧书销毁功能对于淘汰、损坏、丢失的书目可及时对数据库进行修改但损坏和丢失其实是两种不同情况——损坏可能是读者损坏后赔偿丢失可能是读者丢失后赔偿这两种情况下书籍状态应该先变成丢失或损坏再进入销毁而不是直接从在库跳到销毁。如果你在状态图里补上这两个中间状态后面做数据库设计时就会多出两个状态字段业务逻辑会更严谨。4. 类图与数据库设计的对应关系从七个类到表结构4.1 七个核心类的属性与方法文档里的类图包含七个类Reader、Admin、Title、Item、Borrow、Reservation、PersistentStore。逐个拆解Reader 类属性有 reader_id、reader_Name、Address、class、borrowed操作有 addborrowed()、deleteborrowed()、reservation()。注意 borrowed 这个属性它应该是一个集合类型存放该读者当前借阅的所有 Item 引用而不是一个字符串。Admin 类属性有编号和姓名操作是书籍增删改和读者增删改。这里有个设计问题Admin 的操作直接操作 Title 和 Reader还是通过 PersistentStore 中转文档说其他对与书籍有关的活动都要经过其存储类所以 Admin 应该依赖 PersistentStore而不是直接依赖 Title。Title 类属性有 name、author、book_id代表书目信息。Item 类属性有 id操作有 reserve()、find_on_title()代表具体某本书。Title 和 Item 是一对多关系——一个书目可以有多个副本每个副本是一个 Item。Borrow 类属性有 ISBN、date代表借阅记录。Reservation 类属性有 date、ISBN、UserID代表预订记录。PersistentStore 类是持久化存储所有对数据库的读写都经过它。4.2 类之间关系的代码映射类图画完之后落到代码里就是类和接口的定义。下面用 Python 伪代码示意核心关系class Title: def __init__(self, book_id, name, author): self.book_id book_id self.name name self.author author self.items [] # 一个书目对应多个 Item class Item: def __init__(self, item_id, title): self.item_id item_id self.title title # 关联到 Title self.status available # available/borrowed/reserved/destroyed def reserve(self, reader): if self.status available: self.status reserved return Reservation(reader.reader_id, self.title.book_id) def find_on_title(self, title): return [item for item in title.items if item.status available] class Reader: def __init__(self, reader_id, name): self.reader_id reader_id self.name name self.borrowed [] # 当前借阅的 Item 列表 def addborrowed(self, item): self.borrowed.append(item) def deleteborrowed(self, item): self.borrowed.remove(item) class Borrow: def __init__(self, reader, item, date): self.reader reader self.item item self.date date class Reservation: def __init__(self, user_id, isbn, dateNone): self.user_id user_id self.isbn isbn self.date date这段代码里最关键的是 Item 的 status 字段它对应状态图里的五个状态。Title 和 Item 的一对多关系用列表实现Borrow 和 Reservation 作为关联类记录多对多关系。PersistentStore 在代码里通常对应一个 Repository 层负责把对象序列化到数据库。4.3 类图到表结构的转换规则类图转数据库表一般按以下规则普通类转一张表属性转字段主键选一个唯一标识Reader 用 reader_idTitle 用 book_idItem 用 item_id。一对多关系在多的一方加外键比如 Item 表加 title_id 外键。多对多关系拆成中间表Borrow 表就是 Reader 和 Item 的中间表字段包括 reader_id、item_id、borrow_date。Reservation 表是 Reader 和 Title 的中间表字段包括 reader_id、book_id、reserve_date。类对应表主键外键Readerreaderreader_id无Adminadminadmin_id无Titletitlebook_id无Itemitemitem_idtitle_idBorrowborrowborrow_idreader_id, item_idReservationreservationreserve_idreader_id, book_id注意 Item 表里要加 status 字段对应状态图的五个状态值。Borrow 表里要加 return_date 和 fine 字段分别记录归还日期和罚款金额这两个字段在类图里没画出来但业务上必须有属于类图遗漏但实现时必须补的典型情况。5. 避坑与排查建模过程中最容易翻车的五个地方5.1 用例粒度太粗导致时序图画不下去现象用例图里只有一个借阅管理用例画时序图时发现借书、还书、罚款的消息全混在一起对象不知道找谁。 原因用例粒度对齐了模块而不是业务目标一个用例承担了多个独立流程。 解决把借阅管理拆成借书、还书、预订、续借、过期处理、丢失处理六个用例每个用例单独画时序图。判断标准是如果一个用例的主事件流超过 10 步或者备选流超过 3 条就该考虑拆分。5.2 时序图里把数据库当对象画现象时序图里出现数据库生命线所有消息都发给数据库业务对象反而没有消息。 原因把时序图当成了 SQL 执行流程图忽略了业务对象之间的交互。 解决时序图的对象应该是业务对象读者管理模块、书籍管理模块、借阅管理模块数据库只在持久化操作时出现而且通常用 PersistentStore 代替。业务逻辑的消息应该在业务对象之间传递比如借阅管理模块调用读者管理模块的 getreaders()而不是直接查数据库。5.3 活动图缺少合并节点导致流程无法结束现象活动图里每个分支都有出边但分支结束后没有合并节点流程悬在半空。 原因只画了判断节点忘了画合并节点或者把合并节点和判断节点画成了同一个菱形。 解决每个分支结束后如果后续流程相同必须用一个合并节点把分支收回来。借书活动图里取消预订和未被预订两条分支最终都要走到更新书籍信息和借阅信息所以需要一个合并节点。判断节点和合并节点的区别是判断节点一进多出合并节点多进一出。5.4 类图里把关联关系画成依赖关系现象Reader 类和 Borrow 类之间画的是虚线箭头代码里却用成员变量持有对方引用。 原因混淆了关联和依赖。关联是长期持有的关系成员变量依赖是临时使用的关系方法参数或局部变量。 解决Reader 和 Borrow 是关联关系Reader 对象长期持有 Borrow 列表用实线箭头。Borrow 和 Item 也是关联关系。只有像 Admin 调用 PersistentStore 的 save() 方法这种临时调用才用依赖关系。判断标准如果 A 对象在生命周期内一直持有 B 对象的引用就是关联如果 A 只在某个方法里临时创建或传入 B就是依赖。5.5 状态图遗漏异常状态导致数据库字段不够用现象状态图里只有在库、借出、预订三个状态但需求里提到了书籍丢失和损坏实现时发现 status 字段不知道填什么。 原因状态图只画了正常流程没有覆盖异常流程。 解决在状态图里补上丢失和损坏两个状态以及从借出到丢失、从借出到损坏的迁移边。这样数据库的 status 字段至少需要五个枚举值available、borrowed、reserved、lost、damaged。如果需求里还有淘汰状态再加一个 destroyed。状态图越完整后面写代码时 if-else 越少。6. 从图纸到代码的最后一公里用 PlantUML 复现四张核心图文档里的图是静态图片改起来麻烦。我一般会用 PlantUML 把用例图、时序图、活动图、类图重新画一遍好处是改文字就能改图而且能直接嵌进 Markdown 或 Confluence。下面给出借书时序图和类图的两个模板复制到 PlantUML 编辑器里就能渲染。startuml actor 管理员 participant 借书界面 as UI participant 读者管理模块 as RM participant 借阅管理模块 as BM participant 书籍管理模块 as TM database PersistentStore as DB 管理员 - UI: 提交借书证和书籍 UI - RM: checkstu_card() RM - DB: 查询读者信息 DB -- RM: 返回读者记录 RM -- UI: 返回验证结果 UI - RM: showinformation() UI - BM: borrow() BM - RM: getreaders() RM -- BM: 返回可借信息 BM - TM: gettitle() TM - DB: 查询书目信息 DB -- TM: 返回书目记录 TM -- BM: 返回书目信息 BM - TM: getreservation() TM -- BM: 返回预订状态 BM - BM: getnoreservation() BM - DB: create(borrower, item) DB -- BM: 返回创建结果 BM -- UI: 借书成功 UI -- 管理员: 显示成功信息 enduml这段 PlantUML 的关键是 participant 和 database 的区分database 只用于持久化操作。消息箭头用-表示同步调用--表示返回。注意 getnoreservation() 是自调用消息用BM - BM表示。startuml class Reader { -reader_id: String -reader_Name: String -Address: String -class: String -borrowed: ListItem addborrowed(item: Item) deleteborrowed(item: Item) reservation(title: Title) } class Title { -book_id: String -name: String -author: String getItems(): ListItem } class Item { -item_id: String -status: String reserve(reader: Reader) find_on_title(title: Title): ListItem } class Borrow { -borrow_id: String -date: Date -return_date: Date -fine: Double } class Reservation { -reserve_id: String -date: Date -user_id: String -isbn: String } Reader 1 -- * Borrow Borrow * -- 1 Item Reader 1 -- * Reservation Reservation * -- 1 Title Title 1 -- * Item enduml类图里我把 Borrow 和 Reservation 的字段补全了borrow_id、return_date、fine 这三个字段在文档的类图里没画但实现时必须有。关系的多重性用1 -- *表示Reader 和 Borrow 是一对多Borrow 和 Item 是多对一Title 和 Item 是一对多。验证方法很简单把 PlantUML 生成的图和文档里的图对照如果消息顺序、类属性、关系多重性都一致说明你理解到位了。如果发现文档里有的消息你的图里没有或者你的图里多了文档没提的消息回去查需求分析看是不是漏了某个备选流。我每次做完建模都会把四张图并排放在一起检查用例图里的每个用例是否有时序图对应时序图里的每个对象是否在类图里出现活动图的每个分支是否在状态图里有对应状态。这套交叉验证走一遍课程设计答辩基本不会被问倒。从那以后我每次做 UML 建模都强制走一遍用例→时序→活动→状态→类图的闭环检查希望帮到你。本文还有配套的精品资源点击获取