OO第三单元总结

OO第三单元作业总结

一、前言:

本单元进行的是JML的学习。

二、梳理JML语言的理论基础、应用工具链情况

(一)JML语言的理论基础

 1.概念:JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。

2.注释结构:JML以javadoc注释的方式来表示规格,每行都以@起头。有两种注释方式,行注释和块注释。举例如下:

行注释:

 1 //@ public instance model non_null int[] nodes;

块注释:

1  /*@ requires index >= 0 && index < size();
2       @ assignable \nothing;
3       @ ensures \result == nodes[index];
4       @*/

3.JML表达式:JML的表达式是对Java表达式的扩展,新增了一些操作符和原子表达式。

(1)原子表达式:\result表达式、\old( expr )表达式、\not_assigned(x,y,...)表达式等。

(2)量化表达式:\forall表达式、\exists表达式、\sum表达式、\product表达式等。

(3)集合表达式:集合构造表达式。

(4)操作符:除Java语言所定义的操作符外,JML专门定义了子类型关系操作符、等价关系操作符、推理操作符、变量引用操作符四种。

4.方法规格

(1)前置条件(pre-condition),通过requires子句来表示。

(2)后置条件(post-condition),通过ensures子句来表示。

(3)副作用范围限定(side-effects),指方法在执行过程中会修改对象的属性数据或者类的静态成员数据,使用关键词 assignable 或者 modifiable。

(4)signals子句,结构为 signals (***Exception e) b_expr,意思是当 b_expr 为 true 时,方法会抛出括号中给出 的相应异常e。

(5)signals_only子句,后面跟着一个异常类型,满足前置条件时抛出相应的异常。

5.类型规格

(1)不变式(invariant)是要求在所有可见状态下都必须满足的特性,语法上定义invariant P,其中invariant为关键词, P为谓词。

(2)状态变化约束(constraint)对象的状态在变化时往往也许满足一些约束,这种约束本质上也是一种不变式。对前序可见状态和当前可见状态的关系进行约束。

(二)JML应用工具链

1.OpenJML:OpenJML是一个新的JML工具,包括RAC和ESC,与Java最新版本一同使用。OpenJML建立在Sun™的OpenJDK(javac的开源实现)之上。 

2.JMLUnitNG:用来自动生成一个Java类文件测试的框架,结合OpenJML实现对代码的自动化测试。

3.JMLdoc:与Javadoc工具类似,不同的是它在生成的HTML格式文档中包含JML规范。

三、部署JMLUnitNG/JMLUnit

部署JMLUnitNG,首先安装OpenJML,按照伦佬的教程设置好相关环境后,进行用JMLUnitNG生成测试文件。首先先用以下demo文件进行测试:

 1 // demo/Demo.java
 2 package demo;
 3 
 4 public class Demo {
 5     /*@ public normal_behaviour
 6       @ ensures \result == lhs - rhs;
 7     */
 8     public static int compare(int lhs, int rhs) {
 9         return lhs - rhs;
10     }
11 
12     public static void main(String[] args) {
13         compare(114514,1919810);
14     }
15 }

执行

生成测试文件,接着利用以下两个命令进行编译

最后执行

产生了运行结果如上。其中Failed了三个样例,如图所示。说明我们的减法存在溢出。

(可能在JunitNG的使用上还有一点小的困难,我对Graph类的测试失败了。)

四、梳理作业架构设计

(一)第九次作业

本次作业最终需要实现一个路径管理系统。可以通过各类输入指令来进行数据的增删查改等交互。

类图:

分析:本次作业自己完成的部分是三个类,其中MyPath类继承Path类,MyPathContainer继承PathContainer类,Main类用来运行整个程序。

MyPath类中的内容:

分析:两个成员变量,Arraylist保存节点,Hashmap保存不同节点distinctnodes,方法主要有图中所示的获得大小(size)、获得序号为i的节点(getNode)等。

MyPathContainer中的内容:

分析:成员变量有保存各路径的pathList,保存路径id的pathid,保存整个容器中不同节点的distinctnodes,均用Hashmap实现。方法有添加路径(addPath)等。

其中,由于方法复杂度的限制,为了将distinctNodeCount方法的复杂度降低,选择在每次addPath和removePath(removePathById)时更改distinctnodes的内容,将结果保存下来,查询时不用遍历,从而避免了爆搜。

(二)第十次作业

类图:

分析:本次作业的类与上次作业相同的地方不赘述。不同的地方是

1.MyGraph类继承自Graph类,而Graph继承自上一次作业的类PathContainer。

2.新增了一个GraphContainer类,用于保存图的结构以及图的相关算法。

MyGraph类的内容为:

分析:与上次作业相比新增了containsNode、containsEdge、isConnected、getShortestPathLength四个与图结构紧密相关的算法,重点是如何建立起图的体系,这一点我放在新增图类MyGraphContainer中实现,Graph类包含一个MyGraphContainer类的成员变量,用于查询与图有关的各类信息。

MyGraphContainer类的内容如下:

 分析:

1.图结构的存储表现形式为邻接表,采用一个略微复杂的二级Hashmap具体实现。其中,一个节点的id作为key,索引到另一个Hashmap,建立起每个节点与其他所有节点的一个网络,最终的hashvalue体现二者之间是否存在Edge。

2.最短路径寻找使用BFS广度优先搜索法,这个算法大同小异,因此不再过多赘述。值得一提的是,由于我找到的算法版本较强化,会将最短路径所经过的各个节点都存储下来,然而我们只需要最短路径长度这一数值,因此增加了复杂度,导致CPUtime过长。

3.为了降低查询最短路径长度时的复杂度,仍采用一个二级Hashmap结构存储某两个节点之间的最短路径。每次addPath和remove的时候,更改此表中内容,保存结果以便查询。

(三)第十一次作业

类图:

分析:这次作业的类之间关系几乎与上次作业完全相同,唯一的更改是MyRailwaySystem类继承自RailwaySystem类,而RailwaySystem继承自上次的Graph类。

MyRailwaySystem类的内容:

分析:与上次相比,新增了查询最小票价、查询最少换乘、查询最小不满意度、查询连通块个数四个方法。同样的,将图结构存储和具体的实现算法保存在自己的RailwayGraph类中,此类作为一个成员变量存在于MyRailwaySystem类,从而获得相关的信息。

RailwayGraph类的内容:

分析:

1.用二维数组这一容器建立多个以邻接矩阵为实现形式的图,保存着各个节点的信息,由于算法的需要,这些图的结构大同小异,主要的不同是边的权值,设置不同的权值来体现最少换乘、最低票价以及最少不满意度。

2.计算加权最短路径的算法是Floyd算法,其好处是,(1)代码简洁(2)算法完毕后邻接矩阵直接变为最终用来查询的图。(3)一次空间上的遍历可完成多个图的计算,某种程度上减少了遍历的次数。

3.计算连通块采用dfs深度优先染色算法。

五、分析代码实现的bug和修复情况

(一)第九次作业

进行removePath时,用foreach进行遍历,判断equal后直接在for循环中使用Hashmap的remove将其删去,造成for循环的不稳定,产生了某些语句没有输出的情况。如下图:

修复方法:采用temp存储,找出equal的path后break,for循环结束后利用temp找到应该remove的内容,进行删除操作。如下图:

强测中,因为这个bug,wa了18个点,只得了10分,没有进入互测,修复后即通过了所有强测点。

(二)第十次作业

 三个bug。

1.删边的时候没有考虑到无向图的对称性,导致只remove了“一条边”。如下图:

修复方法:

2.CPUtimelimit。正如上文所说,计算最短路径的算法BFS时间较久。

修复方法:换了算法和结构,用邻接矩阵和Floyd算法。

3.对规格没有仔细研究,当两个节点相等的时候,最短路径应该是0。

(三)第十一次作业

删除路径的时候,我将所有的图推翻重构,此时对distinctnodes中内容的改变出现漏洞,造成bug。

修复方法:遍历更新一遍distinctnodes。

六、对规格撰写和理解上的心得体会

对于规格的撰写,我个人有以下几点心得体会。1.规格撰写的时候逻辑一定要严谨,考虑到各种复杂逻辑下的边界情况,才能写出严密的规格。2.规格的撰写要符合语法,这样才能具有更高的可读性。3.规格的撰写有助于规约我们写代码时候的逻辑,使我们的代码单元化,封装化,更易于代码的维护。

对于规格的理解,我认为首先应该熟悉JML的语法,其次,应该保证代码的行为完全符合规格的规定。JML的使用有以下好处:能更加精确地描述代码所完成的任务,有效地发现和纠正错误,减少随着应用程序的进展而引入错误的机会。所以,学习JML还是很有必要的。

转载于:https://www.cnblogs.com/lwt-0521/p/10905998.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值