OO第三单元总结

这一单元,主要考察了对JML的理解,以及各种容器的选择以及数据结构相关知识。

一、JML的理论基础和相关规范

JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。告诉编程者函数的具体功能是什么,相比文字描述而言,更具有逻辑性。同时他是一种规范的统一的规格语言,能够让阅读代码的人快速的理解,不存在歧义。

1、注释结构

requires子句定义该方法的前置条件(precondition)。

assignable列出这个方法能够修改的类成员属性,\nothing是个关键词,表示这个方法不对任何成员属性进行修改,所以是一个pure方法。

ensures子句定义了后置条件。

2、JML表达式

(1)原子表达式

\result表达式:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值。

\old( expr )表达式:用来表示一个表达式 expr 在相应方法执行前的取值。主要用于评估 expr中的对象是否发生变化。

\not_assigned(x,y,...)表达式:用来表示括号中的变量是否在方法执行过程中被赋值。

\not_modified(x,y,...)表达式:用来限制括号中的变量在方法执行期间的取值未发生变化。

(2)量化表达式

\forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。e.g. (\forall int i,j; 0 <= i && i < j && j < 10; a[i] < a[j])

\exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。e.g.(\exists int i; 0 <= i && i < 10; a[i] < 0)

\sum表达式:返回给定范围内的表达式的和。e.g. (\sum int i; 0 <= i && i < 5; i) ,

\product表达式:返回给定范围内的表达式的连乘结果。 e.g.(\product int i; 0 < i && i < 5; i)

\max表达式:返回给定范围内的表达式的最大值。e.g. (\max int i; 0 <= i && i < 5; i)

\min表达式:返回给定范围内的表达式的最小值。e.g. (\min int i; 0 <= i && i < 5; i)

(3)集合表达式

集合构造表达式:可以在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。 new JMLObjectSet {Integer i | s.contains(i) && 0 < i.intValue() } 表示构造一个JMLObjectSet对象,其中包含的元素类型为Integer,该集合中的所有元素都在容器集合s中出现

(4)操作符

子类型关系操作符: E1<:E2 ,如果类型E1是类型E2的子类型(sub type),则该表达式的结果为真,否则为假。如果E1和E2是相同的类型,该表达式的结果也为真。
等价关系操作符: b_expr1<==>b_expr2 或者b_expr1<=!=>b_expr2 ,其中b_expr1和b_expr2都是布尔表达式,这两个表达式的意思是b_expr1==b_expr2 或者b_expr1!=b_expr2 。
推理操作符: b_expr1==>b_expr2 或者b_expr2<==b_expr1 。对于表达式b_expr1==>b_expr2而言,当b_expr1==false ,或者b_expr1==true 且b_expr2==true 时,整个表达式的值为true 。
变量引用操作符:除了可以直接引用Java代码或者JML规格中定义的变量外,JML还提供了几个概括性的关键词来引用相关的变量。\nothing指示一个空集;\everything指示一个全集,即包括当前作用域下能够访问到的所有变量。

3、方法规格

前置约束:使用requires语句描述,表示方法执行的一些要求约束。

后置条件:使用ensures语句描述,表示方法执行结果的一些约束。

作用范围:使用assignable语句描述,表示方法可以修改的变量。

对于前置条件与后置条件通常有两种类型,分别是正常执行\normal_behavior和异常\exception_behavior两种类型,对于异常行为的结果,通常使用\signal语句描述。

4、类型规格

不变式:invariant要求所有在可见状态下都满足的条件。
状态约束变量:constraint要求状态变化过程中满足的条件。

5、JML工具链

openjml是所有工具链的核心,可以通过命令行工具对代码进行jml的语法检查,代码静态推导和动态检查。

JMLUnitNG/JMLUnit可以针对类自动生成测试样例并进行测试。

二、部署JMLUnitNG并进行测试

JMLUnitNG主要用来根据JML语言自动生成数据并进行测试。针对Group接口实现自动生成测试样例结果如下:

[TestNG] Running:
  Command line suite

Failed: racEnabled()
Passed: constructor MyGroup(0)
Passed: constructor MyGroup(-2147483648)
Passed: constructor MyGroup(2147483647)
Passed: <>.addPerson(null)
Passed: <>.addPerson(null)
Passed: <>.addPerson(null)
Passed: <>.addPerson(java.lang.Object@3db361f2)
Passed: <>.addPerson(java.lang.Object@7a52b631)
Passed: <>.addPerson(java.lang.Object@81a3b443)
Passed: <>.addrelation()
Passed: <>.addrelation()
Passed: <>.addrelation()

===============================================
Command line suite
Total tests run: 12, Failures: 0, Skips: 0
===============================================

这些测试数据,可以测试边界情况,但是其他极端情况还是需要自己自动生成,感觉实际效果并没有想象中的好。

三、架构设计

按照作业梳理自己的架构设计,特别分析自己的模型构建策略

第九次作业

本次作业,需要完成的任务为实现 person 类和简单社交关系的模拟和查询,学习目标为JML规格入门级的理解和代码实现。

唯一的难点在于isCircle,我采用的是BFS的方法,算是比较简单。

有一个坑点是addRelation方法当id1 == id2 && contains(id1)时,不用进行任何操作,没有异常。

OO第三单元总结_第1张图片

第十次作业

本次作业最终需要实现一个社交关系模拟系统。可以通过各类输入指令来进行数据的增删查改等交互。

添加了 Group 接口,实现一些关于查询组内信息的方法。

在Group里面的relationSum和 valueSum两个方法需要考虑到时间复杂度的问题,如果完全按照JML的规格会有O(n*n)的复杂度,导致CTLE。

同时,Network里面也不能用ArrayList了,需要用HashMap来提升效率,可以减少contains+getID的时间复杂度。

OO第三单元总结_第2张图片

第十一次作业

本次作业最终需要实现一个社交关系模拟系统。可以通过各类输入指令来进行数据的增删查改等交互。

本次作业在上一次作业的基础上,考察了大量的数据结构的内容,主要是这些方法之中的核心为MinPath,StrongLinked,BlockSum这三个方法,分别考察的是最短路,点双连通,联通块的数量。分别对应的算法是Dijkstra,Tarjan,并查集。但是需要进行一下优化才能保证不超时。点双联通也可以使用遍历删点看是否联通的做法。

OO第三单元总结_第3张图片

四、BUG情况

这一单元的作业真可谓是惨不忍睹。

第一次因为没有看情况JML规格,addRelation方法当id1 == id2 && contains(id1)时的情况没有考虑,导致强测只有5分,没有进互测。

第二次作业我暗下决心,一定要研读JML规格,一步步按照规格写,结果20个点全部CTLE爆零,同样没有进互测。

第三次作业,我才明白过来,原来只需要看懂JML规格就行,如果完全按部就班反而没有啥好下场,考察的重点在于如何实现,需要选择合适的容器,最优的数据结构算法。最后还是幸运的进了互测,但在实现的性能上还是有不足,仍然有CTLE的bug需要修复。

五、心得与体会

这一单元真的是对我来说,如同过山车一般,经历了一次南辕北辙,两次重构。通过不断地学习与练习,可以更深层次的了解JML并进行代码的实现。希望下次好好做一下阅读理解吧,先搞清楚课程组想让我们做什么,再开干。

你可能感兴趣的:(OO第三单元总结)