oo第三单元总结
一、JML理论基础及工具链分析
(一)JML理论基础
1.简介
JML(JAVA Modeling Language, Java建模语言),是对JAVA代码规格的设计。其主要作用是在编写类和方法之前规定好其行为,包括方法的输入数据要求、返回值以及可能会改变的数据。这种建模语言的优势在于可以更加精确的描述JAVA方法的行为,规避了自然语言在描述问题时的二义性,为自动化测试提供了很多方便。但是其缺点在于可读性要明显弱于自然语言,对于一些问题的描述过于复杂。
2.基本语法
(1)JML以javadoc注释的方式来表示规格,每行都以@起头。有两种注释方式,行注释和块注释。
(2)requires子句定义该方法的前置条件。
(3)副作用范围限定,assignable列出这个方法能够修改的类成员属性,\nothing是个关键词,表示这个方法不对
任何成员属性进行修改。
(4)ensures子句定义了后置条件,即该方法执行后必须满足的条件。
常用原子表达式:
(1)\result表达式:表示一个非void 类型的方法执行所获得的结果,即方法执行后的返回值。
(2)\old( expr )表达式:用来表示一个表达式expr 在相应方法执行前的取值。
(3)\not_assigned(x,y,...)表达式:用来表示括号中的变量是否在方法执行过程中被赋值。如果没有被赋值,返回为
true ,否则返回false 。
(4)\not_modified(x,y,...)表达式:与上面的\not_assigned表达式类似,该表达式限制括号中的变量在方法执行期间的取
值未发生变化。
常用量化表达式:
(1)\forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。
(2)\exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。
(3)\sum表达式:返回给定范围内的表达式的和。
(4)\max表达式:返回给定范围内的表达式的最大值。
(5)\min表达式:返回给定范围内的表达式的最小值。
(二)常用工具链
openJML与JMLUnitNG。
第十周研讨课上曾有同学专门介绍了这两种工具链的使用方法,这是对这两种工具的最早接触。
1.openJML
openJML的基础功能是检查JML的语法是否正确,另外还可以对动态运行的代码进行检查。
参考博客https://www.cnblogs.com/lutingwang/p/openjml_basic.html,但由于openJML年代久远(本人太菜),在配置过程中遇到了大量问题,遂放弃,在作业中也未能使用到
2.JMLUnitNG
二、作业架构设计
本单元由于给出了较为详细的代码规格供参考,在架构设计上没有多加考虑,架构基本按照指导书的基础要求来实现的。在这里仅贴上第三次作业的架构图。
本单元作业的主题是构建一个人际关系网络,如果对架构设计有所思考,应该从图的构造出发,采取图的数据结构,这样很多方法也能较为容易地完成吧。
三、bug分析
本次单元总结的重头戏(因为每次都有bug,甚至第一次作业没进互测)
由于给了较为完整的规格以及架构,bug均出现在规格的实现上
第一次作业
第一次作业整体来说较为容易,正因如此,一点小小的失误就能造成强测爆炸。bug出现在方法addRelation的实现上,当id1 == id2时,该方法规格没有任何要求,也就意味着什么都不执行。这里我看懂了,在与同学交流的时候说的也是正确的,但就在实现的时候出了差错,id1 == id2时抛出了异常
第二次作业
第二次作业中出现了3个bug,且错误类型均不相同,这导致我在后来的bug修复过程中十分头疼。其中RE的bug为求方差时未考虑分母为0的情况(明明在求平均数的方法中考虑到了但求方差就没考虑,不愧是我),WA的bug为求方差的方法中未平方,TLE则因为两个方法采用的循环嵌套导致复杂度达到了O(n^2)进而超时。在修复bug的过程中也换用了一些容器的类型。
第三次作业
第三次作业的问题除了del_from_group方法抛出异常实现有问题以外,还有算法上的bug。判断图中两点在同一环上,通过讨论区大佬们的分析,最后我采用的是搜索-删点的算法,原理是若两点在同一环上,则对于任意连接两点的路径,从图中删去该路径出首尾两点之外的任意一点,两点之间仍连通。但在算法实现未能达到这一理解,导致一些情况下出现错误
总体分析
可以看到三次作业的bug都不是出现在对于规格的理解,而是出现在代码实现上,其中三次作业中的部分甚至全部bug,都来源于粗心(笔误)。如果测试能够做的更加到位,就能很大程度上避免这些由于笔误而导致的错误了,还是自己过了中测之后没有认真再测试的锅。
四、体会与感想
1.JML理解起来不是很困难,主要的逻辑也只是命题逻辑,但写JML不是十分容易,有些点很容易考虑不到
2.单元测试真的很重要,至少要测试每个方法,保证方法基本正确