OO第三次博客作业
一、JML简介及工具链
JML是一种进行详细设计的符号语言,他鼓励你用一种全新的方式来看待Java的类和方法。
JML综合了Eiffel,Larch两者的优点,为Java提供了一个专门设计行为的接口语言。JML用来描述方法模块的动作行为,基于数学模型,其比自然语言更加精确。
JML编译器jmlc可以检查JML形式规范是否正确;JMLdoc与Javadoc工具相似,不同的是它在生成的HTML格式文档中包含JML规范;JMLunit可以成生一个Java类文件测试的框架,它可以让你很方便地使用JUnit工具测试含有JML标记的Java代码。
二、JMLUnitNG
采用了比较暴力的方法,将jmlunitng的包和课程组的包直接添加进了CLASSPATH里。
对第三次作业中的MyPath类进行测试。
import com.oocourse.specs3.models.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
public class MyPath implements Path {
private ArrayList<Integer> nodes;
private HashMap<Integer, Integer> map = new HashMap();
private int distinctCnt;
private int hashCode;
// Iterable<Integer>和Comparable<Path>接口的规格请参阅JDK
public Iterator<Integer> iterator() {
return nodes.iterator();
}
public int hashCode() {
return hashCode;
}
public int compareTo(Path o) {
MyPath compareThis = (MyPath) o;
int len = nodes.size();
if (compareThis.size() < len) {
len = compareThis.size();
}
for (int i = 0; i < len; i++) {
int t = nodes.get(i).compareTo(compareThis.getArrayList().get(i));
if (t < 0) {
return -1;
}
if (t > 0) {
return 1;
}
}
if (nodes.size() > len) {
return 1;
}
if (compareThis.size() > len) {
return -1;
}
return 0;
}
public MyPath(int[] nodeList) {
map.clear();
distinctCnt = 0;
hashCode = 0;
nodes = new ArrayList<Integer>();
for (int x : nodeList) {
nodes.add(x);
if (!map.containsKey(x)) {
map.put(x, 1);
distinctCnt++;
}
hashCode = hashCode() * 233 + x;
}
}
public /*@pure@*/int size() {
return nodes.size();
}
public /*@pure@*/ int getNode(int index) {
return nodes.get(index);
}
public /*@pure@*/ boolean containsNode(int node) {
return map.containsKey(node);
}
public /*pure*/ int getDistinctNodeCount() {
return distinctCnt;
}
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Path)) {
return false;
}
return nodes.equals(((MyPath) obj).getArrayList());
}
public /*@pure@*/ boolean isValid() {
return nodes.size() >= 2;
}
ArrayList<Integer> getArrayList() {
return nodes;
}
private static int[] pow4 = {1, 4, 16, 64, 256};
public /*@pure@*/ int getUnpleasantValue(int nodeId) {
if (!containsNode(nodeId)) {
return 0;
}
return pow4[(nodeId % 5 + 5) % 5];
}
public boolean containsEdge(int fromNodeId, int toNodeId) {
for (int i = 0; i < nodes.size() - 1; i++) {
if ((nodes.get(i) == fromNodeId && nodes.get(i + 1) == toNodeId)
||
(nodes.get(i) == toNodeId && nodes.get(i + 1) == fromNodeId)
) {
return true;
}
}
return false;
}
}
生成测试文件
jmlunitng MyPath.java
编译测试文件
javac MyPath_JML_Test.java
编译MyPath.java
javac MyPath.java
运行测试
java MyPath_JML_Test

三、架构设计
1、第一次作业

第一次的构架为三个类,即主类,继承Path的MyPath类和继承PathContainer的MyPathContainer类,在类内部使用map来完成相应工程
2、第二次作业

第二次作业代码构架仍然为三个类,主类,继承Path类的MyPath类,继承Graph类的MyGraphy类,这一次的作业采用了Floyd算法,做的很不好的一点是没有将图的模块独立出MyGraphy类,而是在MyGraphy类里直接开设了相应的数组运行算法
3、第三次作业

第三次作业除了继承了前两次作业的写法之外,新增了一个ShortestPath类,由于第三次作业中的三个询问不同点在于建边的边权不一,所以只要规定ShortestPath类内部的TYPE,就能用以边权的区分。
四、BUG情况
在第二次作业中,由于采用了Floyd算法,所以每次只要有图结构变化相关指令,最短路就需要重新运算。由于Floyd使用邻接矩阵,所以对于点编号需要map离散化之后加以使用,在这里出现了一个bug:在addPath和removePath两个不同的图结构修改指令中,对离散化map的操作顺序不正确,addPath之前需要先修改map维护编号,removePath则在之后,当时编写这一部分代码时出现了问题
五、心得体会
我觉得JAVA里的规格让工程代码有了灵魂。
之前在学习c语言的时候,对于每一个函数方法,内心也会想着这个函数有什么作用,对于传入的参数应该有什么操作,返回结果是多少,不过这样都是感性思考。规格真正量化了这个思想。就像是在写递归一样,我只要知道这个函数的行为,那我就可以直接调用,而不用深层次进一步地思考这个函数内部执行,这对于编写代码时的流畅性非常有帮助。
JML的写法也不复杂,除了几个域之外,剩下的就是JAVA语法和简单的符号\forall,\exists等等。
JML还能使测试更加简单,模块化测试更加容易。

浙公网安备 33010602011771号