309
社区成员
发帖
与我相关
我的任务
分享本单元的正向建模让我体会到,写代码前先画类图并不是走形式,而是逼着自己从需求出发梳理清楚各个实体之间的关系。一阶类图就像编码前的草稿,让我在动手写Java之前先想清楚图书馆、用户、图书这些核心类该怎么组织,避免边写边改导致的架构混乱。
两阶类图的设计则让UML真正贯穿了整个开发过程:一阶类图是初步设计,二阶类图则是对照代码实现后的修正与完善。相似度检查的存在,让我不能画完一阶就抛之脑后,而必须在编码完成后回头审视设计是否合理、是否需要调整,从而保证类图和代码始终一致,而不是两张皮。
采用 中心控制器 + 领域模型 架构,共 13 个类,分为四层:
┌─────────────┐
│ MainClass │ 入口,读取库存,创建 LibraryManager,调用 run()
└──────┬──────┘
│ use
┌──────▼──────────────────────────────────────┐
│ LibraryManager (中心控制器) │
│ 接收请求 → 校验规则 → 委托执行 → 格式化输出 │
│ 持有全部领域对象引用,编排所有业务流程 │
└──┬──┬──┬──┬──┬──┬──┬───────────────────────┘
│ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌─────────┐
│ Book │ │User │ │Order │ │Arranger│ │ 五个地点类 │
│ │ │ │ │ │ │(整理器)│ │bs/tbs/bro│
│ * │ │→BC │ │→BC │ │ │ │/ao/rr │
│ ▼ │ │→Ord │ │ │ │ │ │ │
│BC────│─│ │ │ │ │ │ │→BC │
│ * │ │ │ │ │ │ │ │ │
│ ▼ │ │ │ │ │ │ │ │ │
│MT │ │ │ │ │ │ │ │ │
└──────┘ └──────┘ └──────┘ └─────────┘ └─────────┘
| 层级 | 类 | 职责 |
|---|---|---|
| 入口 | MainClass | 解析库存输入,启动主循环 |
| 控制器 | LibraryManager | 接收 9 种请求指令,校验信用分/数量限制/在架状态,委托状态转移,输出结果 |
| 领域实体 | Book, BookCopy, User, Order, MoveTrace | 持有数据与状态转移方法,BookCopy 携带 @Trigger 注解 |
| 地点 | Bookshelf, TreasuredBookshelf, BorrowAndReturnOffice, AppointmentOffice, ReadingRoom | 各持有 List<BookCopy>,提供增删查 |
| 整理 | Arranger | 开馆前/闭馆后整理:清空借还处/阅览室、处理过期预约、履约待定订单、重组精品书架、扣分 |
初始值:100,上限 180,下限 0
加分:按时还书(借阅期限内)+10 / 阅读后当日主动归还 +10
减分:逾期还书 -15 / 阅读不还(当日闭馆后)-10 / 预约不取(闭馆后)-15
分级权限:
| 步骤 | Pre-open(开馆前) | Close(闭馆后) |
|---|---|---|
processOverdueBooks | ✓ | ✓ |
clearBro | ✓ | |
clearReadingRoom | ✓ | ✓ |
processExpiredBooks | ✓ | ✓ |
fulfillPendingOrders | ✓ | |
reorganizeShelves | ✓ | |
processReadingPenalties | ✓ |
| # | UML 类 | 代码文件 | 属性数 (UML/代码) | 方法数 (UML/代码) | 覆盖率 |
|---|---|---|---|---|---|
| 1 | MainClass | MainClass.java | 0/0 | 1/1 | ✓ |
| 2 | LibraryManager | LibraryManager.java | 9/9 | 25/26* | ≥60% |
| 3 | Book | Book.java | 4/4 | 8/8 | ✓ |
| 4 | BookCopy | BookCopy.java | 6/6 | 19/19 | ✓ |
| 5 | User | User.java | 6/9† | 16/16 | ≥60% |
| 6 | Order | Order.java | 5/5 | 9/9 | ✓ |
| 7 | MoveTrace | MoveTrace.java | 3/3 | 3/3 | ✓ |
| 8 | Bookshelf | Bookshelf.java | 1/1 | 6/6 | ✓ |
| 9 | TreasuredBookshelf | TreasuredBookshelf.java | 1/1 | 5/5 | ✓ |
| 10 | ReadingRoom | ReadingRoom.java | 1/1 | 7/7 | ✓ |
| 11 | BorrowAndReturnOffice | BorrowAndReturnOffice.java | 1/1 | 9/9 | ✓ |
| 12 | AppointmentOffice | AppointmentOffice.java | 1/1 | 9/8‡ | ≥60% |
| 13 | Arranger | Arranger.java | 0/0 | 10/10 | ✓ |
***** UML 中
handleQueryCredit 已随代码删除同步移除;新增orderNewBook、getOrderedBook已同步添加。
† User 中 3 个静态常量
INITIAL_CREDIT /MAX_CREDIT /MIN_CREDIT为实现细节,按 R2 规则无需全部覆盖(9 个属性 UML 有 6 个,67% ≥ 60%)。
‡ AppointmentOffice 代码比 UML 多 1 个方法
query()(与contains()等价),属轻微冗余,覆盖率仍达标。
13 条关系全部双向一致:
| UML 关系 | 代码实现 |
|---|---|
MainClass ..> LibraryManager : use | main() 中 new LibraryManager(inventory) |
LibraryManager --> Book | Map<LibraryBookIsbn, Book> books |
LibraryManager --> User | Map<String, User> users |
LibraryManager --> Bookshelf | Bookshelf bookshelf |
LibraryManager --> TreasuredBookshelf | TreasuredBookshelf treasuredBookshelf |
LibraryManager --> BorrowAndReturnOffice | BorrowAndReturnOffice borrowAndReturnOffice |
LibraryManager --> AppointmentOffice | AppointmentOffice appointmentOffice |
LibraryManager --> ReadingRoom | ReadingRoom readingRoom |
LibraryManager --> Arranger | Arranger arranger |
Book *-- BookCopy | List<BookCopy> copies(在 Book 构造中创建) |
BookCopy *-- MoveTrace | List<MoveTrace> movingTrace(在 BookCopy 中创建) |
User --> BookCopy | List<BookCopy> borrowedBooks |
User --> Order | Order activeOrder |
Order --> BookCopy | BookCopy reservedCopy |
Bookshelf --> BookCopy | List<BookCopy> books |
TreasuredBookshelf --> BookCopy | List<BookCopy> books |
ReadingRoom --> BookCopy | List<BookCopy> books |
BorrowAndReturnOffice --> BookCopy | List<BookCopy> books |
AppointmentOffice --> BookCopy | Map<BookCopy, Order> reservedBooks |
AppointmentOffice --> Order | Map<BookCopy, Order> 的 value |
状态图(ultimate_state.puml)包含 6 个状态 + 1 个起始伪状态,18 条转移,16 个 Trigger 均与 BookCopy.java 的 @Trigger 注解一一对应:
| 状态图转移 | BookCopy 方法 | @Trigger 注解 |
|---|---|---|
[*] → Bookshelf | 初始化(无需 Trigger) | — |
Bookshelf → User | borrow(date) | @Trigger(from="Bookshelf", to="User") |
TreasuredBookshelf → User | borrow(date) | @Trigger(from="TreasuredBookshelf", to="User") |
Bookshelf → ReadingRoom | read(date) | @Trigger(from="Bookshelf", to="ReadingRoom") |
TreasuredBookshelf → ReadingRoom | read(date) | @Trigger(from="TreasuredBookshelf", to="ReadingRoom") |
User → BorrowReturnOffice | returnBook(date) | @Trigger(from="User", to="BorrowReturnOffice") |
ReadingRoom → BorrowReturnOffice | restore(date) | @Trigger(from="ReadingRoom", to="BorrowReturnOffice") |
AppointmentOffice → User | pick(date) | @Trigger(from="AppointmentOffice", to="User") |
{bs, tbs} → AppointmentOffice | moveToAo(date) | 2 条注解 |
{bs, bro, rr, ao} → TreasuredBookshelf | moveToTbs(date) | 4 条注解,guard: treasured == true |
{tbs, bro, rr, ao} → Bookshelf | moveToBs(date) | 4 条注解,guard: treasured == false |
| Guard | 变量 | 所在类 | 更新时机 |
|---|---|---|---|
treasured == true | Book.treasured | Book.java | addGrade() → getAverageScore() >= 4 |
treasured == false | Book.treasured | Book.java | 同上 |
同源状态到不同目标的 moveToTbs / moveToBs guard 互斥(true vs false),满足 R2 要求。
顺序图(ultimate_collaboration.puml)聚焦预约→取书场景,3 条泳道 :User, :LibraryManager, :AppointmentOffice,19 条消息:
| 消息 | 方向 | 代码方法 | 所在类 | 可见性 | @SendMessage |
|---|---|---|---|---|---|
orderNewBook() | US→LM | orderNewBook() → handleOrder() | LibraryManager | public | from="LM", to="User" |
getCredit() | LM→US | getCredit() | User | public | — |
credit (reply) | US→LM | — | — | — | — |
hasActiveOrder() | LM→US | hasActiveOrder() | User | public | — |
result (reply) | US→LM | — | — | — | — |
hasBorrowedB() | LM→US | hasBorrowedB() | User | public | — |
result (reply) | US→LM | — | — | — | — |
setActiveOrder(order) | LM→US | setActiveOrder() | User | public | — |
| 消息 | 方向 | 代码方法 | 所在类 | 可见性 |
|---|---|---|---|---|
arrange() | LM→LM (self) | arrange() | LibraryManager | private |
addBook(copy, order) | LM→AO | addBook() | AppointmentOffice | public |
| 消息 | 方向 | 代码方法 | 所在类 | 可见性 | @SendMessage |
|---|---|---|---|---|---|
handlePick() | US→LM | handlePick() | LibraryManager | public | from="LM", to="AO"/"User" |
pick(studentId, isbn, date) | LM→AO | pick() | AppointmentOffice | public | — |
reservedCopy (reply) | AO→LM | — | — | — | — |
hasBorrowedB() | LM→US | hasBorrowedB() | User | public | — |
result (reply) | US→LM | — | — | — | — |
removeBook(reservedCopy) | LM→AO | removeBook() | AppointmentOffice | public | — |
getOrderedBook() | LM→US | getOrderedBook() | LibraryManager | public | from="LM", to="User" |
borrowBook(copy) | US→US (self) | borrowBook() | User | public | — |
setActiveOrder(null) | US→US (self) | setActiveOrder() | User | public | — |
| 维度 | 一致项 | 差异 / 说明 |
|---|---|---|
| 类图 13 类 | 类名、属性、方法、关系全部可追溯 | handleQueryCredit 已同步删除;orderNewBook / getOrderedBook 已同步新增;User 静态常量属实现细节无需覆盖 |
| 状态图 6 状态 | 全部 16 个 Trigger 与代码 @Trigger 一一对应 | guard 变量 treasured 位于 Book 类,符合 R2「任意类的成员变量」 |
| 顺序图 3 泳道 19 消息 | 全部消息名可追溯到对应类的公开方法 | arrange() 为 private 自调用,R3 无可见性要求 |
| 信用分逻辑 | 代码 7 处加减分与 problem.txt 完全一致 | 初始 100;+10 按时还书/归还;-15 逾期/预约不取;-10 阅读不还 |
| 整理策略 | 开馆前:履约订单 + 重组书架 闭馆后:处理过期 + 阅读扣分 | Arranger.arrange() 中 pre-open / close 分支清晰,流程顺序经两轮修正 |
| 9 种请求 | borrowed / returned / ordered / picked / queried / read / restored / graded / renewed + 信用分查询 | 全部在 LibraryManager.run() 中分派,LibraryQcsCmd 独立处理信用分查询 |
使用大模型辅助正向建模时,最关键的体验是需求拆解的颗粒度直接决定了输出质量。面对图书馆管理系统这种复杂场景,如果直接抛给模型"帮我设计一个图书馆类图",得到的往往是泛泛而谈的模板;但如果先引导大模型初步考虑算法、给出设计,再逐个要求模型分析其中的实体、状态与交互,就能引导它产出更贴合题意的架构。
在设计状态图时,我会先让模型根据图书的生命周期列出可能的状态,再主动引入题目中的限制条件(如"一次整理每本书只能移动一次""预约保留5天"等)让它修正转移条件;生成类图后,再对照关键词库检查覆盖率,要求补充缺失的核心类或方法。这种"先发散后收敛"的引导方式,比一次性索取完整方案更能得到可用的设计结果。
同时,我也学会了对模型输出保持批判性:大模型偶尔会"脑补"题目不存在的功能或忽略隐式约束,因此必须逐条核对业务规则,把修正指令明确反馈给模型,才能确保最终设计与评测要求一致。也会让多个大模型互相找对方生成的结果文件的问题,彼此核对,可以增强准确性,以及使用上下文记忆更长的大模型。
在这一系列课程中,我的架构设计思维经历了从微观分解到中观协作,再到契约约束,最后迈向宏观抽象的层层递进。
第一单元的表达式求导训练,让我建立了层次化分解与“高内聚、低耦合”的初始直觉。面对复杂的嵌套表达式,我学会了运用递归下降思想,将问题拆解为“表达式-项-因子”的清晰层级。这阶段的架构思维集中在寻找稳定的抽象模型:如何将求导规则封装在各自的类中,而非集中在一堆 if-else 里;以及如何通过定义统一接口,让变化(如新增函数或求导规则)仅局限于新子类中。我初步体会到,好的架构能让扩展变得像搭积木一样自然。
进入第二单元的多线程电梯系统,我的思维从静态的类结构,扩展到了动态的模块协作与状态管理。这里最大的思维转变是学会了在架构层面应对并发行为和对象生命周期。我必须将“调度策略”从“电梯状态机”中剥离,通过生产者-消费者模式解耦乘客请求与电梯执行,这正是关注点分离在并发场景下的实践。而采用状态模式来建模电梯在“正常-检修-双轿厢改造”等流程间的转移,则让我深刻理解了封装变化的另一层含义:不是封装算法,而是封装复杂的状态转换规则,确保在高并发下线程安全的“设计正确性”胜过一切。
后两个单元则让架构思维从“能运行”升华到“可验证、可设计”。第三单元的JML规格化编程,将思维扭转为面向契约设计:架构的骨架不再只是自己设计的类图,更是那一套严格的接口规格。我学到,清晰定义前置、后置条件与不变式,本身就是一种约束下的架构设计,它能自然引导出职责单一的模块,并强制代码与测试都围绕明确的承诺展开。第四单元的图书馆管理系统与UML正向建模完成了从“写代码”到“画蓝图”再到“代码对齐蓝图”的思维闭环。我学会了在编码前主动运用类图、状态图、顺序图去可视化系统的全局结构、触发流程和对象交互,这逼迫我做架构决策时更前瞻、更审慎。从最初凭直觉分解问题,到最后先建模再编码,我掌握了在抽象层面推演并验证设计的方法,真切理解到架构是权衡后的全景视图,而非仅仅是代码的附属说明。
在这一系列课程中,我的测试思维经历了从验证结果到验证行为,再到验证契约,最后走向验证设计的逐步深化。
第一单元的表达式求导,测试思维还停留在最朴素的“黑盒结果验证”阶段。我关注的核心是输出的表达式是否正确、是否足够简练。这种测试是单维度的,仅围绕最终字符串做断言,构造几个典型输入,比对预期输出即可。现在回想,这时的测试是“事后检验”,它的局限在于,即使测试通过,也无法保证内部结构是灵活的,更无法捕捉到递归栈溢出或中间计算的潜在错误。
第二单元的多线程电梯系统,让我的测试思维被迫升级为“并发行为与过程正确性的验证”。多线程下的竞态条件、死锁和时序依赖,无法用单一的最终状态来覆盖。我必须构造并发场景,去观察线程交互的中间过程:比如资源分配是否安全、wait-notify协作是否按预期唤醒线程、状态转移是否在规定时间内完成。测试不再只问“结果对不对”,而是开始追问“过程对不对”,这让我意识到可观测性设计对于并发测试的重要性。
后两个单元则让测试从经验驱动转向了规格驱动和设计驱动。第三单元的JML规格化编程,彻底重塑了我的测试观:测试不再是凭感觉枚举边界,而是严格按照契约。我必须为每个方法的前置条件、后置条件、异常行为和assignable副作用设计测试用例,这逼迫我追求语义级别的完全覆盖,让我理解了什么是“测试即文档”。到了第四单元,测试维度再次升维:除了代码功能的正确性,我还得“测试”我的UML设计是否与代码一致。两阶类图的相似度检查、状态图的触发条件与代码@Trigger的对齐、顺序图的消息顺序与真实调用栈的匹配——这本质上是架构级的回归测试,确保设计意图没有在编码中走样。至此,我的测试思维已经从“找代码的错”演进到了“找设计思路与最终实现之间的偏差”。
不得不说,当同学都使用大模型、有更多空闲的时间去打冯如杯等竞赛的时候,如果手敲代码所使用的时间实在是过于奢侈。时代的洪流下、紧张激烈的环境中,不只是我,每位同学应该都很难完全不图成绩、只为学到面向对象知识地,手敲代码、钻研概念。
所以在这样的压力下,其实我也被迫努力研究了怎样才能让AI更好用。从最开始的只用豆包,发现豆包上下文能力的局限,到广泛尝试gemini、deepseek、kimi、GPT、copilot,到claude接入deepseekAPI,到添加一些skill,才发现自己已经走了很远很远。即使没有学到那么多面向对象的知识,但是在摸索和交流中学到了见证了很多AI的时代发展、人工智能领域的发展状况,怎么不算一种更前沿更实际的收获呢。过程中的苦难,claude不允许注册新的账号、GPT便宜的渠道被封锁、copilot学生认证后也不再赠送会员,也渐渐被时间冲淡了。
还有,不得不感慨电梯单元的多线程、死锁,我当时并没有很会使用AI,到最后死活解决不掉,还是几乎原原本本地把这单元的知识啃下来了。这让OS考试中的PV操作得心应手,不得不感恩OO的课程设计之精巧。
期待递归下降的学习让下学期的编译课程变得优美,有一个高视野和近乎完美的初始架构设计。
还误打误撞地成为了OO的助教……真的说来惭愧,实在U3、U4连题都没有完整地读和理解,最后的小测也感觉是我非常应该会的内容但是实在错得稀碎。本来感觉在OO课程中吃了太多苦,有心理阴影了,不要再见了,但是善良的uho一直在鼓励我,力图打消我一些关于OO的顾虑(天啊,说到这又想起我周二崩溃地和心理咨询师发疯,出了咨询室给uho发消息想问问我的电梯哪里写错了能不能提供一些帮助,他很耐心很详细地帮助我……泪目,uho老师当时似乎课业压力也很大)后来纠结良久,想着把决定再往后拖拖,实在不行不去面试了呢?就还是报名了问卷。后来想着,实在不行人家不愿意要我当助教呢?就还是去了面试。在G315坐下之后,在拿到毫无头绪的问题后,在吴老师出现之后,可能出于想证明自己的心,又努力多思考了许多。可是吴老师在认真的记录每个同学的意见啊……可是我的发言让老师很惊艳啊……可是我可以自信地说“我只想成为宣传助教”啊……我可能就是这样积极性差的人吧,可能就是被架起来“来吧来吧”我就顺坡下了吧,可能我就是被夸两句就愿意无限地奉献吧……
另外再感恩在纪老师班上课,老师真的很幽默很亲切很有趣很生动!!!
若未受其苦,则不知其深远。认清生活的真相后,仍然热爱生活。
OO,我们来日方长!