OO第三单元博客总结
OO第三单元博客总结
一、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等工具 以静态方式来检查代码实现对规格的满足情况。
1.JML表达式
\result:表示返回值
\old(expr):用来表示一个表达式expr在相应方法执行前的取值
\forall:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束
\exists:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束
\sum:表示给定范围内表达式的和
操作符: <, >, <==>, ==>, \nothing, \everything
2.方法规格
·前置条件(pre-condition)
前置条件通过requires子句来表示: requires P; 。其中requires是JML关键词,表达的意思是“要求调用者确保P为 真”。注意,方法规格中可以有多个requires子句,是并列关系,即调用者必须同时满足所有的并列子句要求。
·后置条件(post-condition)
后置条件通过ensures子句来表示: ensures P; 。其中ensures是JML关键词,表达的意思是“方法实现者确保方法执 行返回结果一定满足谓词P的要求,即确保P为真”。同样,方法规格中可以有多个ensures子句,是并列关系,即方 法实现者必须同时满足有所并列ensures子句的要求。
·副作用范围限定(side-effects)
副作用指方法在执行过程中会修改对象的属性数据或者类的静态成员数据,从而给后续方法的执行带来影响。经常用assignble表示可赋值,modifiable表示可修改。
其中public normal_behavior表示接下来的部分为方法的正常功能给出的规格。public exceptional_behavior 表示异常功能给出的规格,用signals子句抛出异常。
3.类型规格
不变时invariant:要求在所有可见状态下都必须满足的特性
状态变化约束constraint:对象在变化时满足的约束
4.应用工具链
OpenJML: OpenJML is a program verification tool for Java programs that allows you to check the specifications of programs annotated in the Java Modeling Language.
JMLUnitNG: JMLUnitNG is an automated unit test generation tool for JML-annotated Java code, including code using Java 1.5+ features such as generics, enumerated types, and enhanced for loops.
二、部署SMT Solver
没太搞懂。。。
三、部署JMLUnitNG
我按照评论区大佬的帖子安装了openJML和JMLUnitNG并尝试进行自动化测试
以下是被测试的代码
在对Demo.java使用jmlunitng后自动生成的一系列java(我已经编译过了所以class也在)
但是在我接下来测试的时候出现了问题,无论我怎么输入java -cp jmlunitng.jar Demo/Demo_JML_Test总会提示我找不到或无法加载该测试文件,第一次接触JMLUnit,网上的资源也太少了,我实在是不会弄了……
看了很多大佬的博客,jmllunitng生成的大部分是极端数据,0或者MAX_INT之类的,确实能帮忙测一些人工不太好测的数据,而且快捷便利。
四、架构分析
1.第一次作业
第一次作业要求实现的是path类和pathcontainer类,第一次我按照JML规格给出的,在path类设置了一个ArrayList类用于存点列。然后在PathContainer中设置了HashMap用于对应pathid和path。
2.第二次作业
第二次作业我去看了Graph.java,发现是直接继承了Path.java也就是说,这次从JML给出的规格来讲,应该是不需要重构的,只需要添加上新接口的方法就行了。但是实际上,因为我第一次作业的性能不太好,所以在第二次作业中,添了一个HashMap用于储存,add,remove等方法都增加了新的部分。
3.第三次作业
第三次作业的接口RailSystem.java, 依然是继承了上次Graph接口, 这次我基本没有重构了。但是因为这次需要添加新的找最小票价和最小不满意度的方法,所以我增加了新的容器用来做图,然后相应的方法也更新了。
我这里多出了一个Dijkstra类,其实完全没有面对对象的封装思想,完全是为了减少RailSystem中的代码行数才弄出来的,我写完后觉得写的太难看了,但是当时赶deadline就比较难受。
老师其实在最后一次作业的时候提示我们说要再抽象出一些类,用于统一管理图之类的,但是我当时就没太听懂,然后写的时候觉得JML规格也没说,就没分,然后果然代码行数超限制了。我觉得我这里写的时候还是被JML规格给框死了,缺少在JML下灵活应变的那种感觉。
五、bug分析
我这几次都没有出现wrong answer这样的逻辑错误,而在第一次作业和第三次作业中都出现了TLE的情况。所谓在性能出现问题之前性能都不是问题,但是一旦出现问题可能就要面临重构。所以我第一次作业修改bug的时候把找不同点那个方法给重构了,第三次作业因为重构太困难,而且我也想不到其他能提高性能的方法了,就没有改bug。
其实我觉得这次会有卡性能这个问题,是对我们在满足JML规格的情况下,如何灵活运用算法的一种考验。也是对JML的一种理解,不是说JML就把怎么写代码规定死了,而是在满足一定规则的情况下又有相对的自由。
六、心得体会
我觉得JML规格让我的代码更工程化了。不同于前两个单元在学的是比较具体的封装多态继承抽象和多线程,JML规格不能让我们会写更多的程序,但是让我们成为了更合格的程序员。JML规格写好了整个代码会非常整洁美观。当然JML规格也是有效避免我们自己做出bug的手段。而JML链接工具库更是给一二单元是对debug无从下手的我提供了一个快速,自动化地debug手法。