oo第三单元作业博客总结

卓沁文-zc061011 2026-05-28 21:29:05

Unit3 规格编程与迭代开发总结

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

1. JML 核心认知

JML是一种专门用于对 Java 程序进行行为规格描述的形式化语言,它通过严格的数学化语法,替代自然语言来定义程序的功能约束、状态限制与行为边界。在实际开发中,JML 最核心的作用就是把模糊不清、容易产生歧义的需求,转化为可阅读、可校验、可传递的程序契约。

JML 的核心语法围绕程序的状态与行为展开,主要包括:

  • requires:方法执行前必须满足的前置条件,不满足则方法不执行或抛出对应异常;
  • ensures:方法正常执行结束后必须满足的后置条件,是对返回值与对象状态的严格承诺;
  • invariant:类在整个生命周期内必须始终保持的不变量,任何时刻都不能被破坏;
  • \forall\exists 等量化表达式:用于描述集合、数组等批量数据的约束关系。

在我看来,JML 本质上不是编程语言,而是沟通语言与约束语言。它让开发者、测试者、协作人员对同一个功能拥有完全一致的理解,避免“我以为、我觉得”这类主观理解带来的实现偏差。

2. 规格驱动开发(SDD)的理解与实践

理解:规格驱动开发是以 JML 规格为核心的开发模式,它完全改变了传统“先写代码、再补文档”的开发流程,强调规格先行、实现跟进、测试对标、重构受控

在这种模式下,所有工作都围绕规格展开:

  • 需求确定后,先编写完整 JML 规格,而不是直接写代码
  • 编码阶段只做一件事:让代码严格满足 JML 的所有约束,不额外增加逻辑,也不随意简化需求
  • 测试用例完全依据 JML 设计,确保前置、后置、异常、不变式全部覆盖
  • 需求变更时,必须先修改 JML 规格,经过确认后再修改代码实现

规格驱动开发最大的价值,是让程序行为可预期、可验证、可维护。尤其在多人协作、多次迭代的项目中,能有效避免需求扩散、逻辑混乱和隐性 Bug。


二、JUnit 测试经验总结

1. 测试用例设计的核心原则

在 Unit3 的作业中,JUnit 测试不再是简单验证功能是否“跑通”,而是严格验证代码是否完全符合 JML 规格。经过多次实践,我总结出以下关键原则:

  • 必须完整覆盖 JML 中所有 requires 约束,既要测合法输入,也要测非法输入,确保异常正确抛出
  • 必须覆盖所有边界条件,包括空集合、0 值、最小值、最大值、唯一元素、重复元素、互斥关系等
  • 必须覆盖所有要求抛出的异常类型,并且校验异常抛出的顺序是否符合规格要求
  • 必须使用精准断言,直接验证 ensures 描述的结果,不依赖控制台输出做人工判断
  • 必须保证测试无副作用,方法执行后不应修改任何不该被修改的数据状态

2. 实际测试中的方法与技巧

  • 对每个方法进行隔离测试,不依赖业务流程,避免一个方法出错导致整个测试失败
  • 构造多组测试数据:常规数据、极端数据、临界数据、重复数据,全面覆盖行为分支
  • 对推荐、查询、统计类方法,多次调用验证结果一致性,确保无随机、无状态污染
  • 对关系维护类方法(关注、取关、投币、点赞),测试前后对比对象状态,防止误修改
  • 使用参数化测试批量处理多组输入,大幅提升测试效率与覆盖范围

三、三次作业迭代过程分析

1. 三次作业整体迭代脉络

Unit3 的三次作业呈现出从基础到复杂、从功能到性能、从单对象到关系网的清晰迭代路径:

  • 第一次作业:实现最基础的用户管理、视频上传、关注关系、观看/点赞等核心功能,以简单增删改查为主,容器以基础的 HashMap、ArrayList 为主;
  • 第二次作业:新增投币、评论、转发、贡献值统计、垃圾评论清理、粉丝年龄分布等功能,逻辑复杂度提升,开始出现关联查询与批量处理;
  • 第三次作业:加入推荐算法、最热视频、最具影响力 UP 主、最短路径、最长递减序列、全局贡献者统计等复杂计算,对算法效率、容器设计、图结构处理提出高要求。

每一次迭代都不是简单新增功能,而是对原有结构的扩展与约束强化,也迫使我不断重构方法、优化容器、提升性能。

2. 如何发现方法与容器在迭代中的变化

在迭代过程中,我主要通过以下方式识别方法与容器的适配性变化:

  • 对比新旧版本 JML 规格,观察 requiresensuressignals 的增减,判断方法行为是否扩展
  • 跟踪新增指令的依赖关系,识别哪些原有方法被高频复用,判断是否需要重构
  • 分析用户、视频、关注、评论、贡献之间的关联变化,判断数据结构是否需要升级
  • 统计查询、遍历、修改的操作频率,判断当前容器是否满足新的性能要求
  • 通过代码依赖分析,定位被多处调用的核心方法,优先优化与稳定化

3. 如何发现程序的性能瓶颈

随着数据量增大,程序很容易出现超时问题,我主要通过以下方式定位瓶颈:

  • 构造大数据量测试用例,直接观察哪些方法导致超时,定位高耗时模块
  • 分析代码时间复杂度,重点关注嵌套循环、线性遍历、频繁 contains 判断等 O(n) 操作
  • 检查容器使用是否合理,例如用 ArrayList 做频繁查询会明显慢于 HashSet/HashMap
  • 在关键方法入口与出口添加耗时日志,直观看到执行时间分布
  • 观察重复计算问题,例如多次遍历同一个集合统计结果,可通过缓存优化

四、程序出现过的 Bug 以及原因分析

1. 典型 Bug 与表现

在三次作业迭代中,我遇到过多种典型 Bug,覆盖功能、状态、异常、性能等多个方面:

  • 互相关注数量统计错误:关注与取关时,未正确判断双向关系,导致计数加减异常
  • 未观看视频可以直接点赞、投币:遗漏 JML 中必须先观看的约束,状态校验不完整
  • 推荐 UP 主列表包含自己或已关注用户:候选集合过滤逻辑不完整,未严格按规格排除
  • 垃圾评论清理统计错误:遍历评论时直接删除,导致索引错乱,数量与关键词出现次数计算错误
  • BFS 最短路径出现死循环或漏节点:距离字典初始化不完整,访问标记逻辑错误
  • 最热视频、最佳贡献者同分处理错误:未按 ID 最小规则排序,结果不符合规格要求
  • 异常抛出顺序错误:先检查了后序条件,未按 JML 要求的优先级抛出对应异常

2. Bug 出现的根本原因

总结所有 Bug,根源主要集中在以下几点:

  • 容器选择不合理,只考虑功能实现,未考虑查询、修改、遍历的效率
  • 对象状态维护不同步,例如关注列表与粉丝列表未同时更新,导致数据不一致
  • 缺乏对空集合、空数据、极值、重复值的处理,代码只适配正常场景
  • 同分、同值、同贡献的比较规则理解偏差,未严格遵循 ID 最小的约定
  • 性能意识不足,前期只追求功能正确,未提前优化高频操作

五、大模型在规格驱动开发中的使用体验

1. 大模型在规格开发中的优势

在 Unit3 作业中,我大量使用大模型辅助开发,它在以下方面带来明显效率提升:

  • 快速翻译复杂 JML 规格:把 \forall\exists 等难懂的量化表达式转化为清晰自然语言逻辑
  • 自动生成方法代码骨架:根据 JML 直接生成参数校验、异常判断、返回值结构,减少重复编码
  • 辅助完善 JML 约束:帮助检查不变式、前置条件是否遗漏,提升规格完整性
  • 快速生成基础测试用例:根据方法行为自动生成正常、异常、边界三组测试模板

2. 大模型存在的明显问题

但在使用中我也发现,大模型并不能完全替代人工思考:

  • 容易忽略边界条件:对同分、同值、空集合、异常顺序等细节处理不够严谨
  • 容易忽略部分题目条件

3. 使用大模型进行基础单元测试

我常用大模型辅助生成 JUnit 测试,流程非常简单:

  • 输入:方法签名 + 对应的 JML 规格
  • 指令:生成完整 JUnit 测试,覆盖正常用例、异常用例、边界用例,并写出精准断言
  • 输出:可直接运行的测试代码,只需少量修改即可使用

这种方式能大幅减少手写测试用例的时间,同时提升覆盖完整性。


六、JML“击鼓传花”游戏感悟

1. 在游戏中发现的 JML Bug

在击鼓传花过程中,我发现了大量 JML 编写问题:

  • 语法不规范:变量未定义、括号不匹配、符号使用错误,导致后续无法阅读;
  • 逻辑错误:\forall 范围写错、不变式缺失、约束条件矛盾;
  • 约束不完整:遗漏 ID 唯一性、关系互斥、状态不变量等关键限制;
  • 异常条件模糊:没有明确什么情况抛出什么异常,导致实现混乱。

2. 传递过程中需求与边界的变化

随着传递次数增加,需求出现明显偏移:

  • 原始需求的细节不断丢失,边界条件被不断弱化
  • 不同人对同一句 JML 的理解出现偏差,导致实现方向逐渐偏离
  • 异常规则、比较规则、边界值处理等隐性需求,在传递中几乎完全丢失

这充分说明:自然语言不可靠,不完整的 JML 同样不可靠

3. 多人组队编程的改进措施

通过这次游戏,我深刻意识到团队协作必须建立严格规则,以减少信息差:

  • 统一规格先行:任何功能开发前,必须先编写完整 JML,全员评审通过后再编码
  • 提供示例用例:用具体输入输出说明需求,避免纯文字理解偏差
  • 制定统一规范:明确容器选择、异常抛出顺序、命名规则、同分处理策略
  • 建立同步机制:每日短会同步进度、疑问、风险,及时消除理解差异
  • 模块化分工与对接:按 JML 模块划分任务,对接时只依赖规格,不依赖个人实现
  • 使用工具校验:借助 OpenJML 等工具自动检查 JML 语法与逻辑,提前发现错误

七、总结

Unit3 的规格驱动开发,让我真正理解了“契约式编程”的意义。JML 不仅是一种语言,更是一种严谨的开发思维,它强迫我们用精确、无歧义的方式定义需求,用可验证的方式实现程序,用标准化的方式进行协作。

三次迭代让我明白:程序不仅要功能正确,还要状态稳定、性能高效、易于扩展。而测试、规格、容器设计、算法优化,都是高质量程序不可缺少的部分。

未来在团队开发中,我会坚持规格先行、测试驱动、规范统一的思路,用严谨的流程替代主观经验,用明确的契约减少沟通成本,写出更稳定、更健壮、更易维护的代码。

...全文
41 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

307

社区成员

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

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