OO第三单元博客作业
OO第三单元博客作业
JML语言的理论基础
JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言,它使一种行为借口规格语言。JML为严格的程序设计提供了一套行之有效的方法。通过JML及其支持工具,不仅可以基于规格自动构造测试用例,并整合了SMT Solver等工具以静态方式来检查代码实现对规格的满足情况。
一般而言,JML有两种用法:
- 开展规格化设计。这样交给代码实现人员的将不是可能带有内在模糊性的自然语言描述,而是逻辑严格的规格。
- 针对已有的代码实现,书写其对应的规格,从而提高代码的可维护性。这在遗留代码的维护方面具有特别重要的意义。
表达式
原子表达式
\result
:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值。\old(expr)
:表示一个表达式expr
在相应方法执行前的取值,该表达式涉及到评估expr
中的对象是否发生变化。\not_assigned(x,y,...)
:用来表示括号中的变量是否在方法执行过程中被赋值。如果没有被赋值,返回为true ,否则返回 false 。用于后置条件的约束,限制一个方法的实现不能对列表中的变量进行赋值。\not_modified(x,y,...)
:该表达式限制括号中的变量在方法执行期间的取值未发生变化。\nonnullelements(container)
:表示container对象中存储的对象不会有null。\type(type)
:返回类型type对应的类型(Class),如type(boolean)为Boolean.TYPE。TYPE是JML采用的缩略表示,等同于Java中的 java.lang.Class。\typeof(expr)
:该表达式返回expr对应的准确类型。如\typeof(false)
为Boolean.TYPE。
量化表达式
\forall
:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。\exists
:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。\sum
:返回给定范围内的表达式的和。\product
:返回给定范围内的表达式的连乘结果。\max
:返回给定范围内的表达式的最大值。\min
:返回给定范围内的表达式的最小值。\num_of
:返回指定变量中满足相应条件的取值个数。
集合表达式
可以在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。集合构造表达式的一般形式为:new ST {T x|R(x)&&P(x)}
。
操作符
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
推理操作符:相当于离散的->,只有(1,0)是false。\nothing
或\everthing
变量引用操作符:表示当前作用域访问的所有变量。前者空集,后者全集。变量引用操作符经常在assignable
句子中使用,如assignable \nothing
表示当前作用域下每个变量都不可以在方法执行过程中被赋值。
方法规格
前置条件
前置条件通过requires子句来表示:require P;
,要求调用者确保P为真。
后置条件
后置条件通过ensures子句来表示:ensures P;。表示方法实现者确保方法执行返回结果一定满足谓词P的要求,即确保P为真。
副作用
使用关键词assignable
或者modifiable
,指方法在执行过程中会修改对象的属性数据或者类的静态成员数据。
类型规格
- 不变式
invariant
,要求在所有可见状态下都必须满足的特性,语法上定义invariant P
,其中invariant
为关键词,P为谓词。 - 状态变化约束
constraint
,对前序可见状态和当前可见状态的关系进行约束。
JML工具链
- OpenJML:用来做jml规格的校验,可以对代码进行JML规格的语法的静态检查,还支持使用SMT Solver动态地检查代码对JML规格满足的情况,因此OpenJML一般也自带有其支持的JML solver。
- JML UnitNG:强大之处在于可以根据规格的实现自动生成TestNG测试样例,即根据JML描述自动生成与之符合的测试样例,重点会检测边界条件。
代码架构设计
第九次作业
第十次作业
第十一次作业
bug及修复情况
第九次作业
在isCircle
函数的实现中出了一些问题。对于规格中以下代码
@ ensures \result == (\exists Person[] array; array.length >= 2;
@ array[0].equals(getPerson(id1)) &&
@ array[array.length - 1].equals(getPerson(id2)) &&
@ (\forall int i; 0 <= i && i < array.length - 1;
@ array[i].isLinked(array[i + 1]) == true));
理解出现了错误,理解成了在person
数组中存在一个子序列满足上述条件。经过修复后改为了DFS深度优先遍历,最后通过了所有的检查点。
第十次作业
由于在代码中使用存储容器是Arraylist,所以在性能方面出现了比较严重的问题。由于代码量比较大,重构的工作量也比较大,最终没有完成修改。
心得体会
经过本单元对代码规格的学习,在规范化、系统化开发代码方面的理解更加深刻了。同时,经过三次作业,我也能比较熟练地阅读JML规格并根据规格完成代码。在第十次作业中出现的性能方面的问题也让我对代码性能方面的重要性有了更深的认识。
参考资料
https://blog.csdn.net/piaopu0120/article/details/89527175
https://gitlab.buaaoo.top/oo_2020_public/jml/blob/master/JML Level 0手册.md