OO第三单元总结

OO第三单元总结

任务一 :JML的理论基础,应用工具链情况

JML是一种形式化的,面向java的行为接口规格语言(来自于PPT)。他具有充分的理论基础:JML的建立是建立在LARCH方法的基础上的。

JML主要通过前置条件,后置条件,副作用,不变式等多种形式来规定方法的正常行为和异常行为,以达到契约式编程的目的。只要完全符合规格的代码就是没有bug的。
JML写在有@标记的java注释中,通过特殊的转义字符如 \result \exist \forall 等及特殊规定的操作符,结合java代码自身的数据结构与jml允许的规格变量进行描述,详细的语法在此次就不再赘述了。

JML目前有一套工具链,但是恕我直言,使用体验不佳。

openjml是所有工具链的核心,可以通过命令行工具对代码进行jml的语法检查,代码静态推导和动态检查,不过貌似openjml还不能完整的支持jml的所有特性。而且对于规模稍大的代码,静态检查的时间消耗十分惊人。

在eclipse中,有jml的官方gui,但是仍然是基于openjml命令行的,命令行有的问题gui里一应俱全,不过gui中可以更有针对性的给出一部分推导过程,使用起来很方便。

IDEA的插件库里有一个非官方的jml插件,显然是出自某位国外不知名的contributor,不过其效果奇烂无比,甚至比命令行还要差,(毕竟是私人写的),而且还多年不更新,要动手解包修改里面的版本信息才能在现在的idea上使用。

jmlunitNG则是一个根据testNG进行测试,能根据jml来生成测试样例的工具。

任务二(选做,未完全完成)部署SMT Solver

本次采用了cvc4定理证明器来对代码进行静态检查。

java -jar openjml.jar -prover cvc4 -exec Solvers-windows\cvc4-1.6.exe -cp spec-homework-1-1.1-raw-jar-with dependencies.jar;. ixre/MyPath.java

我只选取了Mypath类中的几个方法进行静态推导,gui和命令行都用了,结果是,仅有一个方法符合规格。

eclipse gui的输出总计有1536行,其中大部分是推导过程,一部分摘录如下

TRACE of ixre.MyPath.MyPath(int...)
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:15: ע:    requires nodeList != null; 
            VALUE: nodeList  === @uc_REF_26
            VALUE: null  === NULL
            VALUE: nodeList != null  === true
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:15: ע:    super()
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:1: ע:     Precondition assertion: _$CPRE__1
            VALUE: _$CPRE__1     === true
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:1: ע:     Precondition assertion: _$CPRE__2
            VALUE: _$CPRE__2     === true
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:83: ע:    //@ public invariant content.owner == this; 
            VALUE: content.owner     === @uc_REF_42
            VALUE: this  === @uc_REF_42
            VALUE: content.owner == this     === true
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:86: ע:    //@ public invariant content.theSize >= 0; 
            VALUE: content.theSize   === 0
            VALUE: 0     === 0
            VALUE: content.theSize >= 0  === true
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:83: ע:    //@ public invariant content.owner == this; 
            VALUE: content.owner     === @uc_REF_42
            VALUE: this  === @uc_REF_42
            VALUE: content.owner == this     === true
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:86: ע:    //@ public invariant content.theSize >= 0; 
            VALUE: content.theSize   === 0
            VALUE: 0     === 0
            VALUE: content.theSize >= 0  === true
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:83: ע:    //@ public invariant content.owner == this; 
            VALUE: content.owner     === @uc_REF_42
            VALUE: this  === @uc_REF_42
            VALUE: content.owner == this     === true
E:\WorkSpaces\eclipse\OO\src\ixre\MyPath.java:83: ע:    UndefinedNullDeReference assertion: !(_JML__tmp25.content != null) || _JML__tmp25.content != null
Completed proof of ixre.MyPath.size() with prover cvc4 - with warnings [5.59 secs]
Completed proving methods in ixre.MyPath [268.96 secs]
Summary:
    Valid:        1
    Invalid:      7
    Infeasible:   0
    Timeout:      0
    Error:        1
    Skipped:      0
   TOTAL METHODS: 9
   Classes:       0 proved of 1
   Model Classes: 0
   Model methods: 0 proved of 0

从这1536行中我主要看懂了以下几点:

1.代码中有问题的地方:getNode()中有整数溢出(这个问题确实存在,不过在我们的作业基本规则中保证了这里肯定不会溢出,故这个问题忽略)

2.有数个方法由于getNode()的整数溢出bug被直接判定为不符合规格。

3.jml规格中有一部分是不被openjml支持的,主要是建立的jml模型变量的部分。

4.openJml工具在运行过程中出现过一次crash,即不可逆转的错误。

由于-esc静态检查无法通过,-rac的动态检查也未能正常运行

任务三 部署JMLUnitNG/JMLUnit

完全按照讨论区伦佬教导部署了jmlunitng,执行如下命令

java -cp jmlunitng.jar ixre.MyPath_JML_Test

结果如下

任务四 作业架构梳理

类图



前两次作业架构十分简单;第二次作业中引入了junit测试类

第三次作业则新加入了一个迪杰斯特拉有向图类和一个继承于该类的不满意度图类

重构情况

三次作业之间没有大规模推翻重写,每次都只增加一部分方法或类,并修改了add remove方法,除此之外没有大的重构,每次作业都是基于上次作业的增量修改。

复杂度

第三次作业中,有第三次新加的三个方法的基本复杂度被爆红,这可能是因为这三个方法调用了多个模块,增加了非结构化程度。不过这次和前几次作业相比,被爆红的项明显减少,爆红的数值也不高,这说明了这次作业相比以前有一定的进步。

思路梳理

对前两次作业,我在最短路问题上采取了无向图的BFS方法,对于联通问题采取了并查集的方法。

对于第三次作业的新增内容,我采取了拆点+有向图+迪杰斯特拉的方法。

任务五 BUG总结

坦诚的说,这单元我写的bug有点多,这种一而再再而三犯傻逼错误的事情必须深刻反省

第一次作业在公测中被炸了两个TLE,主要是我没有认真观看指导书,没有注意到add remove的数量限制,误以为add remove指令是多数,结果为了保证add remove为o(1)方法而让distinctNodeCount成了o(n^2)方法导致超时.

第二次作业公测,互测没有bug被爆出

第三次作业,惨不忍睹,惨绝人寰,怎一个惨字了得

强测被炸了17个测试点,互测被炸了37个测试点

主要原因是remove方法里少写了一句代码(我以为我写了的),这种手残bug本来稍加测试就可以发现的,但是由于这章偏数据结构较为容易,使我麻痹大意,加上之前六次作业从未在课下与课上测出自己的任何bug,因此抱有侥幸心理,没有进行任何像模像样的测试,导致了人间惨剧的发生。如果我进行了哪怕一次测试,也是可以发现这个bug的。

任务六 感想与体会

虽然我知道这里要写规格的体会,但我还是要乱入一句,不可麻痹大意,不能不测试啊,大意失荆州啊啊啊啊啊啊啊啊啊啊

规格撰写确实是一种行之有效的方法,这种规格的意义绝不仅仅在于正确性问题上,提前写出规格,会让我们莽代码之前对架构有着更深刻的设计与认识。这是更为难能可贵的。

猜你喜欢

转载自www.cnblogs.com/tjm17231181/p/10902230.html