OO 第四单元博客与课程总结

王嘉琪-ZC061017 2026-06-22 14:21:30

OO 第四单元博客与课程总结

一、前言

第四单元围绕图书馆管理系统展开,三次作业逐步扩展需求:

  • 第十三次作业:实现基础借书、还书、预约、取书、查询、开闭馆整理。
  • 第十四次作业:新增阅读、归还阅读书籍、评分、珍本书架,并提交状态图。
  • 第十五次作业:新增信用分、借阅期限、续借、信用查询,并提交顺序图。

这一单元和前三个单元最大的区别是:代码不是唯一目标,UML 模型本身也成为设计和评测的一部分。开发过程需要从需求中抽取对象和关系,先完成一阶类图,再根据模型写代码,最后用最终代码修正二阶类图,并通过状态图、顺序图表达动态行为。


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

1. 从题目描述中抽取对象

需求内容代码设计说明
图书种类Book表示某一种 ISBN 图书,保存类别、副本和评分
具体副本BookCopy表示一本实际可移动的书,保存位置、持有者、预约者和轨迹
用户User保存借阅、阅读、预约、信用分和借阅记录
普通书架BookShelf管理普通书架上的副本
珍本书架TreasuredBookShelf管理评分达到珍本标准的副本
借还处BorrowAndReturnOffice接收还书、阅读归还和续借相关操作
预约处AppointmentOffice管理预约记录和已经送达的预留副本
阅览室ReadingRoom管理用户正在阅读的副本
借阅记录LoanRecord记录借阅日期、到期日期、续借天数和逾期扣分状态
预约记录Reservation记录预约用户、ISBN、绑定副本、到期时间和是否扣分
移动轨迹MovingTrace记录副本从一个位置到另一个位置的过程
规则判断LimitPolicy统一判断借阅、预约、阅读、归还、续借是否合法
整理流程ArrangementService处理开馆前、闭馆后的副本移动

其中最关键的抽象是 BookBookCopy 的区分。Book 对应 ISBN 层面的图书,BookCopy 对应某一本具体副本。图书馆系统真正发生移动、借出、归还、预约送达和查询轨迹的是具体副本,因此移动轨迹必须绑定到 BookCopy,不能只绑定到 ISBN。

2. 从一阶类图到代码实现

一阶类图的作用是给编码前的架构定方向,主要解决三个问题:

  1. 系统有哪些核心类LibraryManagerBookBookCopyUserBookShelfAppointmentOfficeBorrowAndReturnOffice 等。

  2. 类之间有哪些基本关系LibraryManager 管理用户、图书、副本和各场所;Book 包含多个 BookCopyUser 持有借阅、阅读和预约状态。

  3. 哪些逻辑不能塞进 MainMain 只负责输入输出适配,业务调度由 LibraryManager 完成,规则判断由 LimitPolicy 完成,副本位置管理由不同场所类完成。

第十三次作业中,借书流程由 LibraryManager.borrowBook() 调度:先取得用户和图书,再调用 LimitPolicy.canBorrow() 判断限制,之后从书架取出可用 BookCopy,更新用户状态、副本位置和移动轨迹。

3. 从代码实现到二阶类图

根据最终代码修正模型。实现过程中,如果新增的类确实承担了业务职责,应进入二阶类图。

  • 第十四次新增阅读功能后,ReadingRoom 成为副本可能停留的位置,二阶类图和状态图都需要体现。
  • 第十四次新增评分和珍本书架后,Book 中出现评分字段,TreasuredBookShelf 成为新的场所类。
  • 第十五次新增借阅期限和续借后,仅用 User 保存已借副本已经不够,需要 LoanRecord 记录 borrowDatedueDaterenewedDaysreturnedoverduePenaltyApplied 等信息。
  • 第十五次新增信用分后,User 中的 creditScoreLimitPolicy 中的信用判断都需要反映到模型中。

因此,两阶类图之间的关系可以概括为:

阶段作用重点
一阶类图建立设计假设从需求中抽取核心对象和关系
代码实现验证设计是否可用暴露职责过重、状态不足、关系缺失等问题
二阶类图固化最终结构与最终代码中的类、字段、方法和关联保持一致

三、两阶类图在正向建模中的作用

1. 一阶类图:防止过程式堆代码

如果没有一阶类图,图书馆系统很容易写成一个巨大的命令分支:读入命令、判断规则、修改副本位置、输出结果全部写在一起。这样第十三次可能还能通过,但第十四、十五次新增状态和规则后会难以维护。

一阶类图至少提前固定了几个基本事实:

  • ISBN 层面的图书和具体副本不是一个对象;
  • 图书副本的位置需要被单独维护;
  • 用户状态不仅包括已借书,还包括预约和阅读状态;
  • 借还处、预约处、阅览室、书架都是副本可能停留的位置;
  • 借阅限制、信用限制、续借限制应统一判断;
  • 开闭馆整理是跨场所移动,不应散落在每个请求方法里。

这些设计不一定一开始完全准确,但它们保证了代码从一开始就有对象边界。

2. 二阶类图:校验模型和代码是否一致

二阶类图更接近“实现后的追踪表”,主要回答:

  • UML 中的类是否在代码中存在;
  • UML 中的属性是否是代码中的关键字段;
  • UML 中的一对多关系是否能对应到 ListMap 或对象引用;
  • UML 中的方法是否对应主要业务方法;
  • 状态图中的状态是否对应 BookLocation
  • 顺序图中的消息是否对应实际调用路径。

在第十五次作业的代码中,User 只保存信用分还不够,还要能通过 LoanRecord 追踪某次借阅是否到期、是否续借、是否扣过逾期分。如果二阶类图只保留 UserBookCopy 的关系,而没有体现 LoanRecord,模型就不能解释续借和逾期扣分的实现。

3. 两阶类图的实际价值

  • 一阶类图强调编码前先想清楚主干结构
  • 二阶类图强调编码后再检查模型是否真实反映实现

二阶类图不能只追求和一阶类图相似,应该保留一阶类图的主干,同时把实现过程中合理新增的类和关系补进去。


四、三次作业架构设计迭代

1. 第十三次作业:建立基础图书馆内核

第十三次作业的核心是基础借阅流程,包括借书、还书、预约、取书、查询和整理。

1.1 主要架构

模块代表类职责
输入解析MainCommandParser、各类 Request把输入转换为请求对象
统一调度LibraryManager处理用户命令和开闭馆流程
图书模型BookBookCopyIsbn区分图书种类和具体副本
用户模型User保存借阅和预约状态
场所模型BookShelfAppointmentOfficeBorrowAndReturnOffice管理副本当前位置
规则判断LimitPolicy判断能否借、约、取
移动记录MovingTrace保存副本流转路径
整理服务ArrangementService处理开馆前、闭馆后的副本移动

1.2 设计重点

第十三次最重要的设计是围绕 BookCopy 的流转建立系统:

  • 借书成功:副本从 BookShelf 移动到 User
  • 还书成功:副本从 User 移动到 BorrowAndReturnOffice
  • 预约送达:副本移动到 AppointmentOffice
  • 取书成功:副本从 AppointmentOffice 移动到 User
  • 整理:副本从借还处或其他位置回到书架;
  • 查询:根据 MovingTrace 输出具体副本的移动记录。

这一阶段的架构重点是把“副本位置变化”作为主线,而不是只围绕输入命令写代码。


2. 第十四次作业:从基础流转扩展到状态机

第十四次新增阅读、归还阅读书籍、评分和珍本书架。表面上只是多了几个命令,实际改变的是 BookCopy 的状态空间。

2.1 新增设计

新需求代码修改作用
阅读增加 ReadingRoomReadRequestRestoreRequest区分阅读和借阅
阅读归还扩展 UserReadingRoomBorrowAndReturnOffice支持从阅览室回到借还处
评分增加 GradeRequestBook 保存 scoreSumscoreCount影响是否进入珍本书架
珍本书架增加 TreasuredBookShelf高评分图书进入新的场所
预约记录增加 Reservation保存预约、绑定副本、过期状态
状态图扩展 BookLocation表示普通书架、珍本书架、阅览室等状态

2.2 架构变化

第十三次中,副本主要在普通书架、用户、预约处、借还处之间移动。第十四次之后,副本可能处于:

  • BOOKSHELF
  • TREASURED_BOOKSHELF
  • READING_ROOM
  • BORROW_AND_RETURN_OFFICE
  • APPOINTMENT_OFFICE
  • USER

这说明状态图最适合围绕 BookCopy 绘制,因为 BookCopy.location 是贯穿全部业务流程的核心状态。

评分功能也改变了整理逻辑。Book.addScore() 更新评分,Book.getAverageScore()Book.isTreasured() 判断是否为珍本。整理时,ArrangementService.chooseShelfByScore() 根据评分决定副本进入普通书架还是珍本书架。评分不只是一个输出操作,而是会影响之后的状态迁移。


3. 第十五次作业:从状态流转扩展到长期规则

第十五次新增信用分、借阅期限、续借和信用查询。这个阶段的重点不再只是副本当前位置,而是跨日期、跨流程的用户状态和借阅记录。

3.1 新增设计

新需求代码修改作用
信用分User 增加 creditScore保存用户信用状态
信用查询增加 CreditQueryRequest查询指定用户信用分
借阅期限增加 LoanRecord记录借出日期、到期日期、是否归还
续借增加 RenewRequest,扩展 LimitPolicy.canRenew()判断续借合法性并更新到期时间
逾期处理LoanRecord.applyOverduePenalty()防止重复扣分
预约过期扣分Reservation.deductCreditIfExpired()处理预约后不取的信用惩罚
官方接口适配Main 调用官方接口,业务仍交给 LibraryManager降低输入输出变化对业务层的影响

3.2 架构变化

第十五次最关键的新增类是 LoanRecord。在前两次作业中,User 保存已借副本基本够用;但加入期限和续借后,必须记录一次借阅行为的细节:

  • 借阅用户是谁;
  • 借的是哪一本副本;
  • 借阅日期是什么;
  • 到期日期是什么;
  • 是否已经续借;
  • 是否已经归还;
  • 是否已经因为逾期扣过分。

因此,LoanRecord 不是为了增加类数量,而是因为“借阅行为”本身已经成为一个需要长期保存状态的对象。

信用分也改变了规则判断。User 保存 creditScoreLimitPolicy 负责判断信用是否满足借阅、预约、阅读和续借要求。这样,信用规则不会散落在 borrowBook()orderBook()readBook()renewBook() 等方法里。


五、最终代码设计与 UML 模型的追踪关系

1. 类与代码文件的追踪

UML 类代码职责
LibraryManager系统业务调度中心,管理用户、图书、副本和各类场所
BookISBN 层面的图书,保存类别、副本和评分
BookCopy具体副本,保存位置、持有者、预约者和移动轨迹
User用户状态,包括借阅、阅读、预约和信用分
BookShelf普通书架
TreasuredBookShelf珍本书架
BorrowAndReturnOffice借还处
AppointmentOffice预约处
ReadingRoom阅览室
Reservation预约记录
LoanRecord借阅记录
MovingTrace移动轨迹
LimitPolicy规则判断
ArrangementService开闭馆整理

如果代码中新增了核心类,二阶类图中应补充;如果 UML 中某个类在代码中没有实际职责,就应删除或合并。

2. 属性与代码字段的追踪

关键字段
LibraryManagerusersbookscopies、各场所对象、arrangementService
BookisbncategoryscoreSumscoreCount
BookCopycopyCodeisbnbooklocationholderUserIdreservedUserIdreservedDateexpiredDate
UseruserIdcreditScore、借阅/阅读/预约相关集合
ReservationuserIdisbncopyreserveDateexpireDateactivecreditDeducted
LoanRecorduserIdcopyborrowDatedueDaterenewedDaysreturnedoverduePenaltyApplied
MovingTracedatesequenceNumbercopyCodefromtouserId

这些字段都不是装饰性属性,而是直接参与业务判断或输出。

3. 关联关系与代码容器的追踪

UML 关联代码依据
LibraryManager 管理多个 UserMap<String, User>
LibraryManager 管理多个 BookMap<Isbn, Book>
LibraryManager 管理多个 BookCopyMap<String, BookCopy>
Book 对应多个 BookCopyBook 中维护副本集合
User 关联借阅、阅读、预约状态User 中维护相关集合和记录
Reservation 绑定 BookCopyReservation.copy
LoanRecord 绑定 BookCopyLoanRecord.copy
BookCopy 对应多条 MovingTrace副本移动时持续追加轨迹

二阶类图中的关系应当能在字段、集合或明确的管理关系中找到依据,不能只凭语义感觉添加。

4. 行为与顺序图的追踪

业务代码方法
借书borrowBook()
还书returnBook()
预约orderBook()
取书pickBook()
阅读readBook()
归还阅读书restoreBook()
评分gradeBook()
续借renewBook()
信用查询CreditQueryRequest 对应处理分支
查询轨迹queryCommand() / queryMovingTrace()
开闭馆整理openLibrary()closeLibrary()ArrangementService

顺序图要体现一次业务请求中对象之间的调用关系。例如预约流程中,LibraryManager 需要调用 LimitPolicy 判断规则,更新 User 的预约状态,再通过 AppointmentOfficeReservation 记录预约信息。顺序图如果只画“用户调用系统”,就不能反映真实对象协作。

5. 状态图与 BookCopy.location 的追踪

状态图的核心对象是 BookCopy。它的位置状态对应 BookLocation

  • 普通书架;
  • 珍本书架;
  • 借还处;
  • 预约处;
  • 阅览室;
  • 用户手中。

状态迁移对应具体业务操作,例如借书、还书、预约送达、取书、阅读、阅读归还、整理。状态图的价值在于检查是否出现不合理迁移。例如,副本从用户手中归还后不应直接回到书架,而应先进入借还处,再由整理流程移动到普通书架或珍本书架。


六、大模型辅助已有模型优化的经验

本单元中,我主要把大模型作为已有模型和代码的检查工具,比较有效的用法有以下几种。

1. 检查已有类图是否覆盖新增需求

作业新增需求,对照已有类图和代码检查遗漏。例如:

  • 第十四次是否补充了 ReadingRoomTreasuredBookShelfReservation
  • 第十五次是否补充了 LoanRecordRenewRequestCreditQueryRequest
  • BookLocation 是否覆盖新增状态;
  • User 是否体现信用分和长期状态。

2. 检查类职责是否过重

LibraryManager 作为总控类可以承担调度职责,但不应把所有逻辑都写进去。大模型可以帮助分析哪些逻辑应该拆出:

逻辑更合适的位置
借阅、预约、阅读、续借限制LimitPolicy
副本位置变化BookCopy 和各场所类
开闭馆整理ArrangementService
预约到期和扣分Reservation
借阅期限、续借、逾期扣分LoanRecord
信用分数值保存User

避免 LibraryManager 变成无边界的大类。

3. 检查一阶类图和二阶类图的差异

二阶类图需要和最终代码一致,但也要能说明从一阶类图演进而来。比较有用的检查包括:

  • 哪些类是一阶类图中已有并保留的;
  • 哪些类是新增需求导致增加的;
  • 哪些字段是实现后才发现必须补充的;
  • 哪些关联关系是代码容器结构决定的;
  • 哪些修改属于合理演进,哪些属于前后脱节。

例如 LoanRecord 不一定在第十三次就出现,但第十五次加入期限和续借后,它就成为必要对象。这样二阶类图补充它是合理演进,而不是随意增加类。

4. 检查模型与代码的追踪关系

比起笼统问“类图合不合理”,更有效的问题是:

  • UML 中的类是否有对应 Java 文件;
  • UML 属性是否对应关键字段;
  • UML 的一对多关系是否能对应 ListMap
  • 状态图中的状态是否对应 BookLocation
  • 顺序图中的消息是否对应真实方法调用;
  • config.yml 中路径是否指向正确的 UML 文件。

这种提问方式能把大模型限制在真实代码和真实作业要求之内,避免得到表面完整但无法提交的答案。


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

1. 第一单元:从表达式处理到对象分层

第一单元主要处理表达式解析、化简和求导。架构重点是把表达式拆成有层次的对象,例如表达式、项、因子、幂函数、三角函数、自定义函数等。不同对象负责自己的解析、求导和输出。这样做的好处是,新增一种因子或函数时,不需要重写整个表达式处理流程。

第一单元让我形成的基本认识是:面向对象首先是把复杂结构拆成有职责的对象

2. 第二单元:从静态对象到并发协作

第二单元的电梯作业引入了多线程。相比第一单元,困难不再只是数据结构和算法,而是运行时对象如何协作。需要解决的问题变成了:

  • 请求队列如何保存乘客请求;
  • 调度器如何分配请求;
  • 电梯线程如何独立运行;
  • 多个线程如何共享数据;
  • 线程如何安全结束;
  • 调度策略变化时如何减少对电梯内部逻辑的影响。

第二单元让我认识到,架构不只包括类之间的静态关系,还包括运行时通信协议和共享状态边界。一个对象能不能直接修改另一个对象的状态、哪些数据需要加锁、什么时候通知线程,都属于架构设计的一部分。

3. 第三单元:从代码实现到规格约束

第三单元围绕 JML 规格展开。这个单元的重点是理解规格,并让实现满足 requiresensuresassignable、异常行为等约束。这一单元更重视方法边界:

  • 方法的前置条件是什么;
  • 正常返回时必须保证什么;
  • 异常抛出的优先级是什么;
  • 哪些字段允许被修改;
  • 查询方法是否应保持状态不变;
  • 数据结构是否能支撑规格中的性能需求。

第三单元让我认识到,面向对象设计不只是类划分,还包括方法行为契约。代码能运行不代表正确,只有满足规格描述才算正确。

4. 第四单元:从代码架构到模型先行

第四单元进一步要求从 UML 模型出发设计系统。一阶类图要求编码前先描述对象结构,二阶类图要求编码后保持模型和代码一致,状态图和顺序图要求表达对象生命周期和协作路径。

这一单元让我对架构的理解从“代码文件如何组织”扩展到“需求、模型、代码如何互相对应”。一个概念到底应该设计成类、字段、枚举、服务类、状态还是消息,需要结合需求稳定性和代码职责判断。


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

1. 第一单元:输入输出和等价性测试

第一单元的测试主要围绕表达式展开。测试重点包括:

  • 括号嵌套;
  • 系数为 0、1、-1;
  • 指数边界;
  • 三角函数嵌套;
  • 自定义函数替换;
  • 求导结果和原表达式是否等价。

这一阶段的测试主要是样例、手写边界数据和对拍,目标是保证输出表达式在数学意义上正确。

2. 第二单元:并发状态和运行过程测试

第二单元的测试难度明显增加。电梯系统不是只看最终输出,还要检查运行过程是否合法。测试重点包括:

  • 是否存在死锁;
  • 线程是否能正常结束;
  • 电梯是否重复接人或漏接人;
  • 请求集中到达时调度是否稳定;
  • 检修、换乘、双轿厢等复杂状态是否冲突;
  • 输出时间顺序是否符合要求。

这一阶段让我认识到,多线程 bug 不一定稳定复现,因此需要大量随机数据、压力测试和日志检查。

3. 第三单元:规格驱动的单元测试

第三单元的测试围绕 JML 规格展开。相比前两个单元,测试不再只是看最终输出,而是要检查每个方法是否满足规格。测试重点包括:

  • 正常行为是否满足 ensures
  • 异常行为是否按优先级抛出;
  • 查询方法是否不修改状态;
  • 图结构查询是否正确;
  • 复杂数据下性能是否能接受;
  • 边界条件下容器状态是否一致。

这一阶段我开始更重视 JUnit 和针对方法的单元测试。规格越明确,测试就越能拆成具体断言。

4. 第四单元:代码正确性和模型一致性测试

第四单元的测试对象不仅是代码,还有 UML 模型和提交结构。测试重点包括:

  • 借阅、预约、阅读、续借、信用分等业务逻辑是否正确;
  • BookCopy.location 的状态变化是否符合状态图;
  • 顺序图中的消息路径是否对应实际调用;
  • 类图中的类、属性、方法、关联是否能在代码中找到;
  • config.ymluml_pre.mdjuml_ultimate.mdj 等文件路径是否正确;
  • 提交包目录结构是否符合要求。

这一单元让我认识到,工程作业中的“正确”不只包括程序输出,还包括模型、配置、注解、文件结构和评测接口。


九、课程收获

1. 对面向对象的理解更具体

课程开始时,我对面向对象的理解比较停留在“写类、封装字段、调用方法”。完成四个单元后,我更能理解类存在的依据:一个类应该对应稳定职责,而不是为了拆代码而拆代码。

例如第四单元中,BookBookCopy 的区分就是稳定职责的体现;LoanRecord 的出现是因为借阅行为本身需要保存长期状态;LimitPolicy 的存在是为了集中管理规则,避免判断散落在各个业务流程中。

2. 更重视架构对迭代的支持

四个单元的作业都有增量迭代。前一次作业的结构如果设计过死,下一次新增功能时就会很难改。第四单元表现得最明显:第十三次的基础架构如果没有保留场所类和副本状态,后面加入阅览室、珍本书架、信用分和续借时会很难扩展。

因此我现在更倾向于先找稳定对象和变化点。稳定对象保持清晰职责,变化点通过服务类、策略类或记录类承接。

3. 对大模型辅助开发的认识更理性

大模型可以提高效率,但不能代替自己的设计判断。比较适合让它做:

  • 梳理需求变化;
  • 检查已有类图是否遗漏对象;
  • 对照代码分析类职责;
  • 检查 UML 和代码的追踪关系;
  • 根据报错定位可能的问题;

但最终是否修改类图、是否新增类、是否调整代码,仍然要回到指导书、评测规则和自己的实现。


十、总结

回顾整个 OO 课程,四个单元分别训练了对象分层、并发协作、规格约束和模型先行。从一开始只关注“能不能写出来”,逐渐转向关注“结构是否清楚、职责是否明确、状态是否一致、模型是否能解释代码”。这也是这门课对我最核心的训练。

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

309

社区成员

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

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