2019北航OO第三单元
一、 梳理JML语言的理论基础、应用工具链情况
JML是一种行为接口规格语言,基于Larch方法构建,BISL提供了对方法和类型的规格定义手段,所谓接口即一个方法或类型外部可见的内内筒。通过在JAVA代码中增加一些符号,并用这些符号来标识一个方法是干什么的,却并不关心它的实现。使用JML我们就能够描述一个方法的预期的功能不管他如何实现。
1.注释结构
JML以javadoc注释的方式来标识规格,每行都以@起头。有两种注释方式,行注释和块注释。其中行注释的表示方式为//@annotation,块注释的方式为/*@ annotation @ */。按照javadoc习惯,JML注释一般放在被注释成分的近邻上部。
2.JML表达式
2.1原子表达式
1.\result:表示一个非void类型的方法执行所获得的结果,即方法执行后的返回值。
2.\old(expr):用来表示一个表达式expr在相应方法执行前的取值。该表达式涉及到评估expr中的对象是否发生变化,遵从JAVA引用规则,即对一个对象引用而言,只能判断本身是否发生变化,而不能判断引用所指向的对象实体内容是否发生变化。
3.\not_assigned(x,y,…):用来表示括号中的变量是否在方法执行过程中被赋值。如果没有被赋值返回true,否则返回false。实际上,该表达式主要用于后置条件的约束表示上,即限制一个方法的实现不能对列表中的变量进行赋值。
4.\not_modified(x,y,…):与上面的\not_assigned表达式类似,该表达式限制括号中的变量在方法执行期间的取值未发生变化。
5.\nonnullelements(container):表示container对象中存储的对象不会有null。
6.\typeof(expr):该表达式返回expr对应的准确类型。
2.2量化表达式
1.\forall:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。
2.\exists:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。
3.\sum:分会给定范围内的表达式的和。
4.\product:返回给定范围内的表达式的连乘结果。
5.\max:返回给定范围内的表达式的最大值,
6.\min:返回给定范围内的表达式的最小值。
7.\num_of:返回指定变量中满足相应条件的取值个数。
2.3集合表达式
集合构造表达式:可以在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。
2.4操作符
1.子类型关系操作符:E1<:E2 ,如果类型E1是类型E2的子类型(sub type),则该表达式的结果为真,否则为假。如果E1和E2是相同的类型,该表达式的结果也为真。
2.等价关系操作符:b_expr1<==>b_expr2 或者 b_expr1<=!=>b_expr2 ,其中b_expr1和b_expr2都是布尔表达 式,这两个表达式的意思是 b_expr1==b_expr2 或者 b_expr1!=b_expr2 。
3.推理操作符:: b_expr1==>b_expr2 或者 b_expr2<==b_expr1 。对于表达式 b_expr1==>b_expr2 而言,当 b_expr1==false ,或者 b_expr1==true 且 b_expr2==true 时,整个表达式的值为 true 。
4.变量引用操作符:。\nothing指示一个空集;\everything指示一个全集。
3.方法规格
1.前置条件:对方法输入参数的限制,如果不满足前置条件,方法执行结果不可预测。通过requires子句表示。
2.后置条件:对方法执行结果的限制,如果执行结果满足后置条件,则表示方法执行正确,否则执行错误。其中ensures是JML关键词。
3.副作用:副作用指方法在执行过程中会修改对象的属性数据或者类的静态成员数据,从而给后续方法的执行带来影响。使用关键词assignable和modifiable。
4.其他
1./*@pure@*/:指不会对对象的状态进行改变,也不需要提供输入参数,这样的方法无需描述前置条件,也不会有任何副作用,且执行一定会正常结束。
2.\forall和\exists:前置条件或后置条件需要对不止一个变量进行约束,往往是需要对一个容器中的所有元素进行约束,这时就需要使用\forall或\exists表达式
3.\public normal_behavior和\public exception_behavior:为了有效的区分方法的正常行为和异常行为,提供了这两种表达式。其中public指相应的规格在所在包范围内的所有其他规格处都
可见。Also用来指明,除了正常功能规格之外,还有一个异常功能规格。
4.\signals(***Exception e)b_expr:强调满足某个条件时抛出相应的异常。
5.类型规格
类型规格指针对Java程序中定义的数据类型所设计的限制规则,一般而言,就是指针对类或接口所设计的约束规则。 从面向对象角度来看,类或接口包含数据成员和方法成员的声明及或实现。不失一般性,一个类型的成员要么是静态 成员(static member),要么是实例成员(instance member)。一个类的静态方法不可以访问这个类的非静态成员变量 (即实例变量)。静态成员可以直接通过类型来引用,而实例成员只能通过类型的实例化对象来引用。因此,在设计 和表示类型规格时需要加以区分。
1. 不变式invariant:要求在所有可见状态下都必须满足的特性。(可见状态包括:对象的有状态构造方法(用来初始化对象成员变量初值)的执行结束时刻,在调用一个对象回收方法(finalize方法)来释放相关资源开始的时刻,在调用对象o的非静态、有状态方法(non-helper)的开始和结束时刻,在调用对象o对应的类或父类的静态、有状态方法的开始和结束时刻,在未处于对象o的构造方法、回收方法、非静态方法被调用过程中的任意时刻,在未处于对象o对应类或者父类的静态方法被调用过程中的任意时刻)
2. 状态变化约束constraint:。用constraint来对前序可见状态和当前可见状态的 关系进行约束
应用工具链:主要有openJML:用来检测JML语法的正确性;JMLUnitNG:根据JML对给定的单元进行测试。
二、部署JMLUnitNG/JMLUnit,针对Graph接口的实现自动生成测试用例, 并结合规格对生成的测试用例和数据进行简要分析
不知道哪个环节出了问题,编译的时候一直报错,一会儿找不到类,一会编译错误,生成的文件也不能运行。弄了一下午也没好。。。。。等我问明白再回来补上吧。
三、按照作业梳理自己的架构设计,并特别分析迭代中对架构的重构
第九次作业:
MyPath中使用了ArrayList保存结点序列。MyPathContainer中使用HashMap来保存路径,由于add 、remove指令较少,在add、remove的时候对一些信息(如不同结点个数)进行了修改,以便直接返回。
第十次作业:
在第一次作业的基础上添加了查询边是否存在,以及求最短路径等指令。基本架构不变,增加了二维数组来保存边、最短路径等信息。其中求最短路径用了简单粗暴的floyd算法。
第十一次作业:
将图延伸为地铁问题,添加了求最少换乘、最少票价、最少不满意度等问题。对上次作业进行了小小的修改,基本不变,还是用二维数组来存储信息,并且使用floyd算法求最短路径。使用了讨论区wjy大佬的算法,将最少换乘、最少票价、最少不满意度转换为同一图中不同边权的问题。
四、按照作业分析代码实现的bug和修复情况
第九次作业:没有发现bug
第十次作业:一个bug。产生原因:floyd写错了!!!不知道咋想的,floyd循环的范围写错了,竟然还过了弱测!
第十一次作业:一个bug,惊天大bug,竟然又过了弱测!!!产生原因:进行了封装,封装之后传错参数了。。。我保证以后变量命名的时候,一定命一个独一无二的名字!!!为什么要让他们长的那么像!!
五、阐述对规格撰写和理解上的心得体会
有了规格之后,对代码的理解和检查有了更好的方向,非常清晰的描述了这段代码是做什么的,逻辑性非常强,对读你代码的人,对减少bug的出现,对后续查找bug有很大帮助。
然后就是每次都要说,但是每次都犯的毛病,就是不认真。这次因为一点小问题没修好,强测炸的惨不忍睹。但是还是要赞美wjy大佬在第十一次作业中提出的算法,真的是非常巧妙,受益良多。