面向对象第三单元总结

面向对象第三单元总结

第三单元的重点是JML语言和规格。三次作业都采用了形式化的语言进行描述。

1.JML语言的理论基础、应用工具链

  • 理论基础

    ​ 定义:JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。JML是一种行为接口规格语言(Behavior Interface Specification Language,BISL),基于Larch方法构建。BISL提供了对方法和类型的规格定义手段。所谓接口即一个方法或类型外部可见的内容。

  • 基本语法及关键词

关键词 含义 例子
\result 表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值 ensures \result == false;
\old(expr) 表示一个表达式expr在相应方法执行前的取值,该表达式涉及到评估expr中的对象是否发生变化 ensures \old(getGroup(id2).people.length) == getGroup(id2).people.length + 1;
\not_assigned(x,y,...) 用来表示括号中的变量是否在方法执行过程中被赋值。如果没有被赋值,返回为true ,否则返回 false (\forall int i; 0 <= i < groups.length; \not_assigned(groups[i]));
\not_modified(x,y,...) 该表达式限制括号中的变量在方法执行期间的取值未发生变化
\nonnullelements(container) 表示container对象中存储的对象不会有null public instance model non_null Person[] people;
\type(type) 返回类型type对应的类型(Class)
\typeof(expr) 该表达式返回expr对应的准确类型
\forall 全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。 (\forall int i; 0 <= i && i < acquaintance.length; acquaintance[i].getId() != person.getId());
\exists 存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。 (\exists int i; 0 <= i && i < acquaintance.length; acquaintance[i].getId() == person.getId());
\sum 返回给定范围内的表达式的和 (\sum int j; 0 <= j && j < people.length && people[i].isLinked(people[j]); people[i].queryValue(people[j])));
\product 返回给定范围内的表达式的连乘结果
\max 返回给定范围内的表达式的最大值
\min 返回给定范围内的表达式的最小值
requires 前置条件,对方法输入参数的限制 requires contains(id1) && contains(id2) && id1 == id2;
ensures 后置条件 ensures \result == true;
public normal_behavior 正常行为
public exception_behavior 异常行为
signals (Exception e) b_expr 强调满足某个条件抛出相应异常 signals (PersonIdNotFoundException e) !contains(id1) || !contains(id2);
  • 工具链

    openjml:使用SMT Solver来对检查程序实现是否满足所设计的规格。
    jmlunitng:根据规格自动化生成测试样例,进行单元测试。

2.部署openjml并验证


​ 在本地运行了openjml后出现了一些版本问题,最后没有解决,只是出现了报错的现象。

3.部署jmlunitng并验证


​ 可以看出,jmlunitng主要是对各个方法参数的边界情况进行测试,如int型参数的最大值和最小值,以及其他引用的null值等等。

4.架构设计

  • 第一次作业
    第一次作业我完全按照规格进行书写,但是对于复杂度的计算出现了错误,在iscircle方法中没有采用数组标记的方法,导致超时。这次作业的容器基本都是采用Arraylist。

  • 第二次作业
    第二次作业我修改了很多模块的代码,但是对于复杂度的掌握还是没有达标。首先我将容器全部改为Hashmap,减少查询所需的时间,其次优化了iscircle方法。但是我没有设计关于grouprelationsum和groupConflictSum等的暂存变量,所以每次查询都需要遍历,相当耗费时间,以致超时。

  • 第三次作业
    第三次作业在保证代码正确性的情况下,我尽量提高了性能。我把iscircle方法调整为采用并查集的方式进行查询。对于queryMinPath方法,我采用了优化的dijkstra算法。对于queryStrongLinked方法,我采用了每次mask一个点寻找路径,以此查看是否有关键点的方法。

5.bug以及修复

  • 第一次作业
    在找出bug所在后,在iscircle方法中加入了Hashset以进行标记,解决了超时的问题。

  • 第二次作业
    将group类中getConflictSum、getValueSum、getAgeMean、getAgeVar方法改为查询已经计算好的暂存值。在addperson方法中加入计算的过程。

  • 第三次作业
    虽然强测没有错误,但是在完成作业时,queryStrongLinked方法出现了很多问题。最开始采用的tarjan算法,由于我理解不透彻,得出了错误的答案。然后我改为了双dfs,发现算法本身有问题,最后才改为寻找关键点的算法。

6.心得体会

​ jml规格非常适用于需求方向程序员提供要求。形式化的语言可以精准描述出类或者函数的作用。良好的契约性使得设计和实现恰当地分离,同时,形式化语言也为形式化验证提供了非常好的基础。在出现bug时,可以比较容易分辨出到底是设计的问题还是实现的问题。
​ jml规格只是提供了一种方式或者是所要达成的目的,而不是唯一途径。所以在书写代码时,要先理解其意图,再通过好的手段进行实现。
​ jml的格式比较严格,在书写时稍显不便,而且对于复杂逻辑的描述很容易出现问题。因此,我认为自动化辅助书写jml工具是必要的。
​ 我更倾向于使用jml规格、解释性注释和说明文档结合的方式说明代码目的和意图。

posted @ 2020-05-22 22:40  歌未竟  阅读(162)  评论(0编辑  收藏  举报