OO第三单元总结(规格)

理论基础

这个单元主要介绍了JML语言,可以用来规范java设计中的类和接口。形式上来说,它可以作为注释被添加到java代码中,让我们明确每个函数和类的一些行为特征和特性。我们可以在设计一个类和方法的时候,先不考虑具体的实现以及涉及到的数据结构等,而是只考虑它的功能,这样一来,我们就可以在设计的时候明确如何用面向对象的思想来解决这个问题,将实际问题在设计阶段就分解为对象的各种动作。

一个规格中,通常包含前置条件、副作用、后置条件,分别是调用方法必须满足的条件,可能修改的变量和方法返回时必须满足的条件。

单纯的读JML语言还是比较好理解的,其中的许多描述都是在离散数学中学到的,而真正写一个好的规格确实是比较费时费力的,尤其是一些比较复杂的功能不太好用标准的数学语言来描述,在上机考试中,我主要还是靠着看课上给出的JML语言,仿照着写出相应的规格。

JML的工具链

JML有一些工具可以进行规范检查或者生成测试数据,我接触的不是很多,在发现自己不是很熟悉JML语言后,怕课上作业让写规格而自己写的规格不符合语法,于是尝试了一下openjml的parsing功能,代码如下

#!/bin/bash
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/bin/java -jar <openjml 目录>/openjml.jar "$@"

前面那一堆其实是由于openjml只支持特定版本的Java,刚好的电脑中默认的java并不是java7或java8,然后将这个文件存成openjml并将其设为可执行,执行代码如下

./openjml <文件地址>

这样openjml将会对规格代码的格式进行检查。

第六次课上实验中,要求我们补充IntSet的规格,使用上述方法对自己编写的规格进行检查。

 

发现这个错误后,发现是错将<==>写成了<=>。更改后成功通过parsing功能。

另外还尝试了一下JUNITNG来自动生成测试,在最简单的demo进行实验,代码如下

bash testng demo/Demo.java 
javac -cp testng.jar demo/*.java 
javac -cp testng.jar demo/Demo_JML_Data/*.java 
bash openjml -rac demo/Demo.java 
java -cp testng.jar demo.Demo_JML_Test

结果如下

 

可以看出,自动生成的测试点都是边界数据,这样的话似乎很难对所有分支进行测试。这里怎么设置测试点的分布还没太明白

关于Junit,使用起来还是比较简单的,对于一个简单的函数可以编写junit文件,用Assert.assertEquals函数给出正确结果。不过这样只能测试一些比较简单的方法,一旦复杂起来,就难以直接在测试文件中写出正确结果。

作业架构的分析和bug的反思

总体来说,尽管这个单元的作业不是很难,这三次作业我都出现了或多或少的问题,依次总结下来如下。

首先是第一次作业,我错将实现规格变成了翻译规格,几乎所有的方法都是按照方法中的数学表达式原封不动的写下来,因此也就完全忽略了性能。用数组来储存节点和路径,就会使许多原本O(logn)复杂度可以解决的事情变成O(n),举个例子,比如判断是否存在某个路径,我需要将这个路径于数组中的每个路径分别比较,然而路径之间其实是有序关系的,如果我维护了有序数组,就可以在log的复杂度找到是否存在某个路径的问题。因此第一次作业不出意料的在强测上出了问题。这次作业也让我认识到,规格不是算法,他只是描述方法和类的行为的一个工具,重点是读懂他的意思,实现时还有另外考虑效率的问题,而不是简单的机械翻译。

有了第一次作业的教训,我第二次作业中,我对代码进行了重构,基本就是重写了container的代码,使用hashmap和hashset这些高级数据结构来操作节点和路径。由于题目中图更新指令比较少,使用弗洛伊德算多源最短路径并储存下来,同时在类中维护了一个dirty变量,在对图进行更新后设置dirty变量,需要读取图的时候,如果发现dirty是true的,那么需要重新运算弗洛伊德算法。一切看起来都很好。然而由于我的一个笔误,在自己测试的时候没有测试全面,导致在强测中依然没有取得比较好的结果。这也反映出测试的重要性。

第三次作业,为了尽可能少的更改我之前的架构,我采用了拆点的算法,对于换乘站,多加入一个辅助节点,并通过在其上设置不同的值。依然采用弗洛伊德算法。然而这次由于加入副主节点,让图中的节点数目变大,弗洛伊德是一个O(n^3)的算法,就导致我的cpu时间不够了。后面看了例程后,发现采用迪杰斯特拉算法并且保存中间结果的方式是一种更好的算法,事实也证明这种算法是不会导致cpu时间过长的。

感想

这个单元主要是关于JML的规格的理解,经过这三次作业的训练也能体会到课程组的精心设计。让我意识到在工程领域,描述设计和实现算法都是可以用面向对象的思想来解决的,所以如何能够在设计和实现中平衡正确性、复杂度和工程性的是非常重要的。编程不仅仅是简单的堆代码,更是要思考算法、结构、优化、复用等等这些细节问题才能写出好的代码。如何能够设计出保证正确性、复用性好、还有性能的代码,是值得我们在编写代码前就想好的。另外,这几次作业也让我认识到了测试的重要性,有两次错误都是可以通过测试找出来的,而我没有认真的对代码进行测试而是想当然的认为不会出错,这是以后需要注意的点。总的来说,希望自己在面向对象这门课的最后一个单元可以有更好的表现。

 

posted @ 2019-05-22 20:15  张卓1037  阅读(115)  评论(0编辑  收藏  举报