OO第三单元总结
OO第三单元总结
一.JML语言的理论基础与应用工具链
1.理论基础
注释
行注释://@annotation
块注释:/* @ annotation @*/
表达式
\result表达式:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值
\old( expr )表达式:用来表示一个表达式 expr 在相应方法执行前的取值
\not_assigned(x,y,...)表达式:用来表示括号中的变量是否在方法执行过程中被赋值
\not_modified(x,y,...)表达式:限制括号中的变量在方法执行期间的取 值未发生变化
\nonnullelements( container )表达式:表示 container 对象中存储的对象不会有 null
\type(type)表达式:返回类型type对应的类型(Class)
\forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束
\exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束
\sum表达式:返回给定范围内的表达式的和。
\product表达式:返回给定范围内的表达式的连乘结果
\max表达式:返回给定范围内的表达式的最大值
\min表达式:返回给定范围内的表达式的最小值
\num_of表达式:返回指定变量中满足相应条件的取值个数
方法规格
前置条件(pre-condition):requires P;要求调用者确保P为 真
后置条件(post-condition): ensures P;方法实现者确保方法执 行返回结果一定满足谓词P的要求,即确保P为真
副作用范围限定(side-effects):使用关键词 assignable 或者 modifiable ,assignble 表示可赋值,而 modifiable 则表示可修改
signals子句: signals (Exception e) b_expr ,当 b_expr 为 true 时,方法会抛出括号中给出的相应异常e
类型规格
不变式invariant : invariant P, 要求在所有可见状态下都必须满足特性P
状态变化约束constraint :对前序可见状态和当前可见状态的关系进行约束
2.应用工具链
OpenJML:用于检查JML语法规范
SMT Solver:根据规格判断程序的正确性
JMLUnitNG:自动生成测试样例进行单元测试
二.部署使用JMLUnitNG
这里使用自己的类和方法
public class Demo2 {
/*@
@ public normal_behaviour
@ requires x > 0 && y > 0;
*/
public static int product(int x, int y) {
return x * y;
}
public static void main(String[] args) {
product(-45, -5);
}
}
将jmlunitng.jar和Demo2.java放在同一文件夹,openjml.jar在另一文件夹
使用git bash执行指令
最后结果
可以看出,对于x<0,y<0,以及溢出的样例出现Failed报错
三.架构设计
第一次作业
在MyPath类中建立一个HashSet
在MyPathContainer中建立HashMap<Integer, Path> idToPath和HashMap<Path, Integer> pathToId双映射,便于根据id查找路径和根据路径查找id,同时也建立一个HashSet
第二次作业
第二次作业要将Path集合存成无向图,计算两个结点之间的最短路,以及判断连通性。首先设立一个新类Nodes表示一组点对,然后建立HashSet
第三次作业
这次作业新增最少换乘,最少票价,最少不满意度的计算,以及统计连通块个数。统计连通块采用并查集的方法,使用HashMap<Integer, Integer> pre保存每个结点的起始结点,再借助join和find方法找到并合并起始结点。三个计算本质上也是找最短路,但是需要重新构图,即将每条Path的任意两个结点相连,最少换乘图权值都为1,最少票价图权值为两点间最短路加2,最少不满意度图权值为两点间最少不满意度加32,将这些权值存在HashMap<Nodes, int[]> newedges中,然后据此用dijstra算出最短路存进transfergraph,pricegraph,unpleasantgraph。
四.bug和修复
第一次作业cpu超时了,原因是采取ArrayList存点,每次读指令都会计算一次,改成HashMap存储以及将计算放在写指令中就能解决问题。
第二次作业还是CTLE。每次访问点对都会新建对象,且每次构图都会建立一个大矩阵,这时使用floyd算图就会消耗很多时间,因为有很多无用计算,改为dijstra就能减少无用计算量,节省时间。
第三次作业算不满意度报WA,检查发现之前算得的最少不满意度会影响后面的不满意度计算,即此图没有最优子结构,因此每次计算不满意度时清空原来的图就能保证正确性。
五.心得体会
JML规范化语言初看是很繁琐冗长的,让人没有学习的欲望,但在根据规格写代码做作业的时候可以发现写代码变得很容易,只要按着规格的描述一步步去实现即可,而且只要规格正确就能保证正确性。事实上这也正体现规格和自然语言的差异,我们以前拿到的需求都是简单直白的,一看就知道要什么,但真正实现的时候却发现其实并不知道该怎么做,而规格虽然”麻烦“,但完全理解后却能对需求有更准确更有目的性的认识。
但不能否认的是JML真的难写,用OpenJML跑课程代码都能找到很多语法错误。。另外不能只看规格死写程序,OO还是需要数据结构的,有一个好的架构和合适的数据结构是有很大帮助的,这能帮助我们理解程序应该做什么,我们应该做什么,层次明晰了也许就不会出现大片的CTLE。