第三次面向对象博客作业

第三次面向对象总结

JML语言理论基础

Java建模语言(Java Modeling Language,JML)是一种进行详细设计的符号语言,他鼓励你用一种全新的方式来看待Java的类和方法。JML是一种行为接口规格语言 (Behavior Interface Specification Language,BISL),基于Larch方法构建。BISL提供了对方法和类型的规格定义手段。所谓接口即一个方法或类型外部可见的内容。通过在Java代码中增加了一些符号,这些符号用来标识一个方法是干什么的,却并不关心它的实现。使用JML,我们就能够描述一个方法的预期的功能而不管他如何实现。通过这种方式,JML把过程性的思考延迟到方法设计中,从而扩展了面向对象设计的这个原则。

JML以javadoc注释的方式来表示规格,每行都以@起头。有两种注释方式,行注释和块注释。JML引入了大量用于描述行为的结构,比如有模型域、量词、断言可视范围、预处理、后处理、条件继承以及正常行为(与异常行为相对)规范等等。对代码进行抽象:

方法规格抽象 

执行前对输入的要求----前置条件(requires)

执行后返回结果应该满足的约束----后置条件(ensures)

副作用范围限定,assignable列出这个方法能够修改的类成员属性

规格中专门说明exceptional_behavior

数据(类型)规格抽象

数据状态应该满足的要求----不变式(invariant)

数据状态变化应该满足的要求----约束(constraint)

通过一系列的JML表达式实现描述:

原子表达式

\result表达式:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值。

\old( expr )表达式:用来表示一个表达式 expr 在相应方法执行前的取值。

\not_assigned(x,y,...)表达式:用来表示括号中的变量是否在方法执行过程中被赋值。

\not_modified(x,y,...)表达式:与上面的\not_assigned表达式类似,该表达式限制括号中的变量在方法执行期间的取 值未发生变化。

\nonnullelements( container )表达式:表示 container 对象中存储的对象不会有 null 。

\type(type)表达式:返回类型type对应的类型(Class)。

\typeof(expr)表达式:该表达式返回expr对应的准确类型。

量化表达式

\forall表达式:全称量词修饰的表达式。

\exists表达式:存在量词修饰的表达式。

\sum表达式:返回给定范围内的表达式的和。

\product表达式:返回给定范围内的表达式的连乘结果。

\max表达式:返回给定范围内的表达式的最大值。

\min表达式:返回给定范围内的表达式的最小值。

\num_of表达式:返回指定变量中满足相应条件的取值个数。

JML应用工具链

JML相应的工具链,可以自动识别和分析处理JML 规格。常用的有openjml,其使用SMT Solver来对检查程序实现是否满足所设计的规格(specification)。目前openjml封装了四个主流的solver:z3, cvc4, simplify, yices2。z3由Microsoft开发的,并已在github上开源:https://github.com/Z3Prover/z3 其正式发布版可通过https://www.microsoft.com/en-us/download/details.aspx?id=52270获得。cvc4由Standford开发,可以通过http://cvc4.cs.stanford.edu/downloads/下载。

SMT Solver部署

部署好SMT Solver后运行, 由于规格有错误无法运行.

 

三次作业架构设计

第一次作业:

纵览: 第一次作业整体比较简单,唯一需要注意的就是getDistinctNode这个方法不能直接遍历,因为那样的话getDistinctNode的时间复杂度就会是O(n^2),会超时。最简单的解决办法就是另外开一个HashMap存所有不同的点,在每次添加或者删除路径的时候更新这个HashMap,这样getDistinctNode是O(1)而add和remove是O(n)。其实应该还有更有效率的方法,但是从强测得角度做这个最基本的优化就可以了。

第二次作业:

 

纵览:第二次作业在和第一次作业相比加入了图结构,主要增加的功能是(1)判断两点是否相连。(2)计算两点之间的最短距离。这一次的时间复杂度限定并不严格,而且add和remove指令限定只有20条。所以,完全可以在add是remove的时候更新所以点之间的最短距离。这样add和remove指令的时间复杂度是O(n^3)而最短距离和是否相连的判断都是O(1)。优化的话可以每次请求的时候再进行计算,并且把计算的所有中间结果保存下来。这样理论上时间复杂度一定小于等于上一种方法。但是实际尝试的时候并没有明显区别,也许是因为第二种方式需要加入更多的数据结构进行维护,而第一种方法只需要利用邻接矩阵,所以访存的时间差距弥补了计算上浪费的时间。

第三次作业:

 

感觉这次作业是目前为止最难的一次,由于第二次作业存储图结构的时候没有记录pathId,所以换乘问题很难解决基本需要重构。 这次我采用新建一个图把每一个条路径上所有相连的点都记录下来。比如1->2->3, 存储的时候1,2 是相连的,权重为1;然后1,3也是相连的,但是权重是2。这样计算的过程会比较简单, 但是这样的图不好维护。由于强测中add和remove指令不多,所以我选择在add的时候把每一个path当作一个单独的图用dij计算距离,remove的时候遍历所以剩余路径重新建图,顺便不满意度、最短距离、最少票价的计算也放在了add和remve里面。这样add的时间复杂度是O(n^3), remove的时间复杂度O(n^4)。 理论上这个复杂度是过不了压力测试的,但是强测得数据都比较善良就没有翻车。从优化的角度,首先在add的时候如果不成环可以不进行dij,然后可以建一个Hashmap存储所有的边使得在remove不用重新建图,但是这样需要添加很多新的数据结构维护起来比较困难。

关于规格化的体会:

这次作业虽然主要的目的是学习JML,但是实际上感觉还是数据结构的元素比较重一点。一开始写第一次作业的时候还认真读规格并且尝试用JUNIT测试,等到第二次作业的时候就只大概看了一下规格里面对特殊情况的规定就写代码了。然后第三次感觉重心完全在数据结构上,根本没看规格。事实上openJML我是在写这个实验报告的时候才配置好的。总体感觉这个单元学完以后可以做到看懂JML,但是如果要写出严谨正确的规格还很难做到,希望能有大佬推荐练习写JML的平台。其实整体觉得JML最大的优势在于可以根据规格自动生成测试集,所以写好JML在方法很多的复杂程序的测试中能提供很大的便利。但对本单元的作业来讲,由于我本人是在博客周才开始用JMLUnit所以并没有用上。

猜你喜欢

转载自www.cnblogs.com/17373091-cathy/p/10900700.html