一、关于JML
JML理论基础
所谓的JML,它是一种形式化的,面向JAVA的行为接口规格语言。对于我们所写的代码,正确性的重要性不言而喻,JML正是我们为了获得回答方法正确性问题的一种重要的技术手段,同时可作为我们开展测试设计时的依据,是多人协同开发时的交互基础。
方法规格:
举一个例子,首先我们看到最上面的就是描述方法规格的类别。方法规格分为正常行为规格和异常行为规格,第一个规格写的normal_behavior对应的则是正常行为规格。Requires关键字描述的是规格的前置条件,assignable描述的是副作用范围,ensures描述的则是后置条件,中间的also关键词用于连接多个规格。下面第二个规格写的exceptional_behavior对应的是异常行为规格,通过signals子句抛出异常。最下面的是标题,用于定义过程的形式。
类型规格:
类规格组成包括对数据状态的要求,对方法的要求。类规格完整准确定义了一个类的设计目标和能力。对数据状态的要求包括不变式invariant和状态变化约束constraint。不变式是要求在所有可见状态下都必须满足的特性,而状态变化约束对对象状态的变化进行约束。
JML工具链:
主要的JML工具有OpenJML,JMLUnitNG。可以对JML语法进行检查以及作为自动化单元测试的一个生成工具。
二、部署SMT Solver
OpenJML与Solver
Parsing and Type-checking
主要是检查JML规格的语法
我主要检查的是自己魔改的Person类,
可以看到无任何返回信息,应该是没有问题吧。
Extended Static Checking
主要是利用Solver对按照JML编写的程序进行简单的静态检查。
可以看到,我魔改的Person类是没有对queryValue写规格的,就是因为在静态检查的时候会返回神奇的错误,大概是报空指针之类的意思,我觉得可能是OpenJML有问题,所以把queryValue的规格删掉了,进行静态检查,就没有错误返回了,只剩下了13个警告。大概都是Solver无法对程序进行充分的解析导致的。
Runtime Assertion Checking
本功能是对JML编写的程序进行简单的运行时检查
可以看到对我的Person类没有抛出异常,并且在我的目录下生成了一个Person.class的文件。
内容有接近5000行之多,前几十行内容大概如上,可以看到都是对代码运行过程的简单检查。
JMLUnitNG测试
首先通过命令,我们得到测试类。并直接运行Group_JML_Test.java文件得到以下结果:
可以看到所有的方法均没有问题。
架构设计
第一次作业
第一次作业都是按照规格来写的,只是单纯的对JML规格进行了翻译。
第二次作业
第二次作业同样是对规格的翻译,因此造成了非常严重的错误。
第三次作业
这是de完bug之后的,引入了图这一新对象,从而能在实现的过程中专注算法的编写。在之前是所有东西都混在MyNetwork里的。并查集,树状数组,迪杰斯特拉,DFS等混在里面显得很凌乱和臃肿。
BUG与修复情况
这一单元由于自己的疏忽,所以三次作业都爆炸了。第一次作业中判断是否存在某一始末端确定的链时没有考虑自环。第二次作业,因为没有考虑要修改第一次作业中写的addrelation,导致Group中的关系错误。第三次作业的错误在于,区间查询没有考虑区间不存在的情况,最短路没有描述边导致的TLE,以及判断双连通分量的时候一些小细节的错误。都已经得到修复。
心得体会
规格这一单元给人的初印象是简单,逻辑别人都给你写好了,你只要实现就可以了。但是实践下来发现并不是那么容易。首先第一点,在增加新功能的时候依然需要对老功能进行测试,虽然原来的方法规格没有变,但是为了满足新规格的正确性,老方法可能需要修改。其次,有很明显的可以抽象出来的对象时,还是要抽象出来,不用搁那硬做翻译。最后就是一定要进行大量的测试和时间复杂度的测试,不然很容易出错。