ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

图书馆管理系统UML设计:从用例图到部署图的完整实践

图书馆管理系统UML设计:从用例图到部署图的完整实践

简介:《图书馆管理系统UML设计》是一份面向信息管理与信息系统、软件工程等专业学习者的UML实践参考,围绕图书馆管理场景梳理系统开发背景、功能需求、安全性完整性与参与者用例,帮助读者理解从需求分析到用例建模的完整过程。资源为单一PDF文档,仅803KB,包含读者、图书管理员、系统管理员三类参与者的用例图与详细用例描述,并覆盖读者管理、书籍管理、借阅管理、系统管理等模块,对数据库安全性、完整性要求也给出了设计说明,可直接用于课程设计、毕业设计或系统开发初期的建模参考。目前已有135人学习使用,适合需要快速查阅UML设计思路、补充系统分析文档的读者。通过该文档可掌握图书管理系统中角色权限划分、用例关系表达、常见功能模块的设计要点,提升面向对象分析与建模能力。

1. 图书馆管理系统UML设计:先画图还是先写代码

图书馆管理系统几乎是UML课程设计和软考里出现频率最高的业务场景,我见过太多人拿到这个题目就打开IDE建表写接口,最后交了代码却拿不出一份能通过评审的设计文档。这份以PDF形式存在的UML设计文档,本质上是在回答三个问题:系统有哪些角色、每个角色能做什么、对象之间怎么协作。它的适用人群很明确:做课程设计的学生、准备软考UML图试题的考生、以及刚接手旧系统需要做技术预案的初级工程师。我的建议是先画图后写代码,用图把需求边界锁死,再把图翻译成表和接口,整个过程大概需要两到三天,但能省掉后面至少一周的返工。

2. 用例图先行:把借书、还书、预约和逾期罚金画成一张图

2.1 参与者与用例:谁在用系统,系统给谁用

用例图是UML设计里最容易上手也最容易被画错的图。很多人一上来就把读者和管理员画成两个孤立的火柴人,然后每个角色拉出一堆用例,看起来内容丰富,实际上没有表达任何业务规则。正确的做法是先列参与者,再列参与者真正会发起的动作,最后用包含与扩展关系把公共步骤和可选分支串起来。

图书馆管理系统里我一般会只设三个参与者:读者、图书管理员、系统管理员。不要把“图书”“数据库”画成参与者,它们是被操作的对象,不是发起动作的人。读者的用例至少有:查询图书、预约图书、借书、还书、续借、缴纳罚款;图书管理员的用例包括:处理借书、处理还书、上架新书、下架旧书、维护读者信息;系统管理员的用例是:设置借阅规则、管理账号、查看统计数据、备份数据。

这里最容易忽略的是“借书”用例并不属于读者,而是属于管理员。读者拿着书到前台,是管理员发起借书操作,读者只是触发者。这个细节在软考UML试题里经常作为陷阱出现,把借书挂在读者头上会直接丢分。用表格区分参与者和发起者能避免这个错误。

用例编号 | 用例名称 | 主要参与者 | 前置条件 | 主流程简述 借-01 | 处理借书 | 图书管理员 | 读者持有效借阅证,图书在馆 | 验证读者→扫描图书→检查预约与限额→登记借出 还-01 | 处理还书 | 图书管理员 | 读者归还图书 | 扫描图书→检查逾期→计算罚金→更新状态 续-01 | 处理续借 | 图书管理员 | 图书未逾期且未被预约 | 验证身份→检查图书状态→延长应还日期 预-01 | 预约图书 | 读者 | 图书已全部借出或锁定 | 选择图书→填写预约→排入等待队列 罚-01 | 缴纳罚款 | 读者 | 存在逾期或丢失记录 | 查询罚金→支付→核销记录

2.2 借书主流程和两个必须画的异常分支

用例图的价值不在“画了多少个椭圆”,而在“异常分支画得有多细”。借书主流程是读者身份验证通过、图书状态为可借、读者未超过最大借阅数,此时管理员登记借出并修改图书状态。但真实系统里会遇到两个高频异常:图书已被预约、读者有逾期未还记录。

第一个异常的处理逻辑是,如果图书有生效中的预约记录且预约者不是当前读者,系统应阻止借出并提示管理员。第二个异常是读者存在逾期记录时,系统允许借书但自动计算罚金,或者直接冻结借书权限,这取决于业务规则,需要在用例图的扩展点上标注清楚。我一般会在用例图旁边附一段用例描述,把这两条异常流写成文字。这样评审人看到的不只是图形,还有可验证的行为逻辑。

2.3 用例图与需求文档的对应关系

用例图画完之后,下一步是把它和需求文档的章节号对应起来。常见做法是给每个用例分配一个编号前缀,比如借书相关是BORROW-01,还书是RETURN-01,然后需求文档里的功能需求描述引用这个编号。这样当需求变更时,评审人能立刻定位到对应用例,再往下追踪到时序图和类图,形成一条完整的追溯链。

我在实际工作中见过最深的一个坑是需求文档、用例图、类图各自独立维护,改了需求只改文字不改图,半个月后图和数据模型完全对不上,最后项目验收时只能翻聊天记录确认原始需求。避免这个问题的办法是给每个用例增加“需求来源”和“涉及实体”两列,实体名直接写类图里的类名,比如图书记为Book,读者记为Reader。一旦需求变化,先改用例,再顺着实体名改动类图,保证三份材料永远同步。

3. 类图是静态骨架:图书、读者、借阅记录的关系怎么建模

3.1 核心实体类:属性取舍与数据类型选择

类图是图书馆管理系统UML设计里最核心的一张图,它直接决定后续数据库表结构和接口参数。新手画类图最常见的毛病是把所有字段全塞进类里,比如给图书类加上“存放位置”“入库时间”“备注”,结果类图变成一张字段清单,关联关系反而看不清。

我一般会把核心实体压缩到五个:Book、Reader、BorrowRecord、Reservation、Fine。Book的属性只保留ISBN、书名、作者、出版社、出版年份、总馆藏数、可借数、当前状态;Reader保留读者号、姓名、读者类型、最大借阅数、当前借阅数、联系电话;BorrowRecord保留记录号、借出时间、应还时间、实际归还时间、状态;Reservation保留记录号、预约时间、状态;Fine保留记录号、金额、缴纳状态。字段类型也要在图里标注,ISBN用string,时间用datetime,金额用decimal,状态用enum或int,不要全写成String和Integer。

读者类型这里有一个值得展开的细节。高校图书馆通常把读者分成本科生、研究生、教师三类,三类人的最大借阅数和借期不同。建模时有两种做法:直接在Reader里加一个type字段,或者做继承体系。我的建议是只要类型之间只有参数差异,就用type字段加规则配置,不要为了体现面向对象而强行拆继承。拆继承会带来多态查询的复杂度,在课程设计和软考层面都属于过度设计。

3.2 关联、聚合与组合:书架和图书是聚合,借阅记录和借阅项是组合

类图里最容易让新手翻车的是区分聚合和组合。图书馆管理系统里最经典的例子是书架和书的关系:书架是一个整体,删掉书架时图书本身不受影响,图书可以移动到另一个书架,这个关系是聚合,用空心菱形表示。而借阅记录和借阅项记录的关系是组合:借阅记录是整个借书行为的主实体,借阅项是明细,删除主记录时明细也应该删除,这个关系用实心菱形表示。

关联关系里还有一个容易忽略的点是方向。我认为在类图里必须画清楚导航方向,比如Book通过BorrowRecord关联到Reader,但是Reader不一定直接导航到Book,实际业务里查“某读者借了哪些书”是通过BorrowRecord间接查的,所以关联方向是Reader指向BorrowRecord,BorrowRecord指向Book,形成一条单向链。很多人把关联画成双向箭头,代码里就会出现循环引用,序列化时直接栈溢出,这是类图设计失误反馈到真实项目里的经典翻车现场。

多重性也要标注准确。Book和BorrowRecord是1对多,一本图书可以有多条借阅记录;Reader和BorrowRecord是1对多;Reservation和Book是多对1,一本热门图书可以被多个读者预约;Fine和Reader是多对1。多重性写错会导致数据库外键设计错误,最典型的例子是把Reservation画成1对1,结果同一本书被多人预约时,第二次预约直接覆盖第一次。

3.3 把类图翻译成数据库表

类图确认后,数据库表结构基本是机械翻译。我列出常见映射规则:每个实体类对应一张表,类的属性对应字段,类之间的1对多关系通过在多端表加外键实现,多对多需要中间表。图书馆系统里值得注意的两个点:一是Book表需要单独建stock字段表示馆藏总数,不能直接用记录数count,因为还要表达“可借数=总馆藏数-已借出数-预约锁定数”这个计算逻辑;二是Fine表要跟BorrowRecord关联,而不是直接跟Book关联,因为罚款是按借阅行为算的。

实体类 | 对应表 | 主键 | 关键外键 | 说明 Book | book | isbn | 无 | 图书基本信息与馆藏总数 Reader | reader | reader_id | reader_type_id | 读者信息与借阅额度 BorrowRecord | borrow_record | record_id | reader_id, isbn | 每次借还行为 Reservation | reservation | reservation_id | reader_id, isbn | 预约排队 Fine | fine | fine_id | record_id | 与借阅记录关联

翻译完之后我会把类图打印出来,逐字段对照表结构查一遍,确认每个箭头都对应到了外键。这个动作看起来笨,但真的能提前发现一半以上的设计缺陷,属于花小钱省大钱的做法。

4. 时序图、状态图与活动图:把动态行为锁死在图上

4.1 借书时序图:谁先发消息,谁返回什么

时序图要回答的是“一次借书操作,对象之间按什么顺序发消息”。很多人把时序图画成了流程图,把管理员的操作步骤画在一条生命线上,这是错的。时序图的生命线应该是参与者和管理系统内部的对象,而不是操作步骤本身。

借书场景我会画出四条生命线:读者、图书管理员、图书馆管理系统的Controller、数据库服务。核心消息顺序是:读者向管理员出示图书和借阅证;管理员发送验证读者请求给Controller,Controller携带readerId查询数据库并返回读者状态;管理员再发送借书请求,携带isbn和readerId;Controller先检查图书状态,再检查读者未还图书数,通过后创建BorrowRecord,更新Book可用数量,返回借书成功。图中要明确画出每条消息的返回箭头,比如查询读者后返回“读者有效或无效”,检查图书后返回“可借或已预约”。返回内容是什么直接决定流程能否走下去,不画返回箭头时序图就等于没画。

4.2 图书状态图:从可借到逾期,状态迁移的边界

状态图我一般只画两个核心对象的状态:图书和借阅记录。图书状态包括:在馆、已借出、预约锁定、下架、丢失。状态迁移必须标触发事件:已借出通过还书事件回到在馆;借出时若有生效中的预约,则进入预约锁定而不是在馆;预约锁定状态下其他读者无法借阅,但原预约者取消预约后可回到在馆;下架是管理员操作;丢失通过丢失登记事件进入丢失状态,需要走赔偿流程。

借阅记录的状态迁移稍微复杂一点:已借出、已归还、逾期中、已续借。很多人把“已续借”做成一个新记录,这是错误做法,续借应该是对原记录应还时间的修改,而不是新增记录。状态图里“已借出”状态收到续借事件后,应还时间延长,但仍然停留在已借出状态,只是属性变化。逾期状态是从已借出状态在应还时间到达后自动迁移过去的,属于时间触发的迁移,不需要用户操作。如果状态图里漏了时间触发器,开发时就会有人写出定时任务去扫表,虽然也能实现,但设计图上没表达出来,后续维护的人会困惑为什么有这个任务。

4.3 活动图与泳道:跨角色的业务流程

活动图在图书馆管理系统里的价值主要是表达跨角色流程,所以我建议一定带泳道。借书流程可以分三个泳道:读者、图书管理员、系统。读者提交图书和借阅证,管理员验证身份,系统检查读者状态和图书状态,系统内部如果判断需要预约锁定则进入预约处理逻辑,否则直接登记借出。活动图里要用菱形表达决策节点,比如“是否需要缴纳罚款”这个判断要有明确的是/否出口。

活动图和时序图的差别在于抽象层级。时序图精确到对象间的消息,适合开发人员确定接口;活动图精确到角色和业务流程,适合给产品经理和验收人员看。一份完整的设计文档里两种图都需要,但不要画重复内容。同一个借书场景,时序图我画对象协作,活动图我画角色分工,读图的人各取所需。

5. 构件图与部署图,以及绕不开的三个坑

5.1 构件图(组件图)怎么画:三层架构如何拆分

构件图在UML里也叫组件图,很多教材混着叫,指的是一回事。图书馆管理系统的构件图我一般按照逻辑分层拆:表示层构件、业务层构件、数据访问层构件。表示层放读者门户和管理员后台两个构件,业务层放借阅管理构件、图书管理构件、读者管理构件、罚款管理构件,数据访问层放数据库访问构件和缓存构件。

构件间要画清楚依赖关系,比如借阅管理构件依赖图书管理构件和读者管理构件,罚款管理构件依赖借阅管理构件。依赖方向的正确画法是从上层指向下层,表示层依赖业务层,业务层依赖数据访问层。如果构件图里出现业务层反向依赖表示层,比如图书管理构件依赖读者门户构件,说明代码分层有问题,这种依赖会导致后期改前端页面时后端模块跟着编译。

构件图里另一个容易画错的是把数据库当构件。数据库服务器本身不是UML构件,数据访问构件才属于构件图范畴。部署图里可以出现数据库节点,但构件图里画的是访问数据库的代码模块。区分两件事能避免评委或者面试官质疑你的UML基本功。

5.2 部署图:服务器、数据库终端与网络边界

部署图画的是系统跑在哪些物理节点上。常见做法是用一个Web服务器节点部署业务系统,用一台数据库服务器节点部署MySQL或PostgreSQL,客户端通过浏览器访问。节点之间要标注协议类型,Web服务器和数据库服务器之间用JDBC或连接池,客户端和Web服务器之间用HTTP或HTTPS。图书馆管理系统作为课程设计通常单机部署即可,但设计文档里我会画出分离部署形态,因为这样才能体现对吞吐量的基本认识。

节点内部可以嵌套构件,比如Web服务器节点里标注表示层构件和业务层构件。数据库节点里标注数据库访问构件。如果用了缓存,还要单独画一个缓存服务器节点,并标注与业务节点的依赖方向。部署图的信息粒度到节点和协议为止,不要细化到端口号和IP地址,那是运维文档的事,放进设计文档反而显得层级混乱。

5.3 图书馆管理系统UML设计常见问题与避坑清单

现象一:类图长得像ER图,关联线上全是数据库外键标识。

我在评审设计文档时经常看到有人把Book表的主键写到类图关联线上,这是把数据库设计思维带进了UML。原因是对类图和ER图的定位区分不清,类图表达对象及行为,ER图表达表及约束。解决方式是画类图时只保留类名、属性、方法、关联方向、多重性,不出现外键字段名。外键关系等到数据库设计阶段再补充,不要混在一起。

现象二:时序图里出现“点击借书按钮”这种消息。

点击按钮是界面交互,不是对象间消息。原因是没有区分界面层和业务对象层,把操作步骤当成了协作消息。解决方式是在时序图里画Controller对象,消息写成“借书(readerId, isbn)”这样带参数的方法调用,而不是“点击”“选择”这类动作描述。出现这种问题通常是因为画图的人没有做过接口设计,把UML图当成了线框图。

现象三:状态图只画正常流程,缺少异常状态和回迁路径。

很多人的图书状态图只有“在馆”和“已借出”,丢了预约锁定、维修、丢失这些真实业务状态。原因是为了图省事,只按主流程画状态迁移。解决方式是先列一遍图书在完整生命周期里可能出现的历史状态,再为每个状态找进入条件和迁出条件。我一般会在状态图旁边附一张状态迁移表,把触发事件、前置条件、后置状态写清楚,用表格弥补图形表达不完整的缺陷。

现象四:构件图依赖关系画得过于密集,每个构件之间都有连线。

依赖线太多等于没有依赖关系。原因是把“可能用到”和“实际依赖”混为一谈。解决方式是只画编译期或运行期必须存在的调用依赖,比如借阅管理构件调用图书管理构件查询库存。读者门户和管理员后台之间没有调用关系,就不应该画连线。依赖线数量控制在每层不超过三条,是评审时最舒服的状态。

6. 让设计稿能被验收:把图变成可评审的交付物

画完所有UML图之后,最后一步是整理成能够被老师、评委或者技术主管直接验收的PDF文档。我习惯的做法是先用PlantUML或StarUML把每张图导出为矢量图,再嵌入文档统一编号。编号规则我一般定为:UC表示用例图,CL表示类图,SD表示时序图,ST表示状态图,AC表示活动图,CP表示构件图,DP表示部署图,后接两位序号。每一张图下面配一段三到五行的图例说明,重点解释这张图回答了哪个需求问题,而不是复制一遍图上的内容。

评审时最有说服力的做法是从用例图出发,列一份功能覆盖清单。把每个用例映射到类图中的一个方法签名,再映射到时序图中的一条消息序列。比如借书用例,对应Book.borrow()方法,对应时序图中Controller向数据库发送创建记录的消息。三张图能对得上,说明设计是闭环的。对不上的地方,就是未来代码返工的重灾区。

我个人因为吃过亏才养成了这个习惯。有一回做学分制选课系统,我图省事只画了类图和时序图,没有画用例图,结果中期检查时老师问“你能告诉我系统到底给用户提供哪几个功能吗”,我翻了半天文档答不上来。后来所有项目一律从用例图开始推,功能清单、界面清单、接口清单全从用例图导出,设计文档和评审答辩都轻松很多。希望你也能在动手写代码之前,先把这套图画完整,图不欠债,代码就少踩坑。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进