第三单元博客作业
一:JML基础梳理、工具链
1.注释结构
//@annotation
/*@ annotation @*/
2.JML表达式
原子表达式
\result
表示一个非void 类型的方法执行所获得的结果,即方法执行后的返回值。\old(expr)
用来表示一个表达式expr 在相应方法执行前的取值。\not_assigned(x,y,...)
用来表示括号中的变量是否在方法执行过程中被赋值。\not_modified(x,y,...)
限制括号中的变量在方法执行期间的取值未发生变化。\nonnullelements( container )
表示container 对象中存储的对象不会有null ,等价于container != null && (\forall int i; 0 <= i && i < container.length; container[i] != null)
\type(type)
返回类型type对应的类型(Class)\typeof(expr)
返回expr对应的准确类型。
量化表达式
\forall
全称量词(\forall int i,j; 0 <= i && i < j && j < 10; a[i] < a[j])
\exists
存在量词(\exists int i; 0 <= i && i < 10; a[i] < 0)
\sum
连加求和(\sum int i; 0 <= i && i < 5; i*i)
\product
连乘求积(\product int i; 0 < i && i < 5; i)
\max
返回给定范围内最大值(\max int i; 0 <= i && i < 5; i)
\min
返回给定范围内最小值(\min int i; 0 <= i && i < 5; i)
\num_of
返回指定变量中满足相应条件的取值个数(\num_of int x; 0<x && x<=20;x%2==0)
集合表达式
可以在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。
new JMLObjectSet {Integer i | s.contains(i) && 0 < i.intValue() }
操作符
E1<:E2
子类型关系操作符b_expr1<==>b_expr2
等价关系操作符b_expr1==>b_expr2
推理操作符\nothing \everything
变量引用操作符
3.方法规格
- 前置条件(pre-condition) 前置条件是对方法输入参数的限制,如果不满足前置条件,方法
执行结果不可预测requires P;
- 后置条件(post-condition) 后置条件是对方法执行结果的限制,如果执行结果满足后
置条件,则表示方法执行正确,否则执行错误。ensures P;
- 副作用范围限定(side-effects) 副作用指方法在执行过程中对输入对象或this 对象进行了修改(对
其成员变量进行了赋值,或者调用其修改方法)。assignable \nothing;
modifiable \everthing;
- signals子句 满足b_expr时抛出异常
signals (***Exception e) b_expr
4. 类型规格
- 不变式invariant
- 状态变化约束constraint
5. 工具链
openJML
JMLUuitNG
二:JMLUnitNG
根据讨论区教程对Demo.java中compare方法进行测试。
结果如下图,
可见自动生成样例中数据均为边界条件(如Integer.MAX/Integer.MIN/0等)。
三:架构设计与重构分析
第一次作业
第一次作业较为简单,只涉及到根据JML进行方法编写。类图如下:
整体设计,为了使得getDistinctNodeCount()方法复杂度为o(1),维护了两个hash表,并将维护过程放置于增删操作函数中,分散复杂度。
复杂度分析如下:
可见整体复杂度与依赖度处于合理范围。
第二次作业
第二次作业MyPath类完全继承上一次设计,MyGraph类在MyPathContainer类基础上实现了新的接口,作业中直接把代码copy到了MyGraph类中,更好的方式应当是继承。
第二次作业由于设计无向无权图的最短路径问题,选择了BFS算法解决。在图类的设计上,将图与算法分离开,图类单独继承Algorithm接口,实现算法和图的分离。
类图如下:
复杂度与依赖度如下:
可见最短路相关方法复杂度较高。
第三次作业
同第二次作业,进行了代码复制,第三次作业涉及到3个不同图的构建与最短路计算以及连通块数目的计算,分别使用BFS,Dijkstra,并查集解决。
类图如下:
复杂度如下:
可见图工厂的依赖度以及各个新方法的复杂度较高。
四:作业代码实现中的bug和修复情况
第一次作业无bug,
第二次作业因BFS将路径算出以及遍历策略的原因超时,修改后满分。
第三次作业不满意度计算方法出错,且复杂度过高,准备根据标程进行修改。
五:规格撰写相关心得
JML通过离散数学中的逻辑关系对方法与类进行严谨描述,可靠的JML可以使得开发人员对方法的理解更深,不会因为自然语言的二义性导致问题。
实际体验下来,在方法较为简单的情况下,JML确实是优雅且有效的。但在方法复杂度上升时,编写和阅读JML都成为了困难。