面向对象第三单元做业总结

面向对象第三单元做业总结

JML语言和工具链

JML的基本语法

契约式设计或者Design by Contract (DbC)是一种设计计算机软件的方法。这种方法要求软件设计者为软件组件定义正式的,精确的而且可验证的接口,这样,为传统的抽象数据类型又增长了先验条件、后验条件和不变式。这种方法的名字里用到的“契约”或者说“契约”是一种比喻,由于它和商业契约的状况有点相似。java

在契约式设计的背景下, JML(java model langauge)为契约式设计提供了支持。
jml的规格放在了注释里, // /* */ 来表示一些规则
\requires子句定义了方法的前置条件, 也就是程序运行前须要知足的条件
\ensures 子句定义了方法的后置条件, 也就是程序运行后须要知足的条件
\result 表明了方法执行的结果
\old(expr) 表明了表达式expr在方法执行前的值
\forall 对于给定范围的元素, 应知足条件
\exist 存在范围内的元素, 知足条件
\signal 用于抛出异常
\constraint用于限制对象转台的变化
\invariant 可见状态下都必须知足的特性node

openjml的使用

经过openjml对程序规格进行动态和静态的检查
我采用了命令行的方式算法

java -jar ./openjml.jar -esc ~/Downloads/computer_science/schoolwork/OO/hw9/src/mycode/MyPath.java数组

smt solver验证

下载openjml以及使用

openjml是用来检查程序是否符合jml定义的规格的工具, 下载openjml.jar以后, 经过命令行运行, 主要有一下三种检查方式。
-check 仅仅对JML语法进行静态检查
-esc 规格静态检查, 看在代码没有运行的时候, 有没有不符合JML要求的操做。
-rac runtime check给定输入文件, 在代码运行的时候, 判断是否有不符合JML要求的操做。架构

进行检查

静态检查
在命令行中打入java -jar ./openjml.jar -esc -cp ~/Downloads/computer_science/schoolwork/OO/hw9/specs-homework-1-1.1-raw-jar-with-dependencies.jar ~/Downloads/computer_science/schoolwork/OO/hw9/src/mycode/MyPath.java

由于没有修改规格, 致使出现了两个错误和五个warning, 须要修改规格来让命名方式符合, 不过我以为这样检测方法不是很科学, 每一个人实现的方式不一样, 在真实的软件开发中, 你们都很忙, 若是这么去调bug 的话很浪费时间, 单元测试会比这种方法更合适。

修改规格后出现了更多的报错
由于是经过容器和hashSet实现的代码, 和规格的描述不一样。
目前掌握了SMT的用法, 可是因为代码的实现方式不一样, 没法合理的用SMT进行测试, 有些为了知足JML语法而刻意去改的意味。 JML是很好的契约式设计的描述语言, 可是若是直接用SMT去进行测试的话, 还不够成熟。函数

jmlUnitNg

jmlUnitNg的用法

jmlUnitng是一款经过jml语言生成testNg测试数据的工具
首先从官网下载jml-uniting, 获得一个jar包
以后, 对于想要生成数据的java文件, 执行jml uniting
java -jar jmlunitng.jar [OPTIONS] path-list
其中options包括
-d 指定生成测试文件的目录
-cp , --classpath 给定一系列的class path
--public 只对public方法测试
好比工具

java -jar jmlunitng.jar -cp hw10/src/opensource/specs-homework-2-1.2-raw-jar-with-dependencies.jar hw10/src/mycode/MyPath.java

生成上图所示的目录
以后, 就可使用testNg进行愉快的测试了单元测试

运行


运行结果 经过了测试测试

架构梳理

第一次做业


第一次做业总体难度不高, 须要修改MyPathContainer和MyPATH两个类。
架构上MyPathContainer实现了PathContainer接口, 做为路径的容器。
而Path是PathContainer的组成部分。ui

第二次做业


第二次做业利用PATHContainer, 实现了graph, 完成第一次做业的增量式设计。

第三次做业


第三次做业在之前的基础上增长了railwaySystem类, 继承了Graph, 完成新的操做。

分析代码

第一次做业

第一次做业主要是增长和删除路径, 我用了三个HashMap来实现

private HashMap<Path, Integer> pathToInt;
    private HashMap<Integer, Path> intToPath;
    private HashMap<Integer, Integer> nodeCount;

path到id, id到path, 以及一个node有多少个, 让每次查询的复杂度都接近O(1)
path hash值的计算方法以下

for (int i = 0; i < nodeList.length; i++) {
            nodes.add(nodeList[i]);
            nodeSet.add(nodeList[i]);
            hashCode = hashCode * 31 + nodeList[i];
        }

最后再Container里经过update函数来加减PATH, 完成封装。

private int updateByAddPath(Path path) {
        int curNextId = getNextId();
        pidList.add(curNextId);
        pathList.add(path);
        pathToInt.put(path, curNextId);
        intToPath.put(curNextId, path);
        for (int e : path) {
            if (nodeCount.containsKey(e)) {
                int oldValue = nodeCount.get(e);
                nodeCount.replace(e, oldValue + 1);
            } else {
                nodeCount.put(e, 1);
            }
        }
        return curNextId;
    }

第二次做业

第二次做业在第一次做业的基础上增长了graph
我用hash表和数组来存储图

private HashMap<Integer, Integer> nodeIdToIndex;
    private HashMap<Integer, Integer> indexToNodeId;
    private int[][] graphMatrix;
    private int maxNodeCount;
    private int curNodeCount;

最后使用floyd算法来求最短路

protected void buildMinDistance() {
        for (int mid = 0; mid < curNodeCount; mid++) {
            for (int i = 0; i < curNodeCount; i++) {
                for (int j = 0; j < curNodeCount; j++) {
                    if (checkNewRoad(i, mid, j)) {
                        updateCost(i, j, getNewCost(i, mid, j));
                    }
                }
            }
        }
    }

第三次做业

此次做业, 我采用的是拆点发, 而后dijstra
把三种最短路, 化为一个dijstra方法
type == 1, type2, type3 分别对应最低票价, 最少不满意度, 最少换成次数

private void dijLeast(int from, int type) {
        for (int i = 0; i < distinctNodeCount; i++) {
            if (i == from) {
                dijMinDis[i] = 0;
                dijVisit[i] = true;
            } else {
                dijMinDis[i] = getGraphValue(from, i, type);
                dijVisit[i] = false;
            }
        }
        for (int i = 1; i < distinctNodeCount; i++) {
            int minCost = 10000000;
            int minPos = -1;
            int newCost;
            for (int j = 0; j < distinctNodeCount; j++) {
                if (!dijVisit[j] && dijMinDis[j] != -1) {
                    if (dijMinDis[j] < minCost) {
                        minCost = dijMinDis[j];
                        minPos = j;
                    }
                }
            }
            if (minPos == -1) {
                break;
            }
            dijVisit[minPos] = true;
            for (int j = 0; j < distinctNodeCount; j++) {
                if (!dijVisit[j] && getGraphValue(minPos, j, type) != -1) {
                    newCost = minCost + getGraphValue(minPos, j, type);
                    if (dijMinDis[j] == -1 || (dijMinDis[j] > newCost)) {
                        dijMinDis[j] = newCost;
                    }
                }
            }
        }
    }

心得体会

这三次做业, 让我体会到了对于一个工程是如何增量设计的。 传统瀑布式开发虽然好, 可是不够灵活, 从这三次做业, 我体会到了敏捷开发,一边设计, 一边写代码, 一边测试的过程, 加深的我对于开发的理解。 开发一个工程如同滚雪球, 开始只有基本的类和方法, 以后一次一次迭代, 继承, 封装, 最后达到要求。

相关文章
相关标签/搜索