BUAA-OO-JML规格总结

BUAA-OO第三单元博客总结

JML 规格

JML 是一种行为接口规格语言,用注释定义了 Java 代码中调用该方法后的行为,来约束接口类的行为。JML 主要以 Javadoc 注释的形式嵌入到 Java 代码当中。这里简略地表达 JML 常见的语法:

JML 表达式
  • 原子表达式:\result\old\not_assigned

  • 量化表达式:\forall\exists\sum

JML 规格
  • 方法规格

    • 条件

      • 前置条件:requires

      • 后置条件:ensures

    • 行为

      • 正常行为:normal_behavior

      • 异常行为:exceptional_behavior

    • 副作用限定

      • 能够修改的对象: assignablemodifiable

  • 类型规格

    • 规格变量:privatepublicstatic

    • 不变式:invariant

    • 约束:constraint

 

测试方法

作业主要测试都使用了对拍器,首先完全根据 JML 暴力编写的程序和提交到课程平台的程序进行对拍查找错误。当然也可以使用 OpenJML、JMLUnitNG、Junit 等工具进行测试。这里简略介绍 Junit 的功能。

Junit 是一个 Java 测试框架,是为了运行可重复测试,Junit 主要用于白盒测试,回归测试。核心测试方法都会加上 @Test 注解,Junit 将会把带有 @Test 的方法识别为测试方法,然后再使用 Assertion.assertEquals 检测该方法是否输出自己的期望值。但由于期望值必须由自己设定,因此我利用对拍器让数据随机生成并和其他同学一起对拍,查找 bug 的效率也比较高。

 

设计策略

第九次作业设计策略

本次作业只需要实现 PersonNetwork 的接口,在 MyPerson 类上,基本都是跟着 JML 进行编写,而 MyNetwork 类当中有几个函数暴力写的话时间复杂度会很高,因此选择了较高效的容器和适合的数据结构编写 MyNetwork ,并且在 queryBlockSum 中维护了 block 变量并使该方法的时间复杂度为

容器选择

由于这次作业没有分先后顺序因此都可以使用 HashMap 储存信息,主要利用 Personid 映射到相应的信息,例如 MyNetworkpeople 使用了 id 获取持有该 idPerson 。但对于 Network 中的 isCircle 中,HashMap 是无法快速解决的,因此本次作业我利用了并查集验证连通性。

使用数据结构

为了降低 isCircle 的时间复杂度,并查集显然有着很大的优势,在并查集查找根节点同时也实现了路径压缩。因此若遇到多次 isCircle 时,之后的 isCircle 都是以 的时间复杂度验证两节点的连通性。以下为并查集查找根节点的代码。

public int findRoot(int id) {
       Node node = network.get(id);
       if (node.getParent() == node.getId()) {
           return id;
      } else {
           node.setParent(findRoot(node.getParent()));
           return node.getParent();
      }
  }

 

第十次作业设计策略

本次作业加了 GroupMessage 接口,Message 接口的方法基本和 JML 描述的一样,在 Group 接口中的 getAgeMeangetAgeVar 可以通过维护 age 的总和和 age 的平方和降低两方法的时间复杂度,还有 getValueSum 可以通过维护 value 提高性能,维护 value 的相关方法有 addToGroupdelFromGroupaddRelation。因此本次作业提高性能最主要的方法基本上都是维护变量,调用时直接返回结果即可。

容器选择

由于 Person 接口中的 getReceivedMessages 需要将最近收到的 Message 返回,为了保持信息的有序性,我在这里使用了 ArrayList 存储信息。而其他的都一律使用 HashMap 存储。

 

第十一次作业设计策略

本次作业最大的难点一般上是 sendIndirectMessage 了,在之前的作业间接实现了邻接表,就是 MyPerson 类里的 value,所以可以透过这个邻接表利用 Dijkstra 算法将最短路径计算出来。

容器选择

为了优化 Dijkstra 算法,我利用了 PriortityQueue ,然后自定义了 Pair 并实现 Comparable 接口,将离起点最短距离的 Pair 优先级提高。然后用 HashSet 判断那个人是否已从 PriorityQueue 弹出。为了方便记录 emojiId 是否存在,也使用了 HashSet 存储信息。

Dijkstra 算法描述
  1. 先将起点插入到 PriorityQueue

  2. 将头节点弹出,并插入到 sets 记录该节点存在。若元素不存在,则将邻接表的节点都插入到 PriorityQueue 同时计算节点与起点的距离,并反复该动作知道到弹出终点元素。

  3. 弹出终点元素后返回起点至终点的最短距离。

 

Bug 解析

这三次作业主要的 bug 都是在阅读 JML 时理解有误,修了 bug 后,在强测中保持满分,互测也没被 hack 。

 

心得感想

本单元的作业难度比起前两次作业相对低了些,主要是刚接触 JML 会有些不习惯,但熟悉了 JML 的表达方式,就可以大大提升了写作业的效率,每次写作业都会先写了个暴力版后,再写个优化版进行对拍可以减少大部分的 bug 。然而在 Java 中有许多实用的容器可以使用,比起 C 语言实现起来会方便很多,因此降低了作业的难度。

posted @ 2021-05-28 10:42  alvin6012  阅读(99)  评论(0)    收藏  举报