BUAA_OO_HOMEWORK_UNIT3_总结与反思

JML基础

理论基础

JML是用于对Java程序进行格式化设计的表示语言,其用处在于开展规格化设计,使得交给实现人员的不是较为模糊的自认语言,而是逻辑严格的规格。

JML主要包括三个部分,分别是前置条件,副作用范围限定和后置条件。

requires子句定义该方法的前置条件,表示要求调用者确定条件为真,多个条件可以用 && 或 || 这类逻辑符号进行连接。对于方法中不同的表现可能有不同的前置条件。

assignable跟着副作用范围限定,会列出这个方法能够修改的类成员属性,除了自己定义的类成员之外,还有可能存在两种关键词,如/nothing,代表这个方法不对任何成员属性进行修改,以及/everthing,即作用域内可见的所有类成员变量和方法输入对象都可以赋值或者修改。

ensures子句表示后置条件,表示要求实现者确定此方法执行返回结果满足后置条件,同样的,多个条件可以用逻辑符号进行连接。

原子表达式,包括\result表达式,表示一个非void类型的方法只剩所获得的结果。\old(exp)表达式,代表exp在相应方法执行前的取值。\not_assigned(x,y……)用来保证括号中的变量没有被赋值。\not_modified(x,y……)用来保证括号内的变量在方法执行期间的取值未变化。\type(exp)表示返回exp对应的类型。

量化表达式,包括\forall,即全称量词,表示对给定范围的所有元素都满足约束,\exists,即存在两次,表示对于给定范围内的元素,存在某个元素满足相应约束。\sum为返回给定该范围内表达式的和,\product则为乘积,\max和\min分别为最大值和最小值,\num_of为返回指定变量中满足相应条件的取值个数。

另外,为了区分方法的正常功能和异常行为,还有normal_behavior和exceptional_behavior。

应用工具链

JML包括JMLUnit,JMLUnitNG,STLSolver等。
JMLUnit可以对JML进行静态检查,JMLUnitNG可以按照JML自动生成测试用例。

JMLUnitNG测试

BUAA_OO_HOMEWORK_UNIT3_总结与反思_第1张图片
(跑动了jmlunitng的人都哭了)
可以发现jmlunitng主要还是在测试null和int的极限情况,由于数据类似此处仅截取前两个方法的运行结果。因而jmlunitng尽管能自动生成测试用例,但实用性不高。

代码架构

考虑到类图和官方给出的基本没有区别,在此处不再放出类图。

第一次作业

在第一次中,我基本按照JML中的描述直接实现了代码,而没有进行过多的架构设计。

第二次作业

在第二次作业中,我仍然是按照JML中的描述直接实现了代码,但是在强测中我的程序超时,发现直接按照JML的描述实现的方法复杂度过高,于是在bug修复中我重构了本次的代码,在person类和network类中都采取了hashmap容器和arraylist容器相结合的方式,以保证能快速寻找到对象的同时可以方便的进行遍历。
isCircle是的实现是通过bfs进行实现的,其复杂度为\(O(n+m)\),比较高,但是可以通过测试。
同时求AgeVar时先预存AgeMean的值,保证AgeVar的复杂度为\(O(n)\)的。
另外要查询一个group中的relationsum和valuesum,鉴于双循环统计的复杂度较高,我考虑添加两个缓冲,在addrelationship和addperson时更新即可。

第三次作业

在第三次作业中,新增添了delete之外,还新增了查询最短路,查询连通块,查询两点是否强连通和查询年龄在范围内人数这四个方法。对于delete方法我仍然是在上次构架的基础上进行对两个缓冲的修改。

对于查询最短路方法,我选择采用堆优化的dijkstra算法,因为此算法时间稳定,而且时间复杂度较低。

对于查询连通块,我选择采用dfs的方法,遍历每一个点,如果该点没有visit过,就在答案上加一,然后从此点开始搜索,对每一个搜索到的点标记visit即可。

对于查询是否强连通,我选择采用了点双连通分量的做法,我的做法是保留所有点双连通分量,然后查询所有大小大于2的点双连通分量,看查询的两个点是否都在这一强连通分量里,如果有,那么两点间强连通。

对于查询年龄人数,只要遍历即可。

bug与修复

第一次作业

在第一次作业中,我的强测得到了5分:(??怎么会这样??),然后发现是因为没有进行充分测试,所以导致低级错误没有被发现,这个错误是:只加了单向边。这给我提了个醒,之后的作业需要进行全面的测试。

第二次作业

在这一次作业中,我吸取上次的经验进行了全面的正确性测试,然后在强测中得到了20分:(??怎么又是这样??),然后发现我的qurey_name_rank方法因为复杂度过高超时,这给我提了个醒,之后的作业不仅需要进行全面的正确性测试,还需要认真的检查时间复杂度。

第三次作业

在这一次作业中,我吸取上次的经验进行了全面的正确性测试和时间复杂度检查,然后再强测中得到了95分:(我累了——),原因尚不清楚,可能是因为我的程序使用了两种方法倒是常数过大,在bug修复中同样的代码重新交了一次就通过了这一测试点。

作业心得体会

从这三次作业中,我认识到,对于JML不能简单的将其理解为从JML翻译代码,而是应该理解其需求后进行自己设计,同时在完成程序后还应该进行静态检查,单元测试,复杂度检查等等。

你可能感兴趣的:(BUAA_OO_HOMEWORK_UNIT3_总结与反思)