OO第三单元总结

OO第三单元总结

目录
  • OO第三单元总结
    • 一、JML理论总结
      • 表达式
      • 方法规格
      • 类型规格
    • 二、应用工具链
    • 三、JMLUnitNG部署
    • 四、架构分析
      • 第一次作业
      • 第二次作业
      • 第三次作业
    • 五、bug分析与修复
    • 六、心得体会

本单元的内容是根据课程组给出的JML规格完成相应代码,整体项目主要是构建一个复杂的网络系统。总的来说功能实现不是主要难点,更加考察在实现JML规能的同时进行算法的优化。

一、JML理论总结

JML是对一种java的规格描述语言,以注释的形式写在java代码块中,用于描述类中各个方法的行为,其具有无二义性于,因此在一个大工程项目中便于团队之间的交流,避免文字注释出现的歧义。同时JML只关心功能,不关心具体实现,方便后续由程序自动对代码的正确性进行测试,这在一个大工程中也显得尤为重要。

JML语言的要素大值由三个:表达式,方法规格,类型规格。


表达式

表达式包括原子表达式,量化表达式,集合表达式,操作符等。这是JML中最核心的部分。

原子表达式中包括:\result, \old, \not_assigned等等,多是对单个的非容器型对象的描述。

量化表达式:\forall, \exisits, \sum等等,通常用于表达对一个集合的各种操作。

集合表达式:表示对集合本身属性的一些描述与限制。

操作符:多用于逻辑关系表达,如且、或、等价等等。


方法规格

方法规格就是对一个方法功能具体的描述,其核心有三个要素:前置条件后置条件副作用异常

前置条件:指使用该方法所必须满足的一些前提条件,不具备该条件则不能调用该方法。

后置条件:该方法要运行结束后要满足的条件。

副作用:该方法运行完成后,哪些成员属性改变了。如果为\nothing则表示不允许对任何成员进行修改。

异常:方法行为的一种,当满足一定条件时,要求方法抛出相应异常。

从中可以看出,JML不关心方法的实现过程,只关心条件和结果


类型规格

类型规格是对java中数据变量的约束,它不直接出现在方法规格中,但所有的方法规格都必须满足类型规格。

二、应用工具链

  • Junit:最常用的工具,可以方便的对某个方法生成测试代码的模板,必须自己编写测试用例即可轻松实现单元测试。
  • OpenJML:对JML规格语法的检查。
  • JMLUnitNG:通过JML规格,对类自动生成测试用例。

三、JMLUnitNG部署

JML的配置确实让人太抓狂,先是无论如何无法运行,后经大佬指点,将JDK版本切换为1.8,终于能够把JMLUnitNG运行起来了,但是第一次运行跳出不下白条警告信息和error,ok,众所周知,warning等于没有error,直接跳过。来看error,怎奈error信息里居然充满”马赛克“?,零星的几个单词字母被”马赛克“分割构不成完整的意义。想了一想,由于我是git bash运行的,bash可能信息编码格式不支持,于是改为了windows自带的cmd,终于呈现出了完整的信息,修改了一些由于语法格式的不支持,最终运行成功!

以下为对MyGroup()的自动测试结果

OO第三单元总结_第1张图片

OO第三单元总结_第2张图片

MyGroup()类中多是一些pure的get方法,以及一些对成员变量的add操作,从add的数据可以看出,测试对如0这类边界数据有一个良好的测试,但对于图的复杂操作测试效果不明。

四、架构分析

第一次作业

UML图

OO第三单元总结_第3张图片

第一次作业架构非常简单,或者说完全没有自己思考架构,由于也没有任何性能上的要求,我直接把所有的方法JML规格”照抄“了一遍,顺利通过。存储数据的容器全部Arraylist。

唯一一个难点是isCircle判断可达性,为防止爆栈,采用BFS算法。

第二次作业

UML图

OO第三单元总结_第4张图片

第二次作业增加了一个Group类,并且限制了CPU的运行时间,由于指令数巨大,如果每次查询仍采用遍历操作势必超时,因此在第一次作业的基础上给所有需要遍历Arraylist来查询的容器添加了一个对应的HashMap容器,这样将查询的复杂度成功降至O(1)。

其次,Group中涉及对Group中所有成员的年龄加和,年龄平均,年龄方差的计算,同样如果每次都遍历进行加和时间复杂度高,很可能造成超时,因此我选择采用了一个缓存结构,在Group中增加ageSquareSum,ageSum, valueSum, relationSum成员变量,分别表示年龄的平方和,和,value和,relation和。每次向Group进行add操作的时候即更新这几个变量的值,进行查询操作时只需更新这几个量就行,年龄的方差采用公式((ageSquareSum - 2 * mean * ageSum + n * mean * mean) / n)来计算。

不过有一点值得注意,不仅时addPerson的时候需要更新Group中的值,在两个Person 进行addRelation操作时,与二者相关的所有Group的value和relationSum都有可能发生变化,因此每个Person必须知道自己处于哪些Group中,每次addRelation时由Person将所有相关Group的值更新。

第三次作业

这是本单元第三次作业的UML图

OO第三单元总结_第5张图片

整体架构较第3次作业没什么变化,增加了一个Edge类保存Network每一条边的信息用于新增加的各种搜索操作。第三次作业新增的几个方法计算最短路径,查找双连通分量,计算连通块都涉及对整个Network的遍历,甚至是多次遍历,每一个复杂度都极高,不出所料,最后强测超时了1个点。

三个最难的方法,寻找最短路径采用了堆优化Dijkstra算法,如果采用朴素的Dijkstra算法时间上限可达1×109,而堆优化能将这个值降到小于2.5×107。

双连通分量的查找我采用了暴力枚举删点+bfs进行搜索的算法,更好的方法应该是使用tarjan算法找出割点,再对割点进行删除验证,但是tarjan算法本身不简单,之前从未接触过,感觉大概率出现bug,产生功能性错误,因此基于优先保证正确性的考虑,我并未选择tarjan算法。

连通块数量计算我算用了bfs遍历整个图的算法,统计最终的bfs次数,暴力算法,但也只需把整个图遍历一遍,总体可以接受。如果在前面的架构中采用了并查集那么该方法会异常简单,再此不再赘述。

五、bug分析与修复

第一二次作业强测互测都未查出bug,我在课下使用junit包括自动生成的测试样例进行了大量测试,正确性得以保证。同时大量采取了缓存操作,便利与查询通过不同的容器进行,时间复杂度也非常安全。

第三次作业在正确性上也并未发现任何bug,但在强测中一个点出现了超时,如前面所说,在查找双连通分量的时候采用了暴力的算法,导致了超时,目前正在尝试是否能通过一定的改进缩短运行时间。

整体hack的测略就是把其他同学的代码扔进自动评测机,对比输出答案。

六、心得体会

在这一单元的学习中,我首次接触了JML这种形式的语言,其目的在于方便程序员间的交流,也便于根据规格自动化对代码进行测试,这在一个复杂的大型工程中意义重大。在第一次作业的时候我还比较轻视JML,认为这个规格不是相当于把大部分代码都给出来了,并且JML写起来比代码还有难,有点没必要的意思在里面,我也以开课以来最快的速度完成了第一次的作业。但到后面两次作业我爱才渐渐意识到JML真正的意义,JML只是规定了条件和结果,具体的实现优化都要靠程序员自己,如果把JML直接”抄一遍“确实也能确保程序的正确性,但是性能上面直接死亡,实际上老师也说实际工作中优化的工作才是难题,功能的正确性是基本中的基本。

你可能感兴趣的:(OO第三单元总结)