BUAA OO 第三单元总结

周健安-24371433 2026-06-01 17:24:55

BUAA OO 第三单元总结

本单元的的任务要求是模拟 B 站的用户关注、视频互动等功能。

第一次迭代作业先完成用户、视频和关注关系的基础建模,第二次作业在已有关系之上加入观看、点赞、投币等互动行为和经济系统,第三次则进一步加入异常处理和评论管理。

和前两个单元相比,由于官方包提供了大部分类的接口,本单元的作业没有太多可以自由发挥的空间。只能在 JML 提供的约束下选择适合的数据结构来实现它。

我这三次作业的总体迭代路线大致是:

HW9: 搭建 UserVideoNetwork 类的基础骨架,实现用户和视频管理、关注关系维护,以及基于 BFS 的最短路径查询。

HW10:HW9 的基础上扩展用户状态和视频状态,加入硬币、观看记录、点赞记录、视频分区和各种交互计数器,同时实现基于 DAG 的最长年龄递减序列。

HW11: 强化异常处理和评论管理,尤其是在垃圾评论清理中需要严格按 JML 规格处理重叠匹配,并维护清理后的评论集合。

一、我对 JML 和规格驱动开发的理解

在我的理解中,用一句话概括 JML,就是把“方法应该做什么”和“方法是怎么实现的”进行了解耦。

之前写代码时往往我会根据题意、样例,直接凭直觉写代码,然后靠调试和补丁把它修对;但这个单元我需要先关心规格是如何给出的。一个方法能不能调用,要看 requires;正常返回后状态应该变成什么样,要看 ensures;哪些对象允许被修改,要看 assignable;如果条件不满足,又要按规格给出的顺序抛异常。这种感觉和之前单纯按自然语言理解需求很不一样。

如今我对规格驱动开发的理解是:规格先给出正确性的边界,实现再在这个边界里选合适的结构。比如 JML 规定了某个查询最后必须返回什么,但它不会替你决定底层到底该用数组、链表还是哈希表。也就是说,JML 解决的是“程序必须是什么样”,而容器和架构解决的是“程序怎样才能既正确又跑得动”。这一点在本单元里特别明显。

二、代码结构与三次作业迭代

这三次作业虽然功能不断增加,但主结构其实一直比较稳定。我整体上还是围绕 Network + User + Video 来组织代码,用 Network 负责全局管理和跨对象逻辑,用 User 保存用户自己的关系和行为状态,用 Video 保存视频自身的属性和统计信息。

  • 第一次迭代的重点主要是把基础容器选对。全体用户和视频我都放在 HashMap 里,这样按 id 查询和判断存在性都能保持在比较低的复杂度。最短路径查询这部分则是按需做 BFS。

  • 第二次迭代,是复杂度 gap 最明显的一次。我们需要同步维护多处状态。比如点赞和投币不只是修改视频的计数器,还可能影响用户的记录、硬币数量等等。写这次作业时重点是需要注意到代码前后的联动关系。

  • 第三次迭代在算法上的核心其实是字符串匹配。由于模式串长度很小,匹配算法这一块我没有使用 $\mathcal{O}(n + m)$ 的 KMP 算法,而是使用了 $\mathcal{O}(nm)$ 的暴力匹配。这在运行结果上的体现并明显,大家的时间都差不多。

三、JUnit 测试经验

曾经我写 JUnit 的思路还是比较原始的,比较接近构造几个输入然后看返回值对不对。对于上学期先导课中的覆盖率测试,这足够了。但对于这单元的 JUnit 特殊测试,这还远远不够。

所以后面我的测试方式逐渐分成了几个层次。第一层还是手工场景测试,用少量对象构造基本功能和简单异常,先保证主流程能跑通。第二层是做状态快照,在调用方法前后记录关键属性,检查那些不应该被修改的部分是不是保持不变。这个做法主要是验证 pureassignable 。第三层则是把数据构造和规格校验分开:一边用类似 JmlOracle 的方式把 JML 的前后置条件翻译成 Java 判断,一边用固定随机种子的随机数据去生成各种边界情况和操作序列,再交给校验器做高强度验证。

四、我的 bug 与原因分析

这三次作业里,我碰到的 bug 主要有几类。

第一类是接口和底层实现错位。比如在把一部分存储从数组思路转向 HashMap 之后,前期有些旧代码没有及时同步调整。这个 bug 表面上像是小疏忽,本质上还是迭代修改不彻底。

第二类是对 JML 细节理解不够精确。最典型的就是 cleanSpamComments 里关键字出现次数的统计。我一开始进行的是非重叠匹配,后来回头看规格才意识要求的是重叠匹配。

第三类出现在JUnit本身。一开始我默认清理后的评论必须保持原有相对顺序,于是在测试里把顺序也断言死了。但实际上 JML 只要求保留元素的集合一致性,并没有强制顺序。

五、关于 Code Agent 的使用体会

这次 Unit3 我使用了 Codex 来辅助完成一部分工作。我的感受是,它在规格驱动开发里确实有帮助,尤其适合做一些“从文字到结构化检查点”的转换。比如读 JML 时,它可以比较快地帮我把一个方法拆成前置条件、返回条件、异常路径和状态变化点;在写测试时,也能辅助整理边界情况和可能遗漏的检查项。

不过它的局限也很明显。大模型根据 JML 写代码时,通常更容易优先保证“语义上像是对的”,但不一定会主动考虑效率、容器选择或者后续迭代时的可维护性。如果不专门去问复杂度和架构问题,大概率它会给出一个逻辑正确、但性能很差的实现。

所以我现在比较认可的用法是:把它当作辅助阅读、辅助整理、辅助测试的工具,而不是替自己做最终判断。它可以帮我节省一些机械性的时间,但规格到底怎么理解、结构到底怎么设计、这个优化值不值得做,最后还是得自己拍板。

六、研讨课感悟

本次活动中我给出的题目是判断是否图中任何两个人之间都互相是直接好友。前置条件是朋友关系集合不为空集。不满足时抛出异常。

最后的结果是:发现的 JMLBug 数不胜数,几乎没有被成功传递到底的题目。其中大部分来自于自然语言和 JML 相互转换的过程中发生的条件丢失。也有自然语言语义模糊无法精准写成 JML 后被随意增加语义解释的情况。

在未来的多人开发中,为了提升协作效率,可以采取以下措施:

  • 严格定义初始需求。动手编程前必须严谨地进行形式化定义,写出严格的前置后置条件、异常分支和副作用范围。
  • 统一接口规范。提前列出可能用到功能的接口,避免信息在传递过程中失真。
...全文
44 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

308

社区成员

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

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