309
社区成员
发帖
与我相关
我的任务
分享本单元的作业形式与先前的三个单元都不太一样,以前都是简单的构思后直接开始代码实现,而本单元则是需要先建模再完成代码实现。在本单元的建议图书馆系统的任务中,两阶类图发挥了“先稳定结构、后沉淀实现”的关键作用。
一阶类图:确立“协调者+场所+实体”的职责边界
编码前,最容易犯的错误是把所有借书、预约、整理、扣分逻辑都堆在一个大类里。一阶类图的核心价值就是提前划定三类对象的职责:
BookCopy 管理副本状态和移动轨迹,User 管理信用分与借阅列表,Reservation 管理预约生命周期。BookShelf(内部区分普通/精品)、BorrowReturnOffice、AppointmentOffice、ReadingRoom 各自负责自己范围内的图书存取。LibraryManager 仅作协调,接收指令并委托给场所或实体方法,不亲自执行具体操作。这种设计承诺,让后续信用分、借阅期限、精品书架整理等需求到来时,可以很自然地将新规则放入现有类中:信用分判断加入 User,借阅期限加入 BookCopy,整理逻辑集中在 LibraryManager.move() 里。整个架构骨架没有被动摇。
二阶类图:编码后对齐实现细节
一阶类图无法预见所有实现细节。代码完成后,二阶类图补充了实际演化出的具体方法和关系,例如在第三次的迭代中:
User 中增加了 canRead 来根据credit判断阅读权限,增加了 addCredit 来管理信用分变化。BookCopy 中增加了 borrowExpiry、setDueDate、renew、isOverdue,封装了借阅期限和逾期扣分状态。LibraryManager 中加入了 punishOverdue,专门在闭馆时处理逾期扣分,并用 borrowedBookCopies 映射追踪在借图书。这些补充让二阶类图不仅能描述“系统如何组织”,还能解释“为什么某个方法放在这个类里”。一阶定方向,二阶对实现,二者共同保证了模型与代码的一致性。
我的最终架构可以划分为三层:总控层(LibraryManager)、场所层(BookShelf、BorrowReturnOffice、AppointmentOffice、ReadingRoom)、实体层(BookCopy、User、Reservation、GradeOffice)。模型与代码的追踪关系如下。

| 类图设计 | 代码实现 | 追踪说明 |
|---|---|---|
LibraryManager 持有各场所和实体集合 | LibraryManager 拥有 bookShelf、borrowReturnOffice、appointmentOffice、readingRoom、users、reservations 等 private final 字段 | 总控层对场所和实体的组合关系直接在代码中体现 |
User 与 Reservation 一对一关联 | User 类中 private Reservation activeReservation | 用于快速判断用户是否有未取预约 |
BookCopy 聚合移动轨迹 | BookCopy 内部维护 List<LibraryTrace> movingTraceList,moveTo 方法追加轨迹 | 状态图中每次转移都会调用 moveTo,自动记录历史 |
BookShelf 内部区分普通与精品书架 | BookShelf 类中 normalShelf 和 treasuredShelf 两个 Map,addBookCopy 则根据 score 选择容器 | 评分为 4 以上进入精品书架的逻辑集中在此 |

状态图定义了 BookCopy 在书架、预约处、借还处、阅览室和用户之间的全部合法转移。每个转移的守卫条件和触发方法都能在代码中找到对应。
| 状态图转移 | 代码实现 | 追踪说明 |
|---|---|---|
BOOKSHELF → USER | BookShelf.borrowBook() 中先调用 user.checkBorrowLimit(isbn),通过则 copy.moveTo(USER, date) | canBorrow 在代码中体现为信用分与持有数量的双重检查 |
APPOINTMENT_OFFICE → USER | AppointmentOffice.pickBook() 中检查 !copy.isOverdue(date, true) | 逾期副本不可取,与状态图一致 |
BORROW_RETURN_OFFICE → BOOKSHELF (整理) | LibraryManager.move() 中遍历 borrowReturnOffice.getBookCopies(),根据评分调用 copy.moveTo(state, date) | 整理时借还处清空,移至正确书架,触发方法对应 clearBro() |
BOOKSHELF → TREASURED_BOOKSHELF (重排) | BookShelf.move() 中检查 gradeOffice.isTreasuredBook(isbn),若符合则 copy.moveTo(TREASURED_BOOKSHELF, date) | 开馆前重排书架时,评分达标的副本被迁移到精品书架 |
(在这里建议一下,如果可以的,希望下一年的OO这个状态图的Gaurd可以不强求是一个成员变量,毕竟并非每个条件判断都会设一个专门的变量,如果可以放方法的话那再好不过)
顺序图描绘的预约和取书场景,在我的实现中是由 LibraryManager 串联起 User、AppointmentOffice、Reservation 和 BookCopy 等对象完成的。整个过程清晰地分为预约阶段和取书阶段,中间的整理流程由LibraryManager.move()来衔接的。
预约阶段:用户发起预约后,LibraryManager 先调用 User.checkBorrowLimit 和 User.hasReservation 完成资格检查,随后委托 AppointmentOffice.orderBook 创建一个状态为 PENDING 的 Reservation,并将其绑定到 User.activeReservation。此时预约记录已生成,但预约处尚无实体书。
取书阶段:整理流程在开馆或闭馆时将副本从书架送至预约处,并将 Reservation 状态转为 RESERVED。当用户发起取书时,LibraryManager 调用 AppointmentOffice.pickBook,该方法内部再次确认预约有效且未逾期,随后执行 BookCopy.moveTo(USER) 完成副本转移,并调用 Reservation.fulfill() 标记预约完成。整个过程在一条消息链中完成,取书成功后预约即从系统中清除。
| 顺序图消息 | 代码中的方法 | 追踪说明 |
|---|---|---|
LibraryManager → User: checkBorrowLimit | User.checkBorrowLimit(isbn) | 验证信用分与持有数量限制 |
LibraryManager → AppointmentOffice: orderBook | AppointmentOffice.orderBook() | 创建 PENDING 状态的 Reservation |
LibraryManager → AppointmentOffice: pickBook | AppointmentOffice.pickBook() | 查找预留副本,验证未过期,执行移动 |
AppointmentOffice → BookCopy: moveTo(USER) | BookCopy.moveTo(LibraryBookState.USER, date) | 副本从预约处转移至用户,记录轨迹 |
AppointmentOffice → Reservation: fulfill | Reservation.fulfill() | 标记预约为已完成 |
AppointmentOffice → User: addBookCopy | User.addBookCopy(copy) | 副本加入用户借阅列表 |
(orderNewBook与getOrderedBook在表中就不列出了)
在本单元包括前几个单元的训练中,我逐渐意识到大模型的力量是十分有限的,它可以高效完成一些不复杂的任务,而在复杂的场景中它几乎不太可能正确地一次性独立完成,所以如果希望大模型在复杂场景中完成架构设计任务,核心就是拆分任务,明确请求并且及时反馈/修正潜在的问题。
渐进式需求输入,分阶段收敛设计
将复杂系统拆解为基础骨架、功能增量、规则强化三个迭代,每次只让大模型关注增量变化,避免一次性输入导致设计泛化。例如先建立借还预约核心,再增加精品书架和信用分,使架构逐步演进。
明确架构风格与职责边界
提前约束大模型采用“协调者+场所容器”模式,并要求它不断审视类的职责是否单一。例如,当大模型提议将Reservation放入AppointmentOffice时,通过直接反馈“全局状态应由总控管理”的原则,使模型自行修正保持内聚。
要求输出形式化模型并紧盯约束
让大模型直接生成类图、状态图(我个人就是让它生成PlantUML的图,毕竟方便排查,且易于直接通过大模型生成),并对Guard长度、Trigger命名等评测规则做出约束。通过可视化反馈快速发现缺失的关联、错误的状态转移。
交互式纠错与设计审查
利用大模型检查代码与模型的一致性。例如当move()顺序可能导致二次移动时,通过提问“如何保证每本书仅移动一次”引导大模型给出重排步骤,再将其融入设计。对逾期扣分等逻辑缺陷,通过具体反例促使模型修正。
大模型不会为你的代码负责,它到底只是工具,只有人才可以决定自己架构的走向。对大模型有效的引导不是让大模型替代设计,而是通过分步约束、形式化表达和持续比对,将大模型的发散能力收敛到严谨的架构轨道上。
第一单元:表达式解析和化简
我的架构最开始极为的简洁,核心的对象只有Expr以及Poly,其中Poly则用一个Map管理多项式指数与系数。但是在第二次迭代指数函数的加入,我的架构就完全无法扩展,只得重构新增Mono类,于是架构就变成了一个Expr包括多个Poly,而一个Poly包括多个Mono,这样分层设计,使得第三次的迭代我只需要改变对应的部分就行,比如新增指数函数,就在Mono部分迭代就好而不必变化其他的部分。这也是让我第一次体会到,层次化的架构可以极大的保证架构的可拓展性。
第二单元:多线程电梯
本单元的核心是并发编程,主要是要保证线程间的互斥与协作。本单元三次作业中,我的架构设计思维经历了从“功能实现”到“分层解耦”,再到“状态驱动”的转变。第一次作业,我采用“电梯即线程”的单层模型,输入、调度、执行全部耦合在一个线程里,同步用粗粒度 synchronized,空闲用 sleep 轮询。这种设计仅能应付简单需求,扩展性极差。第二次作业引入独立的调度器线程,将“分配”与“运行”解耦;电梯内部引入状态机管理正常、检修等生命周期;同步则升级为 ReentrantLock + Condition,用等待/通知替代轮询。在这次作业的折磨磨练我意识到线程之间应该通过共享队列协作,而不是轮询检查。第三次作业彻底改变了我的认识——一个井道内有两个轿厢,共享物理空间和状态。我将 Elevator 进一步抽象为井道管理器,负责状态同步和 F2 互斥锁,两个 Cabin 线程只负责执行动作。同步设计走向精细化:不同资源使用不同锁,原子标志用 AtomicBoolean之类的保证,复合操作用 ReentrantLock 保护。本单元让我知晓,面对多线程编程时,关键就是辨析出互斥/协同的对象并统一进行管理。
第三单元:JML规格
与前两个单元相比,第三单元的需求已经以形式化规格给出,架构思维变成“从契约到状态”,任务变成了如何优雅的“翻译”规格。在理解JML规格约束的基础上,决定哪些查询需要缓存。例如互关数量、视频的influence等,如果完全翻译规格,时间复杂度会教你做人;如果盲目缓存,有极有可能使得新功能的迭代变得困难,比如在第二次作业中我为了缓存heat导致下一次的迭代被冗杂的方法搞得不得不重写。从中我知道了效率与可拓展性需要一定的平衡。
第四单元:UML正向建模
第四单元则把设计进一步前移到 UML建模。它的核心变成了如何正向建模架构。类图要求我确定对象之间的关系,状态图要求我明晰对象是如何变迁的,顺序图则要求我说明场景中对象是如何协作的。虽然过程略有难受,从这个单元我也知道的就是在开始代码实现前,一份全面的草稿十分重要。
在测试方面,我就主要是通过调试来一步一步的定位错误。首先根据题目构造一些基本的边界数据,然后进行测试;之后就尝试构造大量的数据进行广度的测试,如果发现错误,那么就进行减法——删除测试数据直到刚好错误不再发生,如此来定位发生错误的位置,这个方法在第四单元非常好用,除去第三单元我都是这么完成的,虽然笨但是有效。对于第三单元则比较简单,由于给出了JML规格约束,严格按照约束编写测试代码检查方法的行为是否符合JML即可。
经过一个学期的艰苦奋斗,OO也是圆满结束了。最后实在没想到颁奖典礼居然还给了我一个进步奖。怎么说呢?心情很复杂——说难受吧,获了奖;说开兴吧,每次的强侧都一塌糊涂——但还是感谢老师以及学长们的认可。从第一单元的递归下降算法到第四单元的UML,经过四个单元的磨练,从最次的角度来说,每次的格式检查让我现在代码的格式非常工整,与我大一相比,算是巨大的进步。当然不知于此,OO 这门课最直接的收获自然是在四个单元中一直在磨练的工程能力。第一单元递归下降教会了我层次化设计;第二单元我第一次解接触了多线程的编程,让我对于并发编程有了一个初步的理解;第三单元的JML规格化设计则是通过给定的规格实现方法并编写测试;第四单元的UML正向建模让我完整经历了从建模、实现到迭代的完整过程,对于正向建模在工程中的应用我也有所了解。不得不说,每个单元都给了我很深的印象,也培养了我在一些设计上的思想,比如层次化设计,抽象对象之类的。这些思想为我面对后续的软件工程乃至以后的学习都会有所帮助。OO这门课承载了无数的泪与汗水,现在是时候向他告别!