OO_JML_单元总结

OO_JML_单元总结

概述

  本单元作业主要学习的是规格化设计,即契约式编程,主要工作是根据所给出的JML规格描述,实现相应接口来满足功能需求。而本次作业在实现接口的同时要注意对数据结构的把控,并对功能实现算法进行优化,减少云心那个时间,避免出现TLE。

 

一、梳理JML语言的理论基础、应用工具链情况

理论基础

  JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。JML是一种行为接口规格语言 (Behavior Interface Specification Language,BISL),基于Larch方法构建。BISL提供了对方法和类型的规格定义 手段。所谓接口即一个方法或类型外部可见的内容。JML主要由Leavens教授在Larch上的工作,并融入了Betrand Meyer, John Guttag等人关于Design by Contract的研究成果。近年来,JML持续受到关注,为严格的程序设计提供 了一套行之有效的方法。通过JML及其支持工具,不仅可以基于规格自动构造测试用例,并整合了SMT Solver等工具 以静态方式来检查代码实现对规格的满足情况。

  我的理解其意义是采用统一规范的模块功能描述语言,避免采用自然语言而产生二义性,从而导致问题产生。

JML语法

1.注释结构

  JML以javadoc注释的方式来表示规格,每行都以@起头。有两种注释方式,行注释和块注释。其中行注释的表示方式为 //@annotation ,块注释的方式为 /* @ annotation @*/ 。

2.JML表达式

  • 原子表达式
    • \result表达式:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值。

    • \old( expr )表达式:用来表示一个表达式 expr 在相应方法执行前的取值。

    • ... ...

  • 量化表达式
    • \forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。

    • \exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。

    • ... ...

  • 操作符
    • 子类型关系操作符

    • 等价关系操作符

    • 推理操作符

    • ... ...

3.方法规格

  • 前置条件(pre-condition):前置条件通过requires子句来表示: requires P。

  • 后置条件(post-condition):后置条件通过ensures子句来表示: ensures P。

4.类型规格

  • 不变式(invariant)是要求在所有可见状态下都必须满足的特性,语法上定义 invariant P ,其中 invariant 为关键词, P 为谓词。对于类型规格而言,可见状态(visible state)是一个特别重要的概念。

  • 状态变化约束(constraint)对象的状态在变化时往往也许满足一些约束,这种约束本质上也是一种不变式。

具体语法在此

应用工具链

  • openJML:可以根据JML对实现进行语法检查和静态检查。

  • JMLUnitNG:可以根据JML自动生成对应的测试样例,用于进行单元化测试。

 

二.部署SMTSolver,至少选择3个主要方法来尝试进行验证,报告结果

测试程序如下:

  1 public class MyPath1 implements Path {
  2     // Iterable<Integer>和Comparable<Path>接口的规格请参阅JDK
  3     //@ public instance model non_null int[] nodes;
  4     private ArrayList<Integer>
  5             nodes = new ArrayList<>();
  6     //@ ensures \result == nodes.length;
  7     public /*@pure@*/int size(){
  8         return nodes.size();
  9     }
 10  11     /*@ requires index >= 0 && index < size();
 12       @ assignable \nothing;
 13       @ ensures \result == nodes[index];
 14       @*/
 15     public /*@pure@*/ int getNode(int index){
 16         return nodes.get(index);
 17     }
 18  19     //@ ensures \result == (\exists int i; 0 <= i && i < nodes.length; nodes[i] == node);
 20     public /*@pure@*/ boolean containsNode(int node){
 21         return nodes.contains(node);
 22     }
 23  24  25     /*@ ensures (\exists int[] arr; (\forall int i, j; 0 <= i && i < j && j < arr.length; arr[i] != arr[j]);
 26       @             (\forall int i; 0 <= i && i < arr.length;this.containsNode(arr[i]))
 27       @           && (\forall int node; this.containsNode(node); (\exists int j; 0 <= j && j < arr.length; arr[j] == node))
 28       @           && (\result == arr.length));
 29       @*/
 30     public /*pure*/ int getDistinctNodeCount(){
 31         ArrayList<Integer> tempList = new ArrayList<>();
 32         Iterator<Integer> it = this.iterator();
 33         while (it.hasNext()) {
 34             int temp = it.next();
 35             if (!tempList.contains(temp)) {
 36                 tempList.add(temp);
 37             }
 38         }
 39         return tempList.size();
 40     }
 41  42     /*@ also
 43       @ public normal_behavior
 44       @ requires obj != null && obj instanceof Path;
 45       @ assignable \nothing;
 46       @ ensures \result == (((Path) obj).nodes.length == nodes.length) &&
 47       @                      (\forall int i; 0 <= i && i < nodes.length; nodes[i] == ((Path) obj).nodes[i]);
 48       @ also
 49       @ public normal_behavior
 50       @ requires obj == null || !(obj instanceof Path);
 51       @ assignable \nothing;
 52       @ ensures \result == false;
 53       @*/
 54     public boolean equals(Object obj){
 55         if (!(obj instanceof Path)) {
 56             return false;
 57         }
 58         if (this.size() != ((Path) obj).size()) {
 59             return false;
 60         }
 61         for (int i = 0; i < this.size(); i++) {
 62             if (this.getNode(i) != ((Path) obj).getNode(i)) {
 63                 return false;
 64             }
 65         }
 66         return true;
 67     }
 68  69     //@ ensures \result == (nodes.length >= 2);
 70     public /*@pure@*/ boolean isValid(){
 71         if (this.size() >= 2) {
 72             return true;
 73         } else {
 74             return false;
 75         }
 76     }
 77     
 78     public MyPath1(int[] nodeList) {
 79         for (int i = 0; i < nodeList.length; i++) {
 80             nodes.add(nodeList[i]);
 81         }
 82     }
 83  84     @Override
 85     public Iterator<Integer> iterator() {
 86         return nodes.iterator();
 87     }
 88  89     @Override
 90     public int compareTo(Path o) {
 91         for (int i = 0; i < o.size() && i < this.size(); i++) {
 92             if (o.getNode(i) > this.getNode(i)) {
 93                 return -1;
 94             } else if (o.getNode(i) < this.getNode(i)) {
 95                 return 1;
 96             }
 97         }
 98         if (o.size() == this.size()) {
 99             return 0;
100         } else if (o.size() > this.size()) {
101             return -1;
102         } else {
103             return 1;
104         }
105         /*
106         Iterator<Integer> itThis = this.iterator();
107         Iterator<Integer> itOo = o.iterator();
108         int temp1, temp2;
109         while (itThis.hasNext()) {
110             temp1 = itThis.next();
111             temp2 = itOo.next();
112             if (temp1 > temp2) {
113                 return 1;
114             } else if (temp1 < temp2) {
115                 return -1;
116             }
117         }
118          */
119     }
120 121     @Override
122     public int hashCode() {
123         return this.nodes.hashCode();
124     }
125 126     //@ ensures containsNode(nodeId) ==> \result == Math.pow(4, (nodeId % 5 + 5) % 5);
127     //@ ensures !containsNode(nodeId) ==> \result == 0;
128     public /*@pure@*/ int getUnpleasantValue(int nodeId){
129         if(containsNode(nodeId)) {
130             return (int)Math.pow(4, (nodeId % 5 + 5) % 5);
131         }
132         else {
133             return 0;
134         }
135     }
136 }

 


静态检查结果如下:

java -jar F:\6\oo\share\openjml\openjml.jar -exec F:\6\oo\share\openjml\Solvers-windows\z3-4.7.1.exe -esc F:\6\oo\blog\third\src\MyPath1.java -cp "C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;F:\6\oo\blog\third\out\production\third;F:\6\oo\homework 11\specs-homework-3-1.3-raw-jar-with-dependencies.jar" -sourcepath F:\6\oo\blog\third\src -encoding utf-8
F:\6\oo\blog\third\src\MyPath1.java:11: 错误: 已在类 MyPath1中定义了变量 nodes
           nodes = new ArrayList<>();
           ^
F:\6\oo\blog\third\src\MyPath1.java:14: 错误: 找不到符号
return nodes.size();
      ^
符号:   变量 nodes
位置: 类 MyPath1
F:\6\oo\blog\third\src\MyPath1.java:12: 警告: Method size overrides parent class methods and so its specification should begin with 'also'
   //@ ensures \result == nodes.length;
       ^
F:\6\oo\blog\third\src\MyPath1.java:22: 错误: 找不到符号
return nodes.get(index);
      ^
符号:   变量 nodes
位置: 类 MyPath1
F:\6\oo\blog\third\src\MyPath1.java:17: 警告: Method getNode overrides parent class methods and so its specification should begin with 'also'
   /*@ requires index >= 0 && index < size();
       ^
F:\6\oo\blog\third\src\MyPath1.java:27: 错误: 找不到符号
return nodes.contains(node);
      ^
符号:   变量 nodes
位置: 类 MyPath1
F:\6\oo\blog\third\src\MyPath1.java:25: 警告: Method containsNode overrides parent class methods and so its specification should begin with 'also'
   //@ ensures \result == (\exists int i; 0 <= i && i < nodes.length; nodes[i] == node);
       ^
F:\6\oo\blog\third\src\MyPath1.java:31: 警告: Method getDistinctNodeCount overrides parent class methods and so its specification should begin with 'also'
   /*@ ensures (\exists int[] arr; (\forall int i, j; 0 <= i && i < j && j < arr.length; arr[i] != arr[j]);
       ^
F:\6\oo\blog\third\src\MyPath1.java:52: 错误: 找不到符号
     @ ensures \result == (((Path) obj).nodes.length == nodes.length) &&
                                       ^
符号:   变量 nodes
位置: 接口 Path
F:\6\oo\blog\third\src\MyPath1.java:53: 错误: 找不到符号
     @                     (\forall int i; 0 <= i && i < nodes.length; nodes[i] == ((Path) obj).nodes[i]);
                                                                                                ^
符号:   变量 nodes
位置: 接口 Path
F:\6\oo\blog\third\src\MyPath1.java:75: 警告: Method isValid overrides parent class methods and so its specification should begin with 'also'
   //@ ensures \result == (nodes.length >= 2);
       ^
F:\6\oo\blog\third\src\MyPath1.java:86: 错误: 找不到符号
           nodes.add(nodeList[i]);
           ^
符号:   变量 nodes
位置: 类 MyPath1
F:\6\oo\blog\third\src\MyPath1.java:92: 错误: 找不到符号
       return nodes.iterator();
              ^
符号:   变量 nodes
位置: 类 MyPath1
F:\6\oo\blog\third\src\MyPath1.java:129: 错误: 找不到符号
       return this.nodes.hashCode();
                  ^
符号: 变量 nodes
F:\6\oo\blog\third\src\MyPath1.java:132: 警告: Method getUnpleasantValue overrides parent class methods and so its specification should begin with 'also'
   //@ ensures containsNode(nodeId) ==> \result == Math.pow(4, (nodeId % 5 + 5) % 5);
       ^
9 个错误
6 个警告

 

三.部署JMLUnitNG/JMLUnit,针对Graph接口的实现自动生成测试用例,并针对规格对生成的测试用例和数据进行简要分析

对add函数进行测试,函数内容如下:

 1 public class Test {
 2     /*@ ensures \result == lnum + rnum;
 3     */
 4     public int add(int lnum,int rnum) {
 5         return lnum+rnum;
 6     }
 7     
 8     public static void main(String[] args) {
 9         add(1,2);
10     }
11 }

 

生成文件如下:

运行结果如下:

 

可见其主要在对边界情况进行测试。

四、按照作业梳理自己的架构设计,并特别分析迭代器中对架构的重构**

第一次作业架构

 

本次作业具体函数按照接口文档实现,Mypath使用ArrayList储存节点,Container中使用两个HashMap对路径和id双向映射,同时储存nodeMap,维护当前总共节点。并且将nodeMap的计数操作分解到addPath和removePath中,从而使distincNodeCount只有一个查询操作,减少时间消耗。

 

第二次作业架构

 

本次作业MyGraph继承上次作业Container,同时新增一个HashMap作为邻接矩阵使用,新增一个HashMap作为距离矩阵使用,邻接矩阵在add,remove时维护,距离矩阵在查询时维护,在remove,add时清空。本次作业使用BFS查询,数据结构的使用是为了保存结果,减少不必要的时间开销,以空间换时间。

 

第三次作业

 

本次作业继承上次作业结构。

不过新增了NodeKey<node,id>来表示node,为了将换乘的站点区分开。

同时将邻接矩阵接受类型扩大为obj,为了使其能够同时为两种邻接矩阵服务。

新增权矩阵用于储存当前边所具有的权。同时采用工厂模式给权矩阵赋予根据输入决定不同的权函数。

新增一个setList,储存每一个连通块内节点,在删除时清空,在增加和查询时共同维护。

本次使用迪杰斯特拉进行最短路径查询,同时将结果储存以便再次使用。

 

五.按照作业分析代码实现的bug和修复情况

本次作业前两次作业没有问题,但是死在了第三次作业上。

第三次作业出现的问题有两个,第一个是在未采用并查集计算连通块的情况下,在addPath时没有仔细思考决定要不要clear setList,从而导致连通块计算出现的问题。

第二个是在使用dij计算路径长度,储存中间结果时,由于采用了拆点,没有考虑到中间产生换乘时,换乘后的结果会覆盖前的结果,从而在查询到该换乘站距离时,会多计算一个换乘消耗的过程,可以在储存结果时避免产生覆盖来避免。

这些问题都是因为思考的过程中出现问题,并且测试用例没有很好的覆盖到出现的情况,才会出现。

 

六、阐述对规格撰写和理解上的心得体会

规格的书写是一个非常需要思考的过程,由于规格语言避免了二义性的产生,所以也就更加要求了规格的书写严谨性。

同时也要注意到规格不是为了限制代码的完成,而是相辅相成的一个过程,互相配合,而不是互相限制。规格只是为了让你更好的理解该方法的功能,而不是限制你的思维。

posted @ 2019-05-22 20:13  Sraey7  阅读(209)  评论(0编辑  收藏  举报