面向对象第三次作业总结博客
JML语言理论与应用梳理
Java建模语言(Java Modeling Language,JML)是一种进行详细设计的符号语言。这种语言增加了一些符号用以标识一个方法是用来干什么的,其关注的内容在于方法的效果,而不在乎方法的具体实现。这样,使用JML语言就可以预先设计好方法功能而不用管如何实现。
JML对一个方法的描述主要包括以下方面:
1、确定类的使用规范
一个类根据调用时初始条件的不同可能会有不同的行为,例如:当参数传递不合理时会抛出异常,即遵循异常行为模式;反之遵循正常行为模式。
/*@ public normal_behavior
@ requires containsPathId(pathId);
@ assignable \nothing;
@ ensures (\exists int i; 0 <= i && i < pList.length; pidList[i] == pathId && \result == pList[i]);
@ also
@ public exceptional_behavior
@ requires !containsPathId(pathId);
@ assignable \nothing;
@ signals_only PathIdNotFoundException;
@*/
如上,public normal_behavior
是正常行为标识而public exceptional_behavior
是异常行为模式。
2、前置条件与后置条件
前置条件表示一个方法调用时必须满足的一些要求,后置条件表示这个方法调用完毕后必须满足的要求。
/*@ public normal_behavior
@ requires path != null && path.isValid() && containsPath(path);
@ assignable pList, pidList;
@ ensures containsPath(path) == false;
@ ensures (\exists int i; 0 <= i && i < \old(pList.length); \old(pList[i].equals(path)) &&
@ \result == \old(pidList[i]));
@ ensures (\forall int i; 0 <= i && i < \old(pList.length) && \old(pList[i].equals(path) == false);
@ containsPath(\old(pList[i])) && containsPathId(\old(pidList[i])));
如上,前置条件要求path不可为null并且path有效并且path包含在容器中;而三个ensures标识的是方法的后置条件,从前置后置条件可以看出这是一个删除path的方法。
3、模型域
模型域类似成员变量,只能被应用到行为规范中。
//@ public instance model non_null Path[] pList;
//@ public instance model non_null int[] pidList;
如上定义了pList
和pidList
两个模型域,non_null
表示这两个域的元素都是非null的。
4、不变量
类级别的不变量是指进入和退出一个方法是都必须满足的条件。
//@ public invariant pList.length == pidList.length;
比如这个不变量表示pList.length
和pidList.length
在任何方法调用前后都应当相同。
5、量词
JML中量词的意思与逻辑学上的量词意思相近,主要是存在量词与全称量词。
如上,\exists
表示存在量词。
尝试使用了JUnitNG进行自动化测试,测试使用代码如下:
// demo/Demo.java
package demo;
public class Demo {
public static int[] nodes = new int[5];
/* @ public normal_behaviour
@ ensures \result == a - b;
*/
public static int compare(int a, int b) {
return a - b;
}
/* @ public normal_behaviour
@ ensures \result = nodes.length;
*/
public static /*@pure@*/ int size(){
return nodes.length;
}
/* @ public normal_behaviour
@ requires index >= 0 && index < size();
@ assignable \nothing;
@ ensures \result == nodes[index];
*/
public static /*@pure@*/ int getNode(int index) {
if (index < 0 || index >= size()) {
return -1;
}
return nodes[index];
}
public static void main(String[] args) {
compare(2,323232);
nodes[1] = 2;
nodes[0] = 0;
size();
getNode(1);
}
}
得到测试结果如下:
[TestNG] Running:
Command line suite
Passed: racEnabled()
Passed: constructor Demo()
Passed: static compare(-2147483648, -2147483648)
Failed: static compare(0, -2147483648)
Failed: static compare(2147483647, -2147483648)
Passed: static compare(-2147483648, 0)
Passed: static compare(0, 0)
Passed: static compare(2147483647, 0)
Failed: static compare(-2147483648, 2147483647)
Passed: static compare(0, 2147483647)
Passed: static compare(2147483647, 2147483647)
Passed: static getNode(-2147483648)
Passed: static getNode(0)
Passed: static getNode(2147483647)
Passed: static main(null)
Passed: static main({})
Passed: static size()
===============================================
Command line suite
Total tests run: 17, Failures: 3, Skips: 0
===============================================
可以看到原代码中compare方法存在溢出问题,通过JMLUnitNG自动化测试检测了出来。
针对本单元第二次作业中的MyGraphTest类,使用JUnit生成测试样例,并按照规格进行测试。
首先建图时希望尽可能代表情况种类数多,
private static MyGraph graph = new MyGraph();
private static MyPath p1 = new MyPath(1,2,2,3,4,5);
private static MyPath p2 = new MyPath(1,2,6,2,6,8);
private static MyPath p3 = new MyPath(7,6,4,9);
private static MyPath p4 = new MyPath(10,11,12,13);
接下来是对部分方法的测试: