OO第三单元博客作业
一、JML语言梳理
(1)理论基础
Java建模语言(Java Modeling Language,JML)在Java代码中增加了一些符号,这些符号用来标识一个方法是干什么的,却并不关心它的实现。如果使用JML的话,我们就能够描述一个方法的预期的功能而不管他如何实现。通过这种方式,JML把过程性的思考延迟到方法设计中,从而扩展了面向对象设计的这个原则。
JML引入了大量用于描述行为的结构,比如有模型域、量词、断言可视范围、预处理、后处理、条件继承以及正常行为(与异常行为相对)规范等等。
(2)应用工具链
openJML检查JML是否符合规格。
JMLUnit可以生成测试样例。
二、SMT SOLVER
通过安装OpenJML,部署了SMT SOLVER。
检查JML语法(这里是用cmder缩写了调用jar包的命令,下同):
可以看出JML通过了检查。
使用-rac,-esc会由于OpenJML自身问题导致玄学空指针错误。
但检查稍微简单的JML还是没问题的。例如:
检查出了存在的溢出问题。
三、JMLUnitNG/JMLUnit
要检查的方法如下:
配置好JMLUnit后,使用openjml编译源代码,并使用jmlunit生成测试用例。
可以看到,在当前目录下生成了众多java文件。
编译后目录如下:
之后使用javac编译测试用例,就可以进行测试了。结果如下:
可以看到,检查出了溢出问题。
四、架构设计梳理
第一次作业:
Mypath中使用HashSet+ArrayList,MyPathContainer中使用三HashMap降低复杂度。
第二次作业:
朴素地复制代码实现继承。
新增GraphManager类,以邻接矩阵+离散化方式存储图,在MyGraph中实例化。
新增Graphs类,内部设图相关的公共静态方法,包括bfs,folyd等。
第三次作业:
新增RailwayManager类继承自GraphManager,在其基础上实现了最小不满意度、最小换乘、最小票价等方法。
新增UnionFindSets类,实现了一个简单的并查集,是计算连通块的基础。
新增PathMatrix类,用以管理最小票价、最低不满意度等需求。即朴素的不拆点做法。内部存储结果,防止重复计算,提供查询接口和初始化接口,防止违法操作。
Graphs类中新增dijkstra方法(考虑到边数复杂度,未用到)。
思考:不拆点做法拥有比拆点做法更高的效率:不拆点最坏情况为:50 80^3 4 *120^3(使用floyd算法),这个代价是相对较小的,在强测中,我的cpu时间平均不到4s。对于拆点算法,若不进行优化,则在重复点较多时,复杂度可能超出想象。进行优化后,效率也相对较好。
不拆点算法的正确性其实不难验证。
五、bug修复
比较幸运,这三次作业中没有被找出bug。
六、心得体会
在第一次和第二次作业中,确实体会到了JML带来的规格化的优点,但是在第三次作业中,反而没有了这种感觉。我觉得可能是下面两个原因:
一、JML的规格依赖于底层的逻辑语句,在描述复杂方法的功能时,经常会不得不引入很多中间变量/方法,使人不知所云。也就是为了编写JML增加的数十个辅助方法,不仅增加了理解难度,override也会导致编译错误。
二、第三次作业的问题描述已经包含了绝大部分方法逻辑和功能,只是缺少一些特殊情况,这种情形下,再去耗费大量时间去验证JML规格是没有必要的。
但总的来说,JML还是有其独特的优势的,在描述中小型方法时,可以给出一个简洁而明确的规范,降低了出错的可能性。但是,并不意味着只需要JML就可以了,一份规范的指导书+规范的JML规格,才能获得双倍的可靠性。