北航面向对象设计与构造(OO)课程总结与技术报告
一、 单元总结:正向建模与开发的实践与思考
本单元的核心训练目标是正向建模与开发(Forward Modeling and Development)。与以往“拿到需求直接写代码,写完再逆向画图”的开发模式不同,本单元强调“先设计、再建模、后编码”的系统化方法。
- 正向建模的流程与意义
正向建模是指将抽象的系统需求转化为结构化的模型设计(如 UML 类图、状态图、顺序图),并在模型指导下编写和组织代码的开发方式。其最大意义在于降低复杂系统的认知负荷。在代码尚未动笔之前,通过画图明确各实体的职责边界与交互关系,能在设计阶段规避许多结构性缺陷,避免后期推倒重构。正向建模的基本路径是由系统需求出发,经过抽象与职责拆分形成一阶类图,再经过精细化与语法适配形成二阶类图,最终映射到具体的 Java 代码中。 - 两阶类图在正向建模过程中的作用
一阶类图(uml_pre.mdj)—— 战略设计与职责划分
一阶类图是在未写任何代码之前,根据需求分析提炼出的系统骨架。它的主要作用是确立领域边界和核心实体。例如,在图书馆管理系统的初期设计中,我们通过一阶类图确立了 Library 控制器,以及 Bookshelf(普通书架)、BorrowReturnOffice(借还处)、AppointmentOffice(预约处)、User(学生)和 BookCopy(副本)等核心实体。此时,不需要考虑具体的集合实现(如 HashMap 还是 ArrayList)或者 Checkstyle 的行数限制,只需专注于职责分工,勾勒出核心聚合与组合关系。
二阶类图(uml_ultimate.mdj)—— 战术落地与重构适配
二阶类图是在具体代码实现过程中,对一阶类图进行相应修正、精细化后的终极设计。它的主要作用是实现设计与具体语言特性的平滑对接。在实际编码阶段,我们遇到了诸多实际约束(如 Java 的编译特性、官方提供的 com.oocourse.library3 接口、Checkstyle 的行数与复杂度限制)。为此,在二阶类图中,我们需要引入辅助管理类(如管理借阅期限与逾期判定逻辑的 BorrowedBookInfo 实体),并将过长的方法(如 executeMorningArrangement)重构并拆分为多个短小、职责专一的私有方法。这些调整最终在二阶类图中得到准确同步,保证了“图”与“代码”的一致性。二、 本单元架构设计与 UML 模型的追踪关系
在本单元的最终迭代中,系统不仅要管理复杂的图书转运逻辑,还要兼顾用户信用积分、借阅期限以及续订等动态逻辑。 - 最终代码的架构设计
本系统的物理世界与逻辑控制高度解耦,整体架构如下:
物理存储域(Locations):由 Bookshelf(普通书架)、TreasuredBookshelf(精品书架)、ReadingRoom(阅览室)、BorrowReturnOffice(借还处)和 AppointmentOffice(预约处)组成。这些类只负责维护其持有的 BookCopy 集合,不参与复杂的借阅权限判定。
领域实体域(Entities):
Book:维护特定 ISBN 号的书籍评分,并计算平均分判断是否属于“精品”。
BookCopy:封装了唯一的 LibraryBookId、当前物理状态(LibraryBookState)和该副本的历史移动轨迹。
User:封装了学生信用分及借阅、阅读信息。
BorrowedBookInfo:负责高内聚地维护单本借阅书籍的到期日(dueDate)及逾期扣分标识。
中央控制域(Controller):Library 类作为核心中转,接收外界指令并进行分发,驱动各个实体和地点状态的改变。 - 代码设计与 UML 模型的追踪关系
本系统的代码实现与 UML 模型之间存在着严密的追踪(Traceability)和一致性关系,主要体现在以下三个维度:
类图(Class Diagram)与代码的追踪:
类图中的静态结构关系在代码中通过特定的数据结构得以严格落地。例如组合关系在代码中体现为,Library 与 Bookshelf、TreasuredBookshelf、ReadingRoom 等存在强生命周期组合关系。在代码中,这些物理地点均作为 Library 的 private final 成员变量在构造时被一同创建。聚合与关联关系在代码中体现为,User 聚合并管理 BorrowedBookInfo,在 Java 编码中由 Map 集合予以实现。
状态图(State Diagram)与代码的追踪:
状态图展示了 BookCopy 的生命周期状态变迁。BookCopy 内部的成员变量 state 的取值完全对应状态图中的 6 个核心状态:BOOKSHELF、TREASURED_BOOKSHELF、APPOINTMENT_OFFICE、BORROW_RETURN_OFFICE、READING_ROOM 和 USER。状态之间的每一次转移都严格对应 BookCopy.updateState 方法的调用。在转移发生时,状态转移的约束条件(如信用分判定等 Guards 条件)在 Library 类的逻辑分支中进行判定,判定通过后才向 BookCopy 发送状态更新消息。
顺序图(Sequence Diagram)与代码的追踪:
顺序图精确描述了系统在处理特定场景时的消息流和对象交互顺序。在 Library.handlePick 业务中,顺序图绘制的消息流为:Library 询问并调用 User 的信用分并校验持有上限 canHold,随后从 AppointmentOffice 中检索并移除 ReservedBook,调用 BookCopy.updateState 改变物理状态,最后向 User 添加该借阅实体。代码中的实际调用链完全契合该时序设计,这种严密的追踪关系使得代码逻辑清晰,没有产生悬挂的未定义旁路调用。三、 大模型辅助正向建模的引导与实践
在本单元的迭代中,大模型(LLM)作为架构设计与编码实现的“副驾驶”,在辅助我们分析复杂规章制度时展现了优秀的生产力。然而,要引导大模型在复杂的图书馆场景中完成高品质的架构设计,需要讲求科学的引导策略。 - 结构化输入与规则解耦(Deconstruction)
大模型由于上下文长度和注意力限制,直接输入几千字的作业原文容易导致其生成逻辑混乱、细节遗失。在实践中,我们应当将规则进行解耦并以结构化的 Prompts 喂给大模型。
引导策略:将需求拆分为“存储实体”、“借还书限制”、“整理阶段移动路径”、“信用积分变化事件”四个独立板块。
提示词设计:
“请分析以下关于用户信用积分的规则:所有用户初试分为100。有且仅有以下5种事件会引起信用分改变(加分/扣分规则详情)。请在 User 类中设计其对应的成员属性、方法,并思考如何防范‘重复扣分’的边界条件。” - 渐进式精化与重构(Iterative Refinement)
初期大模型给出的架构可能存在类数不全、或将所有逻辑挤在单文件中的弊端。这时应当进行渐进式引导。
引导策略:从一阶的“极简实体划分”起步,随后输入 Checkstyle 的方法行数限制(不大于60行)、单行字符限制(不大于100字符)等代码质量约束,指导其进行方法的拆分。
提示词设计:
“目前的 executeMorningArrangement 方法包含太多逻辑,行数超出了 Checkstyle 的 60 行限制。请按照以下步骤(清除过期、清退阅览室、精品调配、处理预约、清空借还处)将其重构成 5 个职责专一的 private 辅助方法。” - 边界条件纠偏(Boundary Debugging)
大模型在处理高敏感的边界条件时(例如:中部评分突变引起书籍评级改变,但书籍物理上并不会在中午位移这一隐含条件)容易出现盲区。这时需要通过具体测试用例去“启发”大模型进行纠偏。
引导策略:提供测试用例的差异对比,让大模型分析为什么预期输出会和当前代码输出不一致。
提示词设计:
“在 1月5日 评分变化后,C-6748 变为了非精品图书,但此时由于还未进行开馆前整理,它依然在精品书架上。我的代码因为去普通书架寻找而返回了 null。请重构 getFreeCopyOnShelf,使其能够同时在两个书架中寻找,以规避白天评分瞬时改变带来的定位问题。”四、 面向对象设计与构造学期总结
回顾本学期四个单元的系统性训练,我们的程序架构设计能力、抽象能力以及代码规范性均得到了阶段性的提升。 - 单元训练目标达成与收获
第一单元(表达式展开):初步体验了递归下降解析器(Parser)的设计。通过将复杂的数学表达式抽象为因子(Factor)、项(Term)和表达式(Expr),理解了如何利用多态与组合模式来规避面向过程中的复杂分支判定。
第二单元(多线程电梯):深入理解了多线程并发的运行机制与安全保护。通过设计生产者-消费者模式,理解了临界区、锁机制(synchronized)以及线程间的协同调度(wait / notify),锻炼了高并发场景下的架构控制力。
第三单元(JML 规格建模):实践了契约式编程(Design by Contract)。通过严密的 JML 规格说明,领会到“规格”与“具体实现”的分离,掌握了如何利用强效的数据结构与图算法(如并查集、Dijkstra 等)去在保证前置/后置条件满足的前提下,实现性能的最优解。
第四单元(UML 正向建模):将开发视角拨高到“系统建模”的高度。不仅要求写出正确的代码,更要求用 UML 规范地描述系统的静态设计(类图)与动态行为(状态图、顺序图),体会到了设计领先于编码的正向迭代美感。 - 四个单元中架构设计思维的演进
我们的架构设计思维经历了一个从“微观”到“宏观”、从“补丁式开发”到“正向设计”的蜕变过程。
第一单元主要解决微观编码与多态适配问题,我们在第一单元开始时还带着深厚的面向过程惯性,习惯用大类套小方法的模式解决问题。
第二单元过渡到并发实体与交互设计。第二单元的电梯设计逼迫我们必须在设计之初就画出线程交互图,任何“打补丁”式的重构在多线程死锁面前都会失效。这种对控制流和生命周期的敏感度,直接反哺了后续单元的设计。
第三单元引入规格约束与契约化实现。我们通过 JML 规格,将我们的思维边界从“能跑就行”拓宽到了“符合规约”。
第四单元最终走向系统正向建模与追踪设计。我们能够非常本能地将现实世界中的地点和逻辑纽带抽象为高内聚的类,学会了在动笔写第一行代码前,先严密定义好接口的 Invariant(不变式)、Pre-condition(前置条件)以及状态机的生命周期流转。 - 四个单元中测试思维的演进
测试是软件生命周期的核心屏障,本学期我们在测试手段与测试广度上也实现了飞跃:
第一阶段(黑盒手动构造):主要依赖边界数据(如 0, 1, -1、极端嵌套括号)进行人工构造,效率低且容易遗漏。
第二阶段(随机对拍与自动化测试):针对多线程的非确定性,学会了利用 Python 编写输入生成器,进行多线程时间戳模拟,并通过大量的并发随机对拍来捕获由于并发竞态引起的偶发性死锁或数据溢出。
第三阶段(白盒契约测试与 Junit):学习了 Junit 工具,通过白盒测试的逻辑,针对 JML 规格中的每一个异常分支和正常行为编写针对性的断言测试,真正理解了覆盖率对保障核心库鲁棒性的重要价值。
第四阶段(交互式状态变迁校验):在图书馆系统测试中,由于指令序列存在时间跨度大、信用分衰减等连锁反应,我们演进出了场景化集成测试思维,即通过构造特定生命周期的输入序列,专门检测“未取失效”、“阅读不还”等时序临界点的系统状态。五、 课程收获与展望
面向对象设计与构造课程不仅仅是一门关于 Java 语言的课程,更是一次关于系统工程学、程序严密性与设计美学的硬核洗礼。从一单元表达式展开的生疏与重构,到四单元图书馆系统的游刃有余、Checkstyle 的轻松适配,我们切身体会到了工业界标准软件开发的严谨性。
感谢助教团队的高效评测与解答,也感谢这一学期在重构与 Debug 中不断成长的自己。这一阶段沉淀下来的架构设计、多线程控制、契约式编程和正向建模思维,将成为我们未来走向更高系统级开发时最坚实的基石。