OO第三单元
OO第三单元
JML语言理论基础,应用工具链
JML语言基础
-
JML简介
定义:
JML 是一种形式化的、 面向 JAVA 的行为接口规格语言
作用:
- 开展规格化设计。这样交给代码实现人员的将不是可能带有内在模糊性的自然语言描述,而是逻辑严格的规格。
- 针对已有的代码实现,书写其对应的规格,从而提高代码的可维护性。这在遗留代码的维护方面具有特别重要的意义。
-
JML语言语法相关
- 表达式
\result
:方法执行后的返回值\old(expr)
: 表示一个表达式expr
在相应方法执行前的取值\not_assigned(x,y)
表示括号中的变量是否在方法执行过程中被赋值\forall
全称量词修饰的表达式\exist
存在量词修饰的表达式\sum
返回给定范围内的表达式的和
- 操作符
E1<:E2
:判断类型E1是不是类型E2的子类型b_expr1<==>b_expr2
: 等价关系操作符b_expr1==>b_expr2
:推理操作符\nothing
空集\everything
全集
- 方法规格
- 前置条件:
requires
- 后置条件:
ensures
- 副作用范围限定
assignable
或者modifiable
- 前置条件:
- 异常
- 正常情况:
public normal_behavior
also
关键字- 异常情况:
public exceptional_behavior
- 正常情况:
- 类型规格
invariant
不变式constraint
状态变化约束
- 表达式
JML工具链
openjml
,可以编译含有JML的代码,同时内置SMT Solver,可以实现静态检查JMLUnitNG/JMLUnit
,可以根据JML自动生成测试文件.
部署JMLUnitNG
- 整个部署过程可以用非常痛苦来形容,主要是对命令行不太熟悉,导致出现了很多莫名其妙的错误,,又不知道该怎么处理,最后只是用了一个简单的类来测试一下JmlUnitNG的功能
- 实验结果如图所示(流出了眼泪)
架构分析
第一单元
- uml类图
-
总体的思想是维护两个MAP,,
map<path,pid>
,map<pid,path>
,分别以路径和路径id作为key
-
采用静态的方法维护
distinct_node
,即每次更新路径(增删),的时候更新 节点数量 -
因为代码量比较少,也不适合再添加额外的类,所以整体只有三个类
第二单元
- uml类图
- 这次的重点是计算最短路径,为此我们有两种方法,一个是dij算法,一个是floyd算法,考虑到我们需要算出任意两点间的最短路径,所以多源的floyd算法更合适一些,且最为重要的是floyd算法只有5行,实现起来非常简单,不容易出错,所以本次作业采用的也正是floyd算法
- 整体的思想还是和上次一样,采用静态的方式存储任意两点间的最短路径,在进行图更改(增删路径)的操作之后,用floyd算法更新最短路径.
- 在代码层面,我们分析一下,在上一次的基础上需要增加哪些内容,首先是增删路径的时候,对图的邻接矩阵进行刷新,然后对邻接矩阵经过floyd算出最短路径矩阵
- 所以最好的方式当然是继承上一次的Pathcontainer类,然后增加刷新邻接矩阵,刷新最短路径矩阵的方法即可,但是笔者当时比较懒也比较忙,所以就还是三个类包干,这样造成的后果就是,整体的结构显得不是非常清晰
第三单元
- uml类图
- 这次的难度有了史诗级的提升,笔者关于如何处理换乘问题思考了非常久,结果想出的方案要么性能很差,要么实现细节太过繁琐非常容易出错,最后幸亏得同学指点,采用了这样一个算法.
- 将每条路径上的任意两点进行链接,这样每多一条边就代表换乘一次,对于每个路径采用floyd算出一个路径的距离矩阵,每次增加一个路径,就将这个路径的距离矩阵加入我们已经创建好的最短路径矩阵(票价,换乘或者不满意度),(如果比最短路径矩阵的值要小,就替换),对于每次删除操作,重新遍历,之前的每条路径进行上述操作.
- 在代码架构方面,采用类似于观察者的模式,首先我们的地铁类要继承上次的MyGraph类,新增一个专门用来计算路径联通块的类Block,然后,新增一个PathGraph类和一个PathValueGraph类,专门用来计算得到一个路径的距离矩阵和value矩阵,接着,每次增删,我们更新最少票价矩阵,更新最少换乘矩阵,更新最低不满意度矩阵,更新联通块矩阵,注意为了方便更新操作和获取矩阵,所以我构建了一个资源池,各种最短路径矩阵,连通块数等我们都放在资源池中.资源池内有更新的方法.查询操作均放在地铁类中进行.
bug分析
- 第一次的实现比较简单基本上也不会出现什么bug
- 第二次出现了一个bug,是因为我有一个异常的情况没有考虑到,这里深刻体会到了在使用一个变量的时候一定要首先判断它1是否合法
- 第三次作业翻车了,主要是前面思考了很多方案,采用多个方案对代码进行了重构,导致之后的测试的时间基本不够,所以导致产生了一个非常重大的bug,就是我在每次删除路径,之后重建图的时候并没有对图先进行初始化
心得体会
- 第一次作业的时候,我还是非常仔细的研究了规格的,但是之后的过程随着方法功能的复杂度几何式提升,规格变得越来越复杂越来越难懂,参考的意义也是基本没有,所以在后来的两次作业,我只是参考了一些简单的方法的规格,以及一些异常条件,先决条件等.规格基本成了形式化的东西,后来也没怎么用到.
- 最大的收获就是学会了很多自动化测试的手段比如JUnit真的好用,然后顺便复习了一下数据结构的知识???,还有就是初步意识到了一个良好的架构和清晰简单的算法是多么重要,(这里指的算法是整个问题的解决方案,不是floyd之类的),一个好的方案可以使你的架构变得清晰简单,整体逻辑也非常简明.还有就是我使用了一些设计模式上的方法来帮助完善自己的架构,也初步尝到了设计模式的好处.