308
社区成员
发帖
与我相关
我的任务
分享第四单元要求先画一阶类图(uml_pre.mdj),再写代码,最后修正为二阶类图(uml_ultimate.mdj)。这种"先设计后实现"的流程与前三单元"拿到规格直接写代码"截然不同。
一阶类图的作用是"骨架设计"。在还没写代码时,我需要思考:图书馆有哪些核心实体(书、用户、书架)?它们之间的关系是什么?谁来管理移动?这种思考迫使我先理清类之间的职责边界——比如"信用分管理"是放在 User 里还是抽一个 CreditManager?一阶类图阶段我选择了后者,因为信用分的阈值、增减规则是一套独立逻辑。
**二阶类图的作用是"修正与对齐"**。写完代码后回头修正类图,我发现一阶设计中有不少遗漏:BookCopy 需要 dueDate 字段(借阅期限),User 需要 currentReading 字段(阅读状态),LibraryManager 需要多个 arrange 辅助方法。这些是在编码过程中才暴露出来的细节。
两阶类图的核心价值在于:一阶定方向,二阶校细节。一阶防止"想到哪写到哪"的混乱,二阶保证"图与代码一致"。R5 的关联关系检查(代码有引用则类图必须有线)尤其体现了这种一致性约束。
本单元的核心架构:
Main → LibraryManager → { Bookshelf, TreasuredBookshelf, ReadingRoom,
BorrowAndReturnOffice, AppointmentOffice }
→ CreditManager
→ Map<Isbn, Book> → BookCopy
→ Map<String, User>
设计要点:
追踪关系对比:
| UML 元素 | 代码实现 | 一致性 |
|---|---|---|
| LibraryManager → Bookshelf (1:1) | private Bookshelf bookshelf | ✓ |
| Request ← BorrowRequest 等 9 子类 | extends Request | ✓ |
| Book → BookCopy (1:1..*) | List<BookCopy> copies | ✓ |
User 属性 credit | private int credit | ✓ |
CreditManager 方法 canBorrowOrOrder() | credit > 80 判定 | ✓ |
UML 类图最大的价值是让人一眼看清类之间的依赖关系。不用读代码,看类图就知道 LibraryManager 依赖 7 个地点类和 User/Book 两类数据。
本单元我使用大模型辅助 UML 绘制和代码调试。
有效的使用方式:
踩过的坑:
int credit → name=int, type=credit),需要 Python 脚本修正date > dueDate 还是 date >= dueDate),需要人工仔细阅读规格引导大模型的策略:
| 单元 | 主题 | 核心设计思维 |
|---|---|---|
| U1 | 表达式解析 | 递归下降 + 数据结构抽象(TermBasis),学会"用什么数据结构表示复杂对象" |
| U2 | 多线程电梯 | 生产者-消费者 + 锁粒度控制,学会"并行系统的职责划分与同步" |
| U3 | JML 社交网络 | 规格驱动开发,学会"先定契约再写实现",数据结构优化(HashMap 代替遍历) |
| U4 | 图书馆 UML | 正向建模 + 两阶类图,学会"先设计架构再编码,图示化设计思维" |
演进趋势:从"实现一个算法"→"设计一个并发系统"→"按照规格实现"→"自行设计架构并建模"。U4 是前三个单元的集成:需要自己定义类(U1 的抽象思维)、需要处理状态迁移(U2 的并发思维)、需要精确满足规格(U3 的契约思维)。
U1(表达式解析):手动构造边界测试 + 互测 hacking。测试方式是"猜别人哪里会出错"。测的是正确性和性能(TLE)。
U2(多线程电梯):随机测试 + 死锁检测。多线程的 bug 不是稳定复现的,学会了跑大量随机线程调度来暴露竞态。测的是正确性和线程安全。
U3(JML 社交网络):JUnit 白盒测试。从 JML 规格直接翻译出测试用例。还学会了构造极端数据(10^5 条边)测性能。测的是规格一致性和时间复杂度。
U4(图书馆系统):交互式评测 + 随机自动化测试。评测机动态生成还书/取书/续订请求,要求程序输出必须与自身状态一致。我写了 Python 测试脚本,自动生成随机图书馆场景并验证业务规则(信用分、逾期、借阅限制),帮助快速发现逻辑漏洞。
演进趋势:从"自己构造测试"→"随机压力测试"→"规格翻译测试"→"交互式自动化测试"。测试从手工到自动,从局部到系统级。
面向对象的核心是"职责分配"。四个单元下来,最大的感悟是:不是把代码写成类就是 OO,而是每个类有清晰的职责边界,类之间的协作方式体现了系统架构。U4 的 LibraryManager 如果不拆出 CreditManager,所有信用逻辑堆在一个类里,就是伪 OO。
规格与实现分离。U3 的 JML 和 U4 的 UML 都在强调"先说清楚要做什么,再动手做"。这种思维对大型项目尤其重要——需求变了的代价远大于实现错了的代价。
测试是设计的一部分。U2 的随机测试让我意识到,好的测试框架本身就是一个迷你系统。U4 的交互式评测让我学会了用代码验证代码——评测机的动态请求生成对程序逻辑的完整性和一致性提出了更高要求。
大模型是好的辅助,但不是替代。大模型帮我找到不少bug,但有一些关键 bug(逾期罚分时机、overduePenaltyApplied 标记跨用户污染)需要人工分析才能发现。AI 擅长"加速已知流程",不擅长"发现未知问题"。
迭代优于一次到位。两阶类图的设计哲学——先画一阶、写代码、再修正二阶——本身就是迭代思维的体现。U1 的 TermBasis 抽象也是从 HW1 到 HW2 迭代出来的。