OO第三次博客作业
JML,即java建模语言,是一种为java量身定做的形式化的行为接口规范语言,用来规范java程序模块的行为及详细设计决策。它沿袭了BISL良好定义的形式语言,同时继承了DBC语言较强的执行能力。使用JML接口规范可以推动程序的自动化测试,从而减少单元测试的负担。
(一)JML语言的理论基础梳理以及应用工具链简介
JML语言作为一种规格抽象语言,它有自己坚实的理论基础,概括来说,就是下面四条。
- JML语言可以精确地描述模型的约束条件以及相互之间的关系,非常适合于模型驱动的开发。
- JML断言和表达式不会对程序代码产生任何副作用,因此它对程序代码而言是安全的。
- JML采用扩展的java表达式的语法形式,是一种强类型语言。
- JML是DBC语言,即采用前置谓词、后置谓词、不变式等形式约束类和操作。
以下是JML语法整理
-
JML表达式
- (一)原子表达式
\result:表示一个非void类型的方法执行所获得的结果,即方法执行后的返回值。\old(expr):表示一个表达式expr在相应方法执行前的取值。\not_assigned(x,y,...):用来表示括号中的变量是否在方法执行过程中被赋值。如果没有被赋值,返回为true,否则返回false。\not_modified(x,y,...):该表达式限制括号中的变量在方法执行期间的取值未发生变化。\nonnullelements(container):表示container对象中存储的对象不会有null。\type(type):返回类型type对应的类型(Class),TYPE是JML采用的缩略表示,等同于Java中的 java.lang.Class。\typeof(expr):该表达式返回expr对应的准确类型。
- (二)量化表达式
\forall:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。\exists:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。\sum:返回给定范围内的表达式的和。\product:返回给定范围内的表达式的连乘结果。\max:返回给定范围内的表达式的最大值。\min:返回给定范围内的表达式的最小值。\num_of:返回指定变量中满足相应条件的取值个数。
- (三)集合表达式
- 可以在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。集合构造表达式的一般形式为:
new ST {T x|R(x)&&P(x)},其中的R(x)对应集合中x的范围,通常是来自于某个既有集合中的元素.
- 可以在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。集合构造表达式的一般形式为:
- (四)操作符
E1<:E2子类型操作符:如果类型E1是类型E2的子类型(sub type)或相同类型,则该表达式的结果为真,否则为假。b_expr1<==>b_expr2或b_expr1<=!=>b_expr2等价关系操作符:其中b_expr1和b_expr2都是布尔表达式。b_expr1==>b_expr2或b_expr1<==b_expr2推理操作符:相当于离散的->.\nothing或\everthing变量引用操作符:表示当前作用域访问的所有变量。前者空集,后者全集。变量引用操作符经常在assignable句子中使用.- 除此之外,JML可以正常使用java所定义的操作符。
- (一)原子表达式
-
方法规格
- (一)前置条件
requires:表示对方法参数的限制条件。 - (二)后置条件
ensures:对方法执行结果的限制。 - (三)副作用
assignable(可赋值)或者modifiable(可修改):对对象属性或类的静态成员的修改。
- (一)前置条件
-
其他
/*@ pure @ */:指该方法不需要任何参数,也没有任何前置条件和副作用。public normal_behavior和public exception_behavior:区分正常与异常。signals (***Exception e) b_expr:说明满足某个条件(b_expr为true)抛出相应异常。
-
类型规格
- 对数据状态要求:
invariantconstraint
- 对方法的要求
method specification
- 对数据状态要求:
JML相关的工具主要有OpenJML和JMLUnitNG, OpenJML用于编译, 主要用来检查JML语法的正确性. JMLUnitNG用于单元测试.
(二)部署JMLUnitNG/JMLUnit,针对Graph接口的实现自动生成测试用例
- 使用openJML编译需要检查的方法,并利用JMLunit生成测试用例。
- 我得到的测试结果如下:

(三)作业分析
第一次作业

第一次作业比较简单,在MyPath中我利用ArrayList来存储点的顺序,用来重构equals,同时用HashMap存储点来降低复杂度。在MyPathContainer中,我利用三个HashMap,分别用来存Id2Path,Path2Id,以及distinctNode。总体来说,第一次作业非常简单,只需要注意时间复杂度就可以了。
第二次作业

第二次作业,我的架构是MyGraph继承了第一次作业的MyContainer。算法使用的是BFS,存储结构使用的是嵌套HashMap存点,以及新建了类Edge(属性即边的两端),并且创建<Edge,Integer>的HashMap用来存储计算结果。
第三次作业

第三次作业同样是继承了第二次的作业。在算法上,我使用了Floyd与BFS相结合的算法,所以在属性上,我用两个容器存储了边。第三次作业要考虑时间复杂度,而且计算内容较多,所以比较难。在尝试过程中,想到了很多种方法,实际上我用的方法跑的很慢,事实证明助教和老师们是很仁慈的。
(四)bug修复
由于JML的使用,我觉得bug可以说是很少了,主要是时间复杂度的问题,其次就是细心程度。。
(五)心得体会
本单元主要内容是规格化设计,介绍了JML语言,并且在作业中利用它进行了规格抽象,这是以前从没有接触过的。正如同课堂上讲的,面向对象的过程其实是抽象的过程,面向对象语言实现了行为抽象,结构抽象,但是并没有实现规格抽象,而JML语言正是帮助我们做这件事情。
有了JML语言,我们可以更好地实现设计与实现的高度分离,当我们进行架构设计时只需考虑类和方法想要实现的功能和最终得到的结果,而不去关心具体的实现细节。同时搭配openJML的使用,我们可以做到对自己的代码进行白盒测试,而不是一味地进行黑盒测试。
本单元作业中,题目已经提供给了我们规格抽象,而我们主要思考的是如何根据规格去实现,写代码的时候目的明确了很多,思维也清晰了很多,我感受到了JML用好了是多么美妙的一件事情。但是我对规格化设计的理解还是停留在表面,我希望以后可以加油,熟悉习惯JML的使用。“工欲善其事,必先利其器”。
(六)一些小建议
在做作业的过程中,我们并没有训练当一个项目从零开始时编写JML的过程,我觉得这是一件更重要的事情,也许这也是老师和助教们在权衡了题目难度和时间花费上做的决定吧。本单元的作业虽然目的是为了让我们熟悉JML,但是我感觉除了写代码之前读一些JML以及openJML的使用,大多数的时间都花在了数据结构的时间复杂度和算法上,不过也有可能是我跑偏了。我感觉自己通过这单元的学习最大的感受其实是以前很少去在意容器的时间复杂度这一事情,但是现在却要从它使用的数据结构入手去了解它,而对于JML,还是停留在很浅显的层面。如果放在我面前是一个从零开始的项目,我目前可能还是不会写JML,因为没有习惯,没有写过,也不知道从何写起。
我希望以后可以更改一下本单元的作业要求,即使是降低题目难度,也应该让大家写一写JML,这样或许会让我们对JML的印象更深,也能学到更多东西。

浙公网安备 33010602011771号