oo第三单元总结
JML理论基础
JML是用于对Java程序进行规格化设计的一种表示语言,用来约束java代码中类和方法编写的状态和行为,形成一种契约式编程。实现者根据JML的约束来编写程序,既可以使结构更加清晰,还可以帮助实现者对代码进行测试。
注释结构
JML以javadoc注释的方式来表示规格,有两种注释方式,行注释和块注释。行注释表示方式为//@annotation,块注释方式为/* @ annotation @*/。
表达式
JML表达式是对Java表达式的扩展,主要分为原子表达式、量化表达式、集合表达式、操作符几种。
原子表达式包括\result表达式,\old表达式这些,主要表示对返回值或对象等的约束。
量化表达式包括\forall表达式,\exists表达式这些,这两个表达式类似于离散数学中的任意和存在的意思。
方法规格
方法规格式的核心内容包含三部分,前置条件、后置条件和副作用约定。
前置条件是对方法输入参数的限制,用requires子句来表示。后置条件是对方法执行结果的限制,用ensures子句来表示。副作用约定指方法在执行过程中对输入对象或this对象进行修改的限制,使用关键词assignable或modifiable来表示。
根据前置条件的约束判断该进行何种操作,大致分为normal_behavior
,expcetional_behavior
,不同的输入参数通过前置条件判断然后执行对应的处理操作。expcetional_behavior
多用signals子句来抛出对应异常。
类型规格
对于课程而言,类型规格主要涉及不变式限制和约束限制。
不变式(invariant)就是要求在所有可见状态下都必须满足的特性(就是对所有修改后的状态的约束)。状态变化约束(constraint)则是对前序可见状态和当前可见状态的关系进行约束(也就是修改后的状态相对于修改前状态的约束)。
以上只是对JML语言的大体结构进行了描述,还有更多细节的东西就不赘述了。
JML工具链应用
OpenJML
OpenJML主要用于JML的语法检查分析,通过运行java -jar openjml.jar -exec Solvers-windows\z3-4.7.1.exe -esc test\*.java报错如下图。
开始完全不知道是什么意思,通过求助同学及搜索引擎才知道原来是三目运算符的问题,将三目运算符修改一下就好了。
JMLUnitNG
JMLUnitNG主要基于JML对代码自动生成测试代码并测试。
先通过java -jar jmlunitng-1_4.jar test/Group.java
生成测试代码。
再通过javac -cp jmlunitng-1_4.jar test/*.java
java -jar openjml.jar -rac test/Group.java test/Person.java
java -cp jmlunitng-1_4.jar test.Group_JML_Test
开始测试。
可以发现JMLUnitNG只是针对极端数据进行了测试,覆盖面不全(可以说是弱测),通过该测试很难保证程序就是正确的,所以感觉这个好像有点鸡肋,作用不大。
架构分析
整体架构官方接口已给出,要做的只是具体实现。MyPerson类中的容器主要选用的是ArrayList,MyNetWork则主要使用Hashmap,其余的除了少数几个需要优化的算法,基本上都是按照规格对应实现的,所以直接对某些方法使用的算法进行分析。
第一次作业的isCircle直接使用bfs算法。
第二次作业对于Group类中的getRelationSum、getValueSum等方法采用缓存机制(在组内新增Person时或组内两Person之间新增Relation时更新),避免超时。
第三次作业将isCircle改为并查集方法;queryMinPath方法采用堆优化的dijkstra算法,通过容器PriorityQueue实现堆优化。queryStrongLinked则采用先bfs寻找一条路径,然后依次删除这条路径上的每一个点,再用bfs看是否能找到另一条路径,如果都能找到另一条路径就说明是点双连通分量。由于采用并查集方法,在addPerson,addRelation不断更新blockSum,queryBlockSum的复杂度变为O(1),直接返回blockSum即可。
bug分析
第一次作业就由于isCircle没有考虑到起点和终点是同一点的情况,导致强测炸裂。修复只需加上一句简单的判断即可。
第二次作业由于getValueSum没有使用缓存机制导致超时,解决方案自然就是使用缓存机制。互测中也hack到别人getValueSum未使用缓存机制的超时bug,怪只怪自己强测前不充分测试有些方法是否会超时。
第三次作业由于做好了算法优化,未出现bug,互测中也未发现bug。
心得体会
刚开始接触JML时其实是感觉这个用处不大,但开始自己写作业之后才发现有好的JML规格写起代码来是多么舒服。而且严格检查好每个细节,也很难出现bug。而且JML并不是我开始所认为的是一种束缚,JML只是给出了规格的约束,具体实现依然可以自由发挥,只要保证规格就可以了。
多次作业下来,慢慢发现了JML的众多优点,每次写代码前先读一遍全部规格,就能很清晰的了解要实现的功能及代码架构等,然后再根据具体方法规格进行代码的编写,这样对于实现者而言任务量大大减少,而且JML还保证了实现者不会一时疏忽忽略了某些情况,只要按照规格来写,就能保证正确性。
这三次作业除了对于JML有了一定程度的了解,也对算法把握进行了一定的复习,总而言之,这单元收获还是很多的。