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还能使测试更加简单,模块化测试更加容易。 

 

posted @ 2019-05-21 19:14  17373060  阅读(155)  评论(0编辑  收藏  举报