JML语言的理论基础、应用工具链情况
JML 是用于 Java 的规格描述语言。我们使用 JML 来描述 Java 模块的行为,避免了对设计者意图的歧义。JML 的规格可以写到注释或者单独的文件中,所以带有 JML 规格的文件可以直接使用 Java 编译器来编译,而无需做出修改。
OpenJML 是用 JML 验证 Java 程序的工具。目前它仍然处于待完善状态,只适用于一些实验性、教学性质的场合。本单元中,可以使用 OpenJML 来验证规格语法是否正确,但其他的场景用途极小。
使用 JMLUnitNG 生成测试用例
使用如下图的命令可以生成针对部分函数的测试用例:
观察生成的测试样例可以发现,其并不具备主动构造对象的能力,并且也没有很好覆盖数据的边界情况。结合一些其他情况,可以得出结论,目前根据规格自动化生成测试数据的技术发展还严重不足,不能取代手工的单元测试样例编写。本单元测试的工作采取和上两个单元近似的策略,通过随机生成测试数据+验证器来完成。
作业架构设计
这三次作业几乎每次都完全沿用了前一次的代码,所以从第三次的代码着手进行架构分析。
Person
使用自己的 MyPerson 类实现了要求的 Person 接口。Person 类几乎就起到存储人员信息的作用,因此实现比较简单。需要注意的是 Person 类中不能直接仿造 JML 规格使用数组,而需要使用 HashMap 来实现,这样才不会超时。
Group
Group 接口为 Person 分组并提供了一些统计信息。其中,部分统计信息查询时间复杂度为 O(n2),如果采用查询时再统计的做法,恐有超时之虞。因此,在 MyGroup 接口中对各个待查变量进行维护,查询时直接返回待查变量,使增删的时间复杂度不变而查询的时间复杂度降为 O(1),有效降低超时风险。
Network
第三次作业中 Network 接口增加了几个比较有意思的函数,queryMinPath 中使用 Dijkstra 算法求两人之间的最短路径;queryStrongLinked 算法使用 Trajan 寻找双连通分量(其实也可以用去点 BFS 来实现,不会超时);queryBlockSum 使用并查集来维护连通块个数。
这些算法是非常自然的想法,不过即使不用这些算法,只要做出适当优化,也不会超时。在现实生活中写代码时,需要结合实际情况来决定是否需要高级算法。
bug 分析及心得体会
由于 JML 工具帘尚不成熟,所以本次作业依然采取随机生成测试数据并检验的办法来验证程序的正确性。不过,本次作业代码正确性都比较高,在强测及互测中均没有发现 bug。
由于这次代码架构清晰,不像前两次作业那样人人差异巨大,在查找别人的 bug 时,我选择用直接阅读代码的方式进行。在互测中,成功发现他人超时的 bug。
JML 虽然看上去作用非常大,但经过检验可以知道,其生态非常不成熟,最多只能作为辅助手段,完全依赖 JML 来完成程序验证是不现实的。代码的正确性还是要依赖正确的文档及完善的单元测试来解决,我希望能在以后的工程代码开发中学习更多的经验。