第三单元作业总结
目录:
一、JML语言的理论基础
二、部署JMLUnitNG/JMLUnit并应用
三、程序架构分析
(1)第一次作业
(2)第二次作业
(3)第三次作业
四、BUG与互测
五、规格撰写和理解上的心得体会
一、JML语言的理论基础
1.什么是JML:
JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。
JML有两种主要的用法:
(1)开展规格化设计。这样交给代码实现人员的将不是可能带有内在模糊性的自然语言描述,而是逻辑严格的规格。
(2)针对已有的代码实现,书写其对应的规格,从而提高代码的可维护性。
2.JML语法整理
a.注释结构
行注释://@annotation
块注释:/* @ annotation @*/
b.JML表达式(仅选取部分罗列出来):
(1)原子表达式
\result表达式:表示一个非void 类型的方法执行所获得的结果,即方法执行后的返回值。
\old( expr )表达式:用来表示一个表达式expr 在相应方法执行前的取值。
(2)量化表达式
\forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。
\exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。
\num_of表达式:返回指定变量中满足相应条件的取值个数。
(3)集合表达式
可以在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。
(4)操作符
子类型关系操作符: E1<:E2 ,如果类型E1是类型E2的子类型(sub type),则该表达式的结果为真,否则为假。
等价关系操作符: b_expr1<==>b_expr2
推理操作符: b_expr1==>b_expr2
c.方法规格
(1)前置条件:requires P;
(2)后置条件:ensures P;
(3)副作用范围限定:assignable P;
(4)signals子句: signals (***Exception e) b_expr;
d.类型规格
(1)不变式invariant
(2)状态变化约束constraint
3.JML工具链
(1)使用OpenJML对实现的代码进行检查:包括JML语法静态检查,代码静态检查,运行时检查。
(2)使用JMLUnitNG根据JML语言自动生成TestNG测试。
二、部署JMLUnitNG/JMLUnit并应用
参考网上的教程,在下载好JMLUnitNG后,可以通过如下的语句使用JMLUnitNG对程序进行测试:
- java -jar jmlunitng.jar test/MyGroup.java
- javac -cp jmlunitng.jar test/*.java
- java -jar openjml.jar -rac test/MyGroup.java test/Person.java test/Group.java test/MyPerson.java
- java -cp jmlunitng.jar test.MyGroup_JML_Test
在实际使用过程中,发现JMLUnitNG很难使用,使用时经常报错,并且测试仅仅是针对数据上的边界条件进行测试,无法测试出程序逻辑上的bug,以下是测试结果的部分截图:
三、程序架构分析
第一次作业:
·类图
(1)作业要求:
实现一个社交关系模拟系统,可以通过各类输入指令来进行数据的增删查改等交互。
实现MyPerson和MyNetwork类来实现。
(2)程序架构分析:
第九次作业看上去比较简单,程序所要实现的功能和函数已经通过JML语言明确指出,甚至有些接口的实现已经用伪代码给出了。由于第九次作业功能较小,并没有对程序架构进行过多的考虑,直接一个一个实现就可以了。
第二次作业:
·类图
(1)作业要求:
在第一次作业的基础上,增加了一个MyGroup类,模拟类似微信群之类的群组关系。通过掉用Network中的方法可以动态创建Group并将Person加入到Group中,并对Group中的数据进行一系列的查询操作。
(2)程序架构分析:
第十次作业在实现Group类的各种属性时,需要在执行addToGroup和addRelation方法时动态的去维护他们的值,而不是在执行查询指令时,每次从头开始计算。如果每次都从头开始计算的话,会导致程序的复杂度大大上升。
第三次作业:
·类图
(1)作业要求:
第三次作业并没有再增加新的类,而是实现了更多的方法,比如可以让一个人从Group中退出,询问社交关系中连通块的个数,Person之间的最短距离已经是否强连通等问题。第三次作业对于我这种菜鸡来说,算法上的难度显著提高。
(2)程序架构分析:
第三次作业的架构没有太大的改变,依然是按照接口的布局去实现的(只不过是最后不满足单个文件不超过500行的限制,把一些方法单写了一个文件),所以依旧是按照类图给出的。
四、BUG与互测
第一次作业的bug分析:
阅读规格不仔细,忽略了person.getId() == id这个条件,少写了3行代码,结果导致强测爆炸。。。
第二次作业的bug分析:
实现isCircle方式时,当时实现的方法竟然是每次掉用,直接计算了所有人的联通情况,导致性能爆炸,真不知道当时自己怎么想的,欲哭无泪了已经。。。
第三次作业的bug分析:
计算最短路径时用的Dijkstra算法,但是优化做的不好,几个点出现了TLE;在计算强连通关系时,实现的正确性上有一些欠缺,导致某些情况下本来时强连通的,但是返回了false。
第三次作业的bug在写本次博客作业时还能完全修复,需要进一步的完善。
互测找bug:
首先是提交自己在完成作业时,测出自己程序bug的一些测试用例;然后用python写一个简易的数据生成器,这种批量生成的随机数据在B屋和C屋还是能hack到不少人的。
五、规格撰写和理解上的心得体会
正如JML手册中指出的,JML是用于对Java程序进行规格化设计的一种表示语言。老师在上课时也一再告诉我们不是说一定要用JML语言去描述一个类,一个方法的功能,如果要是能用自然语言准确无误地表示出来,当然也是可以的。但是由于自然语言具有二义性,总是会出现表述模糊的情况。而JML语言具有很好的规范性,并且只要表述正确,是不会出现歧义的。掌握好JML语言对于团队协作是十分有用且必要的。
在两次课上实验中,自己动手写了一些JML语言,发现即使是一些比较简单的方法,想用JML语言描述好也有一定的困难。在阅读JML方面,遇到一些功能十分复杂的函数时,其对应的规格描述看上去让人十分头疼。并且在某些情况下,感觉JML语言的描述反而更使需要实现的功能变得更难以理解,比如在queryBlockSum方法,如果用自然语言描述,求社交网络中的连通块个数,就很容易理解,但是用JML语言就要用两层循环去描述,甚至在读完规格时并不能马上理解出这个方法的具体目的。因此,我觉得在进行规格化设计的时候,应该同时结合JML这种规格化的语言与自然语言,这样才会使得设计工作更加高效。
第三单元的作业,第一眼看上去好像很简单,要实现的方法都已经以接口的形式给出,并且还用JML语言给出了相关的规格。然而我被这种虚假的感觉所欺骗了,在完成作业时过于麻痹大意,阅读JML规格不仔细,未能对作业进行充分的测试,在性能方面表现也欠佳,导致整个单元三次作业翻车严重,十分懊悔。
OO课程只剩下最后一个单元了,希望自己能调整好心态,努力完成好第四单元的作业。