OO 第四单元与课程总结

左茂成-24371203 2026-06-24 23:04:48

一、正向建模与开发

本单元的图书馆系统本质上是一个状态丰富、规则不断叠加的业务系统:书籍副本会在普通书架、珍本书架、预约处、借还处、阅览室和用户之间流转,用户又受到借阅数量、预约状态、信用分、续借、评分等规则约束。如果一开始只顺着命令类型写过程式代码,很容易让所有逻辑堆到一个大方法里,后续每增加一次作业要求都要在旧逻辑上补洞。

因此本单元实践的正向建模,核心是先找出稳定对象和稳定边界。当前工程中最终形成了 LibrarySystemRequestProcessorArrangeServiceBookMoverBookCopyUserReservation 以及若干地点容器和服务类。LibrarySystem 负责系统生命周期和开闭馆分发,RequestProcessor 负责处理用户命令,ArrangeService 负责整理调度,BookMover 负责集中表达书籍状态迁移,BookCopy 记录一本具体副本的状态和轨迹。这样一来,三次作业虽然不断增加新规则,但每次扩展都能找到比较自然的落点。

两阶类图在这个过程中起到了不同层次的作用。一阶类图先回答“系统里有哪些对象”“对象之间大致如何协作”“状态应该由谁持有”。二阶类图则更像“可落地的施工图”,补全操作和细节。二阶类图在代码实现和业务细节逐渐明确后,把借书、还书、预约、取书、阅览、归还、评分、续借、轨迹查询、信用分结算等细节重新收束回模型中,使模型能真正对应代码。

我对两阶类图作用的理解是:一阶类图稳定“对象边界”,二阶类图稳定“行为边界”。一阶类图让我在写代码前先避免职责混乱;二阶类图则让我在实现之后重新检查行为是否有清晰归属,尤其是新增规则有没有破坏原来的分层。对于第四单元这种业务规则多、状态迁移多的作业来说,二阶类图的价值很明显:它把“我临时加了哪些方法”重新整理成“系统现在应当具备哪些正式接口”。

二、架构设计与 UML 追踪关系

最终工程的架构可以概括为四层。

第一层是输入和系统主控层。Main 使用官方包的 SCANNER 读取初始库存和后续命令,然后把命令交给 LibrarySystemLibrarySystem 初始化所有 BookCopy,维护 currentDate,在日期变化时触发逾期结算,并区分开馆、闭馆、信用分查询和普通请求。它本身不处理复杂业务,而是把请求分派给 RequestProcessorArrangeService

第二层是请求处理层。RequestProcessor 是用户命令的主要入口,处理 BORROWEDRETURNEDORDEREDPICKEDREADRESTOREDGRADEDRENEWEDQUERIED 等请求。它会调用 BorrowLimitChecker 检查 A/B/C 类书的借阅限制,调用 CreditService 判断信用分门槛和奖惩,调用 GradeService 更新评分,并和各个地点容器协作完成书籍移动。它承担的是“业务规则判断”和“请求是否 accept”的职责。

第三层是整理调度层。ArrangeService 处理开馆和闭馆时的内部整理:过期预约要释放并扣信用分,阅览室中的书要回到目标书架,借还处中的书在开馆整理时回流,评分变化可能让书在 BookshelfTreasuredBookshelf 之间移动,未满足的预约也会在整理时被分配到 AppointmentOffice。这里的重点不是响应某一个用户请求,而是维护系统在开闭馆边界上的一致状态。

第四层是状态、容器和服务层。BookCopy 保存一本副本的 bookIdisbn、当前位置、移动轨迹、借阅日期、到期日期、预约标记、珍本标记等信息;BookMover 负责调用 moveTo 记录状态转移;BookshelfTreasuredBookshelfBorrowReturnOfficeAppointmentOfficeReadingRoomUserShelf 都实现了 BookLocation,分别管理书架、预约处、借还处、阅览室和用户持有的书。Reservation 则独立保存预约的学生、ISBN、分配到的副本、预约日期、过期日期和 active 状态。

从 UML 到代码的追踪关系比较清晰。类图中的 20 个类/接口都能在 src 目录下找到对应实现。BookLocation 在 UML 中被六个地点类实现,代码中也确实由 BookshelfTreasuredBookshelfBorrowReturnOfficeAppointmentOfficeReadingRoomUserShelf 实现该接口。类图中的字段也基本能对应到代码中的成员变量,例如 LibrarySystem 持有 bookMoverbookshelftreasuredBookshelfreadingRoomappointmentOfficeborrowAndReturnOfficeuserShelfgradeServicecreditServicerequestProcessorarrangeServicecurrentDate

状态图和代码之间的追踪主要体现在 @Trigger 注解上。状态图中的主要状态包括 BookshelfTreasuredBookshelfBorrowReturnOfficeAppointmentOfficeReadingRoomUser。代码中,BookCopy.initOnShelf() 对应从初始状态进入 BookshelfBookMover.borrowBook() 对应从普通书架或珍本书架到 UserreturnBook() 对应从 UserBorrowReturnOfficepickBook() 对应从 AppointmentOfficeUserreadBook() 对应从书架到 ReadingRoomrestoreBook() 对应从 ReadingRoomBorrowReturnOfficearrangeToAo()arrangeToBs()arrangeToTbs() 则对应整理过程中进入预约处、普通书架和珍本书架。

例如代码中有如下状态转移标注:

@Trigger(from = "Bookshelf", to = "User")
@Trigger(from = "TreasuredBookshelf", to = "User")
public void borrowBook(BookCopy bookCopy, LibraryBookState from, LocalDate date) {
    moveBook(bookCopy, from, LibraryBookState.USER, date);
}

顺序图和代码之间的追踪主要体现在预约与取书流程。uml_ultimate.mdj 中的顺序图包含 :RequestProcessor:AppointmentOffice 两条生命线,以及 orderNewBookorderBookpickBookgetOrderedBook 等消息。代码中 RequestProcessor.orderNewBook()RequestProcessor.getOrderedBook() 都使用了 @SendMessage(from = "RequestProcessor", to = "AppointmentOffice"),并同时标注了对 User 的消息发送。这说明预约流程不是只存在于图上,而是确实落到了具体方法上。

当然,最终代码和 UML 并不是机械的一比一。代码中还有一些更偏实现辅助的方法,例如 BookMover.moveBook()moveBookForArrange()arrangeToBro()arrangeToRr(),以及若干用于查找和封装返回值的辅助类、辅助方法。这些方法服务于代码复用和实现便利,不一定全部需要在图中作为核心业务接口突出表达。整体看,本工程的追踪关系可以概括为:类和核心职责高度一致,状态图和顺序图通过注解有明确锚点,少量实现细节在代码中比 UML 更具体。

三、大模型辅助正向建模的体验

以本单元为例,我认为比较有效的提问顺序是:先列出命令类型,包括借书、还书、预约、取书、阅览、归还、评分、续借、查询轨迹、查询信用分;再列出书籍可能处于的地点状态,包括普通书架、珍本书架、预约处、借还处、阅览室、用户;然后补充 A/B/C 类书的借阅限制、信用分门槛、过期预约、逾期惩罚、评分导致珍本流转等规则。这样模型生成的结果就会自然靠近“状态机 + 容器 + 服务”的架构,而不是停留在抽象口号上。

我在使用大模型时也会刻意要求它区分几类问题:哪些数据应该放在 BookCopy,哪些数据应该放在 User,哪些规则适合抽成服务类,哪些流程应该由请求处理器协调,哪些状态转移必须通过统一入口记录。比如移动轨迹如果散落在每个容器类里,就很难保证查询结果一致;放到 BookCopy.moveTo()BookMover 中集中处理,状态图和代码就更容易对应。再比如信用分规则如果写在每个业务方法里,后续调整门槛会很痛苦;抽成 CreditService 后,借阅、阅览、预约过期、按时还书都能通过同一套接口表达。

所以我总结出的经验是:在复杂场景中引导大模型完成架构设计任务,需要给它“业务场景、状态集合、规则约束、输出格式和校验目标”。让模型先输出候选架构,再要求它解释每个类持有什么状态、调用哪些对象、对应哪条状态转移,最后再用代码和 UML 追踪关系去验证。

四、四个单元中架构设计思维的演进

第一单元中,我的架构思维主要围绕表达式解析展开。那时最重要的是把递归下降的语法结构对象化,用 ExprTermFactor 或类似层次表达表达式的嵌套关系,再通过多项式、单项式等结构完成化简。这个阶段的设计重点是“如何把一个算法过程拆成对象”,对象更多是语法树和代数结构的承载者。

第二单元进入多线程电梯后,架构重点从静态对象关系转向并发协作。系统不再只是按输入顺序一步一步计算,而是要同时考虑输入线程、调度器、电梯线程、共享请求队列和同步控制。这个阶段我开始意识到,面向对象不仅是拆类,还要让每个类在运行时有清晰的职责:谁生产请求,谁消费请求,谁持有共享状态,谁负责唤醒和结束。线程安全、调度策略和可终止性成为比单个算法更重要的问题。

第三单元的 JML 作业让我把注意力转到规格。和前两个单元相比,JML 更强调方法的前置条件、后置条件、不变量和异常行为。我的架构设计也从“先想怎么实现”变成“先想对象对外承诺什么”。这对后面的第四单元也有帮助,因为正向建模其实同样要求先确定接口与责任,再考虑内部实现。

第四单元则把这种前置思考进一步图形化和工程化。类图让我先确定对象边界,状态图让我先确定书籍副本的生命周期,顺序图让我先确定关键业务流程中的消息传递。最终工程中的 LibrarySystemRequestProcessorArrangeServiceBookMover 等分层,其实就是这种思维演进的结果:从第一单元的算法分层,到第二单元的并发分工,到第三单元的规格驱动,再到第四单元的模型先行和代码追踪。

回头看这四个单元,我的架构思维从“能不能做出来”逐渐变成“做出来之后能不能解释、能不能扩展、能不能验证”。这也是 OO 课程最明显的训练效果之一。

五、四个单元中测试思维的演进

第一单元时,我的测试思维比较朴素,主要是构造一些表达式,看输出是否和预期等价。后来逐渐发现,表达式测试不能只靠随机生成,还要专门覆盖括号嵌套、符号合并、指数边界、同类项合并等容易出错的位置。也就是说,测试数据要服务于错误模型,而不是只追求数量。

第二单元的多线程电梯让我认识到,测试不再只是输入输出对应。并发程序中,很多问题只会在特定时序下暴露,例如请求到达与电梯运行交错、线程退出条件、队列为空时等待、换乘或调度边界等。这个单元之后,我开始更关注日志、运行过程和极端时序,而不是只看最终是否送达。

第三单元的 JML 作业中,测试思维又转向规格一致性。因为规格已经给出了方法应满足的条件,所以测试不仅要覆盖正常路径,也要覆盖异常路径、边界值和不变量保持。这个阶段我更明显地感受到,好的测试不是“随机戳系统”,而是根据规格逐条验证对象行为。

第四单元的测试重点则是命令流和状态迁移。图书馆系统里单条命令的正确性往往依赖历史状态:一本书是否在架上、是否被预约、用户是否已经借过 B 类书、C 类书是否同 ISBN、信用分是否足够、预约是否过期、书是否已经成为珍本、当天阅览是否归还等。因此测试数据需要按场景组织。例如先预约再开馆整理再取书,先阅读再闭馆触发信用惩罚,先评分让图书进入珍本书架再借阅,先逾期再还书验证信用分只扣一次,都是比单纯随机命令更有效的测试。

总体来说,我的测试思维经历了从“多造数据”到“覆盖场景”,再到“根据规格和状态机覆盖行为”的变化。大模型和评测机可以帮助生成大量数据,但真正决定测试质量的,还是我是否理解系统有哪些状态、哪些转移、哪些边界条件最容易出问题。

六、课程收获

OO 课程结束时再回头看,会发现这门课教的并不只是 Java 语法或某几次作业的解法,而是一整套面对复杂系统的方法。第一单元让我理解递归结构和对象表达,第二单元让我第一次真正面对并发系统,第三单元让我体会规格对代码的约束,第四单元让我把模型、状态和代码联系起来。每个单元都在逼我从更高一层看待程序,而不是只盯着当前这个函数怎么写。

这门课也让我更习惯用工程化的方式面对 bug。以前遇到错误时,我可能会先怀疑某个局部判断;现在会先想清楚对象状态是否一致、职责是否放错、测试是否覆盖到了状态转移链条。强测结果不理想时当然还是会难受,但也会更快地回到问题本身:是哪条规则没建模,哪个状态没有记录,哪个边界没有测试。

最后,我觉得 OO 最大的收获是让我对“可维护的代码”有了更具体的认识。可维护不是代码看起来短,也不是类越多越好,而是当需求继续变化时,我能判断应该改哪里,知道不会牵一发动全身。走完四个单元后,我对面向对象、规格、并发、建模和测试都有了更落地的理解。虽然过程很累,但这种从混乱需求中建立结构的能力,会是我之后继续写程序时很重要的底气。

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

309

社区成员

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

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