OO——第三单元JML规格编程总结

一、JML理论基础及应用工具链

JML存在的意义

1. 开展规格化设计,能够实现设计与实现分离,实现进行提前测试。

2. 方便程序代码的阅读和维护。

JML方法规格

requires —— pre-condition

assignable —— side-effects,列出这个方法能够修改的类成员属性

ensures —— post-condition

区分正常功能行为和异常行为

1 /*@ public normal_behavior
2   @ 
3   @ also
4   @ public exceptional_behavior
5   @ signals (Exception e) expr;
6   @*/

JML类型规格

针对Java重定义的数据类型所设计的限制规则。

invariant —— 不变式

constraint —— 状态变化约束

JML表达式

\result

\old(expr) —— 这个使用起来需要注意

\forall

\exist

\nothing

==>

<==>

等等,此处仅列举一些常用的

JML应用工具链

OpenJML —— 检测JML注释正确性

JMLunitNG —— 进行测试

等等

二、部署JMLUnitNG/JMLUnit

 这个部分让AND gate同学彻底疯了,虽然自知时间有限+能力有限我怕是搞不了Graph,但是抱着尝试的态度,我还是自己写了一个简单的测试样例去做......

但是,体验极差,虽然最后跑了一个什么东西出来,但是我甚至不知道是怎么跑出来的......

接下来,我来记录一下我糟糕的体验过程...顺便希望得到一些指教。

先放一下我自己的测试样例:

 1 package test;
 2 
 3 public class Test extends Exception{
 4     public static void main(String[] args) {
 5         try {
 6             divide(100, 20);
 7         } catch (Exception e) {}
 8     }
 9 
10     /*@ public normal_behavior
11       @ requires dividend != 0;
12       @ ensures \result == divisor / dividend;
13       @ also
14       @ public exceptional_behavior
15       @ requires dividend == 0;
16       @ signals (Exception e);
17       @*/
18     public static int divide(int divisor, int dividend) throws Exception {
19         if (divisor == 0) {
20             throw new Exception("hehe");
21         } else {
22             return divisor / dividend;
23         }
24     }
25 }

首先,我按照讨论区的方法开始使用——很好,前几步都很顺利,接下来就是噩梦的开始。

我用javac编译文件,有一个.java文件无法编译,报错信息是一堆乱码:),然后我用idea打开看,idea的报错是,我的Test是个抽象类,无法被实例化......然后出现了Test.divide的地方也全都报错了。

然后,我仔细地再看了一遍伦佬的教程,思考我和他的区别,我肤浅地觉得是我没有写package,然后我就写上了,然后就是彻底的噩梦的开始。

本来只是一个文件没法用命令行编译,这回好了,我没有能编译的文件了???然后报错依然是一堆混杂着乱码的东西

再后来,我琢磨着是不是包名的问题呢?但是我完全模仿伦佬,从文件名到包名到.java的名字和内容都一样,但是还是失败了——难道我是瞎哪边看错了写错了大小写???

(由于急着交作业,我没有再从头尝试,之后我会再次尝试,看看能不能复现坑爹的结果)

然后,我决定放弃用命令行来做这件事,我选择用ide。

打开ide以后,我就慌了,因为在那个Test文件里,我出现了几处红色,但是我不会解决它,我抱着尝试的心态,点了run,然后居然——

当然没有运行成功,但不是因为红色区域,而是,它提醒我jdk版本有问题?我???

我又抱着尝试的心态,把jdk8换成了jdk11......居然,运行成功——在多处红色警告下,它运行出来了......

我实在太震惊了,导致无话可说,我不知道我到底是怎么完成了这个任务的。我觉得这次尝试,除了震撼我妈以外,我没有什么收获。还请有人能赐教。

三、架构设计梳理

1/ 第一次作业

这一次作业比较简单,基本按照所给规格写就完事了。

唯一注意了一下的是当时怕时间复杂度太高,用hashmap做了一个双向的操作。即我用了两个hashmap,一个key值为id,value为path,另一个key值为path,value为id。

这样在删除和通过path找id时都会更快一些。

2/ 第二次作业

单独开了一个floyd类来处理我的图,但其实并没有什么优越性......

3/ 第三次作业

我所有的图全都用二位数组实现了(没错是写死的),所有的边和节点全都用hashmap存储,后来上课知道这样设计非常垃圾......因为不能拓展,也很难修改。

说起重构,其实也还是没有很好的想法,可能会建一个工厂来建不同的图吧......这样图就不会是写死的了......

的确,这部分当时更关心算法和时间复杂度的问题,并没有去考虑架构的问题(当然其实也是有点不知道对于这次作业什么架构算是比较好的?)

算法的话最后用的就是把换乘也算作权值,然后floyd到底。

由于我觉得这个部分重在分析架构,但是我现在没什么特别好的想法,感觉算法没必要谈,大家心里都有数...所以就暂时简短地结束吧。

以及我比较关心的一个问题是我知道可以用继承,让system继承graph,但是在graph的父类里我存了很多数据,我想在system里快速使用的话就想用protected关键字,但是这和checkstyle的检测冲突,这样我就要写一些新的方法,去完成一些事。第一,我不知道是不是我对继承理解有问题,父类的数据就是不能随便被子类使用的;第二,之前上C++课程的时候,老师却说尽量使用protected...是因为JAVA和C++特性不同吗?为什么此处我们不鼓励(起码checkstyle不鼓励)我们使用protected。

四、BUG及修复情况

很幸运......这单元三次作业强测和互测都没有出现什么问题,个人仅从想当然的角度思考自己的程序的话,先不论垃圾的架构,我更担心的是运行时间和内存,我甚至也不太担心运行时间,因为我觉得我的算法不会tle,但是的确会比较关心内存的使用,静态二维数组开的太多,我的每一条path都有自己的四个二维数组存一些数据,完整的图又有四个较大的二维数组......其实我没有很好地除了二维数组的建图方向,我觉得用邻接表...(特别是我知道有些同学用hashmap来模拟)会非常非常的慢。

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

我觉得其实这次的实验训练,在规格撰写和阅读上并没有做的很多,更多的着眼点还是跟以前的作业给我的感觉差不多。

其实JML的阅读体验还可以,除了有时候一堆(/forall,,)里嵌套(/forall,,)甚至还不止一层嵌套......但是这种情况不会太多,如果真的层数深了也说明这个方法设计并不是非常合理。

第一次去实验的时候,写起规格还是挺懵逼的,懵逼的原因1、根本没有办法想清楚这个东西的定义,所以不会写;2、我自认为我脑内有了一个定义,但是我无法用JML的语言做出表达。但后来看了几次作业给我们的规格之后,写起来就比刚开始顺滑一些了。也能从JML的角度而不是实现的角度去看待一个方法了。

猜你喜欢

转载自www.cnblogs.com/senbei/p/10900857.html