OO_第三单元总结
1.JML语言的理论基础及其工具链
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等工具以静态方式来检查代码实现对规格的满足情况。
一般而言,JML有两种主要的用法:
(1)开展规格化设计。这样交给代码实现人员的将不是可能带有内在模糊性的自然语言描述,而是逻辑严格的规格。
(2)针对已有的代码实现,书写其对应的规格,从而提高代码的可维护性。这在遗留代码的维护方面具有特别重要的意义。
语法方面,JML有它自身的语法表达式,在基于JAVA语法基础上,新增了一些操作符和和原子表达式组成的,例如原子表达式:/result
、量化表达式 /forall
、集合表达式、操作符:<
(表示是子类)等。
规格方面,主要包括:
(1)前置条件
前置条件使用requires语句来表示,requires P意味着需要确保P的值为真。需要注意的是,requires语句如果多行一起出现,是并列条件,需要全部满足。
(2)后置条件
后置条件使用ensures语句来表示,ensures P意味着需要确保P的值为真。同样,ensures语句如果多行一起出现,是并列条件,需要全部满足。
(3)副作用限定
在方法执行时,为了满足前置条件与后置条件,我们可能会对对象做一些与前置条件和后置条件无关的改动,也就是副作用。有时候需要通过副作用限定来对方法实现进行约束。
(4)其他
当前置条件与后置条件需要对多个对象进行约束时,可以与用\forall或者\exists表达式结合使用。
工具链
-
OpenJML:用来做jml规格的校验,可以对代码进行JML规格的语法的静态检查,还支持使用SMT Solver动态地检查代码对JML规格满足的情况,因此OpenJML一般也自带有其支持的JML solver。
-
JML UnitNG:强大之处在于可以根据规格的实现自动生成TestNG测试样例,即根据JML描述自动生成与之符合的测试样例,重点会检测边界条件。
2.SMT Solver部署
SML solver是利用openJML对程序进行静态检查,查找代码中可能出现的错误,并以此建立shell文件,之后可以利用openjml命令进行检查了。具体命令如下:
#!/bin/bash
java -jar ../openjml.jar "$@"
自己对SMT Solver的掌握不深,因此也不过多赘述.
3.JMLUnitNG部署
(1)通过jmlunitng生成测试代码
>java -jar jmlunitng.jar test.java
(2)再用javac 编译 JMLUnitNG 生成文件
>javac -cp jmlunitng.jar *.java
(3)Openjml 编译再运行
>java -jar ..\openjml.jar -rac test.java
(4)最后运行
>java -cp jmlunitng.jar test_JML_Test
4.作业分析与BUG修复
第一次作业
第一次作业实现逻辑完全按照JML的规格来实现,也没有太大的问题。唯一被检查出来的bug就是对异常抛出的处理顺序存在一定问题,产生错误的异常抛而导致失分。
方法复杂度分析:
Type Name | Method Name | LOC | CC | PC |
---|---|---|---|---|
Main | main | 4 | 1 | 1 |
MyNetwork | MyNetwork | 3 | 1 | 0 |
MyNetwork | contains | 10 | 3 | 1 |
MyNetwork | getPerson | 8 | 3 | 1 |
MyNetwork | addPerson | 18 | 5 | 1 |
MyNetwork | addRelation | 18 | 5 | 3 |
MyNetwork | queryValue | 13 | 3 | 2 |
MyNetwork | queryConflict | 8 | 2 | 2 |
MyNetwork | queryAcquaintanceSum | 8 | 2 | 1 |
MyNetwork | compareAge | 8 | 2 | 2 |
MyNetwork | compareName | 8 | 2 | 2 |
MyNetwork | queryPeopleSum | 3 | 1 | 0 |
MyNetwork | queryNameRank | 16 | 4 | 1 |
MyNetwork | isCircle | 21 | 6 | 2 |
MyPerson | MyPerson | 6 | 1 | 4 |
MyPerson | getId | 3 | 1 | 0 |
MyPerson | getName | 3 | 1 | 0 |
MyPerson | getCharacter | 3 | 1 | 0 |
MyPerson | getAge | 3 | 1 | 0 |
MyPerson | equals | 8 | 2 | 1 |
MyPerson | isLinked | 15 | 4 | 1 |
MyPerson | queryValue | 8 | 3 | 1 |
MyPerson | getAcquaintanceSum | 3 | 1 | 0 |
MyPerson | compareTo | 3 | 1 | 1 |
MyPerson | addRelation | 4 | 1 | 2 |
第二次作业
第二次作业强测试炸裂的原因是自己依旧在第一次作业的代码上继续增添方法,完全没有考虑对方法进行优化,最后大量测试样例抛出CPU运行时间过长的bug。修复阶段主要对二重循环采用了类似缓存的手段记录一些数据从而避免超时。
方法复杂度分析:
Type Name | Method Name | LOC | CC | PC |
---|---|---|---|---|
Main | main | 4 | 1 | 1 |
MyGroup | MyGroup | 3 | 1 | 1 |
MyGroup | getId | 3 | 1 | 0 |
MyGroup | equals | 8 | 2 | 1 |
MyGroup | addPerson | 5 | 1 | 1 |
MyGroup | hasPerson | 10 | 3 | 1 |
MyGroup | getRelationSum | 3 | 1 | 0 |
MyGroup | getValueSum | 3 | 1 | 0 |
MyGroup | getConflictSum | 13 | 3 | 0 |
MyGroup | getAgeMean | 12 | 3 | 0 |
MyGroup | getAgeVar | 12 | 3 | 0 |
MyGroup | getPeopleSum | 3 | 1 | 0 |
MyNetwork | MyNetwork | 3 | 1 | 0 |
MyNetwork | contains | 10 | 3 | 1 |
MyNetwork | getPerson | 8 | 3 | 1 |
MyNetwork | addPerson | 18 | 5 | 1 |
MyNetwork | addRelation | 14 | 5 | 3 |
MyNetwork | queryValue | 13 | 3 | 2 |
MyNetwork | queryConflict | 8 | 2 | 2 |
MyNetwork | queryAcquaintanceSum | 8 | 2 | 1 |
MyNetwork | compareAge | 8 | 2 | 2 |
MyNetwork | compareName | 8 | 2 | 2 |
MyNetwork | queryPeopleSum | 3 | 1 | 0 |
MyNetwork | queryNameRank | 14 | 4 | 1 |
MyNetwork | isCircle | 21 | 6 | 2 |
MyNetwork | addGroup | 8 | 2 | 1 |
MyNetwork | getGroup | 8 | 3 | 1 |
MyNetwork | addtoGroup | 16 | 5 | 2 |
MyNetwork | queryGroupSum | 3 | 1 | 0 |
MyNetwork | queryGroupPeopleSum | 8 | 2 | 1 |
MyNetwork | queryGroupRelationSum | 8 | 2 | 1 |
MyNetwork | queryGroupValueSum | 8 | 2 | 1 |
MyNetwork | queryGroupConflictSum | 8 | 2 | 1 |
MyNetwork | queryGroupAgeMean | 8 | 2 | 1 |
MyNetwork | queryGroupAgeVar | 8 | 2 | 1 |
MyPerson | MyPerson | 6 | 1 | 4 |
MyPerson | getId | 3 | 1 | 0 |
MyPerson | getName | 3 | 1 | 0 |
MyPerson | getCharacter | 3 | 1 | 0 |
MyPerson | getAge | 3 | 1 | 0 |
MyPerson | equals | 8 | 2 | 1 |
MyPerson | isLinked | 13 | 4 | 1 |
MyPerson | queryValue | 8 | 3 | 1 |
MyPerson | getAcquaintanceSum | 3 | 1 | 0 |
MyPerson | compareTo | 3 | 1 | 1 |
MyPerson | addRelation | 5 | 1 | 2 |
MyPerson | getValueSum | 3 | 1 | 0 |
第三次作业
第三次作业依旧是和上次相同,自己对新增的图问题方法采用了保守的算法而使得运行时间过长,修复思路是采用时间复杂度更小的算法,在讨论课上同学所提出来的一些算法给了我很大的启示。
方法复杂度分析:
Type Name | Method Name | LOC | CC | PC |
---|---|---|---|---|
Main | main | 4 | 1 | 1 |
MyGroup | MyGroup | 3 | 1 | 1 |
MyGroup | getId | 3 | 1 | 0 |
MyGroup | equals | 8 | 2 | 1 |
MyGroup | addPerson | 3 | 1 | 1 |
MyGroup | hasPerson | 3 | 1 | 1 |
MyGroup | getRelationSum | 12 | 4 | 0 |
MyGroup | getValueSum | 11 | 4 | 0 |
MyGroup | getConflictSum | 13 | 3 | 0 |
MyGroup | getAgeMean | 12 | 3 | 0 |
MyGroup | getAgeVar | 12 | 3 | 0 |
MyGroup | delPerson | 3 | 1 | 1 |
MyGroup | getPeopleSum | 3 | 1 | 0 |
MyNetwork | MyNetwork | 3 | 1 | 0 |
MyNetwork | contains | 10 | 3 | 1 |
MyNetwork | getPerson | 8 | 3 | 1 |
MyNetwork | addPerson | 20 | 5 | 1 |
MyNetwork | addRelation | 16 | 5 | 3 |
MyNetwork | queryValue | 13 | 3 | 2 |
MyNetwork | queryConflict | 8 | 2 | 2 |
MyNetwork | queryAcquaintanceSum | 8 | 2 | 1 |
MyNetwork | compareAge | 8 | 2 | 2 |
MyNetwork | compareName | 8 | 2 | 2 |
MyNetwork | queryPeopleSum | 3 | 1 | 0 |
MyNetwork | queryNameRank | 14 | 4 | 1 |
MyNetwork | isCircle | 21 | 6 | 2 |
MyNetwork | addGroup | 8 | 2 | 1 |
MyNetwork | getGroup | 8 | 3 | 1 |
MyNetwork | addtoGroup | 16 | 5 | 2 |
MyNetwork | queryGroupSum | 3 | 1 | 0 |
MyNetwork | queryGroupPeopleSum | 8 | 2 | 1 |
MyNetwork | queryGroupRelationSum | 8 | 2 | 1 |
MyNetwork | queryGroupValueSum | 8 | 2 | 1 |
MyNetwork | queryGroupConflictSum | 8 | 2 | 1 |
MyNetwork | queryGroupAgeMean | 8 | 2 | 1 |
MyNetwork | queryGroupAgeVar | 8 | 2 | 1 |
MyNetwork | queryAgeSum | 9 | 3 | 2 |
MyNetwork | delFromGroup | 16 | 4 | 2 |
MyNetwork | queryMinPath | 18 | 4 | 2 |
MyNetwork | dijstra | 45 | 15 | 2 |
MyNetwork | queryStrongLinked | 13 | 3 | 2 |
MyNetwork | tarjan | 8 | 2 | 2 |
MyNetwork | queryBlockSum | 21 | 5 | 0 |
MyNetwork | borrowFrom | 20 | 6 | 3 |
MyNetwork | queryMoney | 15 | 4 | 1 |
MyPerson | MyPerson | 6 | 1 | 4 |
MyPerson | getId | 3 | 1 | 0 |
MyPerson | getName | 3 | 1 | 0 |
MyPerson | getCharacter | 3 | 1 | 0 |
MyPerson | getAge | 3 | 1 | 0 |
MyPerson | equals | 13 | 3 | 1 |
MyPerson | isLinked | 8 | 2 | 1 |
MyPerson | queryValue | 8 | 3 | 1 |
MyPerson | getAcquaintanceSum | 3 | 1 | 0 |
MyPerson | compareTo | 3 | 1 | 1 |
MyPerson | addRelation | 4 | 1 | 2 |
MyPerson | getAcquaintance | 3 | 1 | 0 |
MyPerson | getValue | 3 | 1 | 0 |
5.心得体会
三次作业整体难度都不难,但都强调的是自己的测试能力,第二次作业失分的很大原因就是自己的测试样里过短而导致自己没有将正确度之外的一些要素考虑进去,这也算是给自己提了个醒,也让得自己更加重视测试的重要性,毕竟靠评测机不如靠自己。