OO_UNIT4总结

吕明康-24231213 2026-06-19 21:26:33

OO_UNIT4总结

一、本单元正向建模与开发实践

本单元在图书管理系统上实践正向建模:从需求描述出发,先建立 UML 模型,再据此实现代码,并通过官方包提供的 @Trigger@SendMessage 等注解,使模型与实现保持可追踪的一致关系。
本单元的开发流程可概括为:

  1. 研读需求:区分开馆时段用户请求与开馆前/闭馆后整理两类流程,梳理图书可停留的六种位置及合法移动路径。
  2. 第一次架构设计,写代码前:在研读需求后完成详细的架构类图,确定类、属性、方法与关联,作为编码蓝图。
  3. 编码实现:以 LibraryManager 为控制中心,按类图职责划分实现各类。
  4. 第二次架构设计,写代码后:对照实现微调类图,并补充状态图、顺序图;通过 @Trigger@SendMessage 等注解与官方工具校验模型与代码的一致性。

二、两阶架构设计在正向建模过程中的作用

本单元的两阶架构设计指建模工作在时间上的两次展开,而非分析类图与设计类图两种抽象层次。两次设计围绕同一份架构类图迭代,状态图与顺序图在第二次才补全:

阶段时机产出作用
第一次写代码之前架构设计类图在编码前完成详细结构设计,明确类职责、关键属性与方法,作为实现依据
第二次写完代码之后微调后的类图 + 状态图 + 顺序图根据实现修正类图细节,并刻画动态行为:状态转移、对象协作

2.1 第一次:写代码前的类图设计

第一次架构设计的核心目标是先想清楚再动手。此时只画类图,不急于画状态图和顺序图,重点包括:

  • 划定类边界:从需求中识别 BookUser、各书架与各部门 BookshelfTreasuredBookshelfBorrowandreturnDeskReservationDeskReadingRoom,以及控制类 LibraryManager、工具类 LibraryManagerTools、辅助类 ReservationRequestRating 等。
  • 分配职责与方法:在类图上列出主要操作,例如 LibraryManager.run()handleBorrow()processSorting(),以及 moveToUser() 等移动方法的大致分工;整理子流程暂可落在 LibraryManagerTools 的静态方法上。
  • 确定关联与属性:用户持有图书 holdings、图书位置状态 state、信用分 credit、借阅期限 dueDate、预约信息 reservedForreserveUntil 等字段归属哪一类;各 Desk/Bookshelf 与 LibraryManager 的组合关系如何表达。
  • 约束编码方向:类图相当于编码前的施工图纸——例如一人同时只能持有一本 B 类书应在 User.canHold() 中体现,开馆请求与闭馆整理分离应在 handleRequest()processSorting() 中体现,避免写代码时临时拍脑袋拆类。

第一次设计无状态图和顺序图,我感觉是因为此时对动态交互的细节尚未经过实现验证;先把静态结构定稳,可减少编码过程中的大规模重构。

2.2 第二次:写代码后的模型补全与微调

代码写完后,对架构的理解往往比设计初期更具体。第二次架构设计在此基础上完成三件事:

  1. 微调类图:对照已实现代码,修正类名、属性、方法签名与可见性。例如实现中发现整理逻辑过长,将 processOverduePenalty()handleExpiredReservations() 等下沉到 LibraryManagerTools;或补充 MoveAction 接口以统一整理回调——这些调整反映到更新后的类图中。
  2. 绘制状态图:图书在 bs、tbs、ao、bro、rr、user 六处之间的合法移动,在编码中已通过 moveToUser()moveBetweenPlaces() 等方法落实;此时为 Book 补画状态图,并用 @Trigger 注解将转移与具体方法绑定,使「哪些方法引起哪些状态变化」一目了然。
  3. 绘制顺序图:典型场景如系统启动、借书、预约取书、开馆/闭馆整理的对象协作在代码中已跑通;此时补画顺序图,用 @SendMessage 标注 Main → LibraryManager 及各场所之间的消息,与类图中的方法调用对应。

第二次设计的价值在于:类图从设计意图收敛为与代码一致的架构记录;状态图、顺序图则把第一次类图中隐含的动态行为显式化,形成完整的 UML 制品集。

三、本单元作业的架构设计

3.1 总体结构

系统采用以 LibraryManager 为中心的分层式架构:

Main
  └── LibraryManager:控制层,命令循环、请求处理、整理编排
        ├── Book / User / Rating / ReservationRequest:领域实体
        ├── Bookshelf / TreasuredBookshelf:书架容器
        ├── BorrowandreturnDesk / ReservationDesk / ReadingRoom:业务场所
        ├── LibraryManagerTools:整理与规则工具
        └── 官方包类型:LibraryBookId、LibraryBookState、LibraryTrace 等
  • 入口层:Main 仅创建 LibraryManager 并调用 run()
  • 控制层:LibraryManager 持有全部书籍与用户映射、各场所实例,以及预约队列、评分表、移动轨迹等全局索引。
  • 领域层:Book 承载单本副本的状态:位置、持有人、应还日、预约保留信息;User 承载信用分、持有集、预约与阅读状态。
  • 场所层:各 Desk/Bookshelf 类管理 LibraryBookId 集合,提供 add()remove()findAvailable() 等操作。
  • 工具层:LibraryManagerTools 提供静态方法,处理整理子流程与跨实体规则:逾期、精品书架判定等。

3.2 核心流程设计

开馆请求处理:run() 循环读取 LibraryCommand,按类型分发至 handleBorrow()handleOrder()handlePick()handleRead()handleReturn()handleRestore()handleGrade()handleRenew()handleQuery() 等。每个 handler 完成校验 → 状态变更 → 输出响应。

整理流程:processSorting() 在 OPEN/CLOSE 时触发,顺序执行:逾期罚分 → 闭馆时未归还阅读罚分 → 预约失效处理 → 待预约分配 → 精品/普通书架重平衡 → 开馆前借还处清空 → 阅览室清空。移动通过 moveBetweenPlaces() 统一完成,保证轨迹记录与容器同步更新。

状态与移动:图书位置变更统一经过带 @Trigger 注解的方法 moveToUser()moveToReadingRoom()moveToBorrowDesk()moveBetweenPlaces() 等,与状态图中 bs/tbs/ao/bro/rr/user 之间的转移一一对应。

四、代码设计与 UML 模型设计的追踪关系

本单元 UML 与源码通过官方包的注解机制建立双向可追踪关系:代码中的 @Trigger@SendMessage 标注状态转移与消息发送,工具据此生成类图、状态图、顺序图中的对应元素。

4.1 类图:结构级追踪

UML 类源码类追踪关系
MainMain.java操作 main() 与关联 creates → LibraryManager 一致
LibraryManagerLibraryManager.java属性 books、users、各 desk 与全部 handler、processSorting() 操作一致
BookBook.java8 个私有属性 id、state、holderUser、dueDate 等与 getter/setter 完全对应
UserUser.java信用分常量、holdings、预约与阅读字段及 canHold()canReserve() 等方法一致
Bookshelf / TreasuredBookshelf同名源文件findAvailable() 操作与 UML 一致
BorrowandreturnDesk / ReservationDesk / ReadingRoom同名源文件容器职责一致
Rating / ReservationRequest同名源文件一一对应
LibraryManagerToolsLibraryManagerTools.java静态整理与规则方法 + 内部类 MoveAction
LibraryManagerTools.MoveAction接口 MoveAction函数式接口,用于整理回调

组合关联:UML 中 LibraryManager 以组合关系持有各 Desk/Bookshelf,与代码中 private final Bookshelf bookshelf = new Bookshelf() 等实例字段一致。

第一次类图与第二次微调之间的变化:LibraryManagerToolsMoveAction 等往往在编码过程中才从 LibraryManager 中拆分或补充;第二次类图记录的是实现后的最终职责划分,而非第一次设计时就能完全预见。

4.2 状态图:行为级追踪

状态图以 Book 的位置为状态,节点为 bs、tbs、ao、bro、rr、user,与 LibraryBookState 枚举及 @Trigger 注解严格对应:

状态图转移名代码方法Trigger 注解
moveToUsermoveToUser()from bs/tbs/ao → user
moveToReadingRoommoveToReadingRoom()from bs/tbs → rr
moveToBorrowDeskmoveToBorrowDesk()from user → bro
moveToBorrowDeskFromReadingmoveToBorrowDeskFromReading()from rr → bro
moveBetweenPlacesmoveBetweenPlaces()整理时五处之间的多种转移

initInventory() 在状态图中对应从初始到 bs 的建库转移。状态图不单独建模 BookdueDatereservedFor 等子状态,这些信息作为状态内的附加属性存在于类图中——这是常见的位置状态机 + 属性约束组合模式。

4.3 顺序图:交互级追踪

顺序图 SequenceDiagram1 刻画 MainLibraryManager 发送消息启动系统,以及开馆、预约、取书、整理等典型场景中的对象协作。代码中 @SendMessage 及各处 from/to 标注,如 bs→user、ao→user,与顺序图中的消息箭头对应。

顺序图侧重动态协作,类图侧重静态结构,状态图侧重单对象生命周期——三者通过同名方法名 handleOrder()processSorting()moveToUser() 形成垂直追踪链:顺序图消息 → 控制类方法 → 状态图转移/类图操作。

五、引导大模型在复杂场景中完成架构设计的经验总结

5.1 要求输出可追踪制品,而非仅输出代码

引导大模型时明确要求同时产出:

  • 类列表及职责一句话说明;
  • 关键状态及合法转移表;
  • 每个用户操作对应的 handler 名称;
  • 与官方包类型 LibraryBookStateLibraryReqCmd.Type 的映射表。
    这样生成的设计可直接填入 UML,且便于与后续代码做追踪检查。若只让大模型写代码,模型与实现很容易脱节。

5.2 迭代校对

将大模型第一版类图/代码与需求条文逐条对照,把不匹配项作为下一轮输入,例如:需求说预约失效当日闭馆后扣 15 分,请说明应在 handleExpiredReservations() 还是闭馆 processSorting() 中实现,并更新类图职责。差异驱动的迭代比泛泛的请优化更能收敛到正确架构。

5.3 大模型的局限与人工把关点

大模型在以下方面仍需人工把关:

  • 日期与边界:借阅期限次日起算第 1 日、保留期第 5 天闭馆后失效等,易算错一天;
  • 交互评测:还书/取书/续订由评测机动态生成,架构须保证借哪本副本、还哪本副本全链路一致;
  • 整理策略:预约分配、精品书架重平衡无唯一标准答案,但须满足不能无条件拒绝预约等硬约束。

六、架构设计思维的演进

第一单元:表达式解析

从 OOpre 过渡到正课第一单元,我开始更主动地做功能拆分和职责划分。表达式求值天然适合递归下降:把字符串拆成项、因子等对象,每层只处理本层语法,上层通过组合完成整体语义。入口类负责读入输出,解析与求值逻辑下沉到表达式树节点。

第二单元:电梯调度

电梯场景把设计重心从静态分层转向模块协作与并发协同。电梯、请求、调度器、楼层状态各自是活跃对象,彼此通过共享状态耦合;调度策略需要从门开关、方向判断中抽离,生产者—消费者式的请求流转也贯穿其中。实现过程中我系统接触了锁、同步块、等待与唤醒,明白线程边界就是设计边界,并发不变式应在架构阶段想清楚,而不是靠事后加锁试错。

第三单元:社交网络与 JML

JML 促使我在写实现前先想清楚模块对外承诺什么。前置条件、后置条件、不变式把口头约定变成可检查的规格,对人员、关系、消息、群组等操作尤其适用。我的体会是:JML 的价值不在于逐行照着规格填代码,而在于项目初期就逼问系统整体架构——这个方法成功改变什么、失败保持什么、类级别有哪些约束。

第四单元:图书馆与 UML 建模

本单元强调架构设计对工程开发的支撑作用:需求涉及开馆请求、闭馆整理、信用分、预约保留期等多条规则交叉,需要自行划分模块职责并保证整体协同。我采用以 LibraryManager 为中心、场所类管容器、Book 承载位置与借阅状态的方案;两阶架构设计,加上 @Trigger@SendMessage 追踪,使图评测能清晰展示系统结构与类间交互,并持续校验设计与实现是否一致。


七、测试思维的演进

从单元测试到数据驱动验证

OOpre 里的 JUnit 主要验证单个方法、追求代码覆盖率;第一单元实现表达式化简后,单靠手工样例已不够,我转向自作评测机,用随机输入长时间跑测来逼近正确性。测试仍偏靠体量和复杂度碰运气,但已经建立了边界意识:嵌套括号、优先级、单元素表达式等用例往往对应语法某一层的 bug。

并发测试与压力测试

第二单元电梯调度的 bug 常具随机性——同样输入在不同调度时机下走不同路径。我开始做多线程交错测试和压力测试,模拟大量请求观察响应与稳定性,并用日志、状态快照辅助复现死锁、请求丢失等问题。这一单元让我认识到:并发场景下要验证的是状态机在各种交错下仍满足不变式,而不只是终态输出对不对。

JML 规格化与 JUnit 验证

第三单元测试与 JML 契约对齐:合法调用验证后置条件,非法调用验证是否按约定拒绝或保持状态。我用手工构造极端图(空图、完全图、重复边等),加随机生成数据应对中测 JUnit。规格化让测试有了明确靶心,不必与实现行为盲目对齐。

八、课程整体收获与反思

首先是大模型辅助开发,大模型工具能提高效率,但不能替代对需求和架构的理解。其次是测试思维的提升从中测、强测到互测,测试策略从随机对拍、并发压测,对测试的理解逐渐加深。OO课程以 OOpre 的 Java 与面向对象思维作先导,再以一学期理论课和四次大作业完成设计与构造的入门:从会拆分、会协作、会约束到会建模。对我而言,这更像一段方法的积累,面向对象的路,还在后面继续走。

...全文
5 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

308

社区成员

发帖
与我相关
我的任务
社区描述
2026年北航面向对象设计与构造
java 高校
社区管理员
  • 孙琦航
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧