OO第三单元总结
18231169 黄思为
1.JML语言的理论基础及应用工具链
个人感觉JML的书写与离散数学中命题逻辑、谓词逻辑比较类似。大概是用一种形式化的语言,规定代码的实现。
1.1 JML的理论基础
-
JML中有类似离散中的存在量词、全称量词:\exsit、\forall
-
与伪代码类似的for、sum等一系列对数据的操作
-
normal_behavior、exception_behavior,对方法行为进行规定,在条件不同的情况下进行不同的分支操作
-
前置条件、后置条件、不变式、assignable等对输入输出及方法执行过程中数据的限定
使用以上的操作进行组合,便可以非常严格、形式化地对我们的代码行为进行限定,保证需求与实现的一致性。
1.2应用工具链
-
openJML——对JML规范性的检查
-
SMT Solver——检查代码与JML的等价性
-
JMLUnitNG——测试代码自动生成
可能还有更多的关于JML的工具,由于了解不是很深入,这里便不提及。
2.openJML检查
看完群里大佬们的讨论,跟着J哥的博客,我也动手尝试了一下。
可以看到,对于比较简单的Person接口,没有产生报错,而对于Group,竟然对Person类报了找不到符号。可以看出openJML这个工具似乎不太智能.......感觉肉眼检测JML反而会方便很多(使用openJML还要改成jdk8)。
3.JMLUnitNG自动测试
经过一早上的尝试,成功使用JMLUnitNG生成了一些测试代码,如下:
打开其中一个,我们看看到底生成了什么:
这段是对getAge的一个测试,但是不知道为什么测试代码运行时会报错找不到主类。经过了种种尝试最终放弃,转而翻阅大佬们的博客进行学习。看到同学们的博客中有提到,生成的测试代码都比较简单,而且对于代码形式限制较大(如不能使用菱形表达式)。个人觉得这个工具用起来并不是特别顺手,不如使用Junit手写测试代码。
4.架构设计
经过老师的讲评,我意识到自己本单元作业的架构设计是极不合理的。由于本单元是给出了JML描述的接口,我便想直接实现接口就完事了,就是老师特别指出的“对着规格敲代码”的那类同学。最多在第三次作业时,为了使用Dijkstra算法,我在MyNetwork中增加了一个内部类,用来标记节点到出发点的距离。
由于不加思考,所以我的架构十分简单,就是对四个接口的实现,大部分方法都放在了MyNetwork中。并且为了程序运行效率,增加了几个缓存数据,如ageSum等。这样导致类之间的耦合程度较大,甚不合理。
回头再看这一单元的作业,我认为,可以抽象出一个图的类,将图的相关方法,如:最短路径、双联通等单独抽象出来。这样也能够实现更好的代码复用。
5.BUG及修复情况
本单元第一次作业,我就遭遇了“开幕雷击”,在强测中获得了0分。究其原因是对作业放松了警惕,觉得过于简单。在实现时,有两处方法未考虑id1==id2
的情况。本是简单测试后,添加两行代码就可以完全避免的问题,让我追悔莫及。
第二次作业吸取了第一次作业的教训,使用Junit及对拍的手段进行了较为充分的测试,未出现BUG。
第三次作业,在求最短路径的算法中,出现了三个测试点超时的情况。由于我使用优先队列实现了Dijkstra算法,目前还比较迷惑为何超时。怀疑是实现过程出现了一些问题或是addPerson/addRelation时的操作过多。在撰写这篇博客时还未成功修复这几个测试点。
6.心得体会
对于规格撰写,我认为这是开发者、用户、测试人员之间的一座桥梁。通过规格撰写,可以精确地描述我们的需求,对实现过程进行一定的限定。我们作为程序员,应该要能够阅读规格、撰写规格,这样能够让我们的工作中与同事有更高效、精确的交流。
不过我有一点小小的疑惑,作为一种沟通的语言,JML在某些语法上是否过于复杂?比如多个forall/exsit嵌套,让人很容易“看走眼”,不知道这个地方是否需要一些改进?使JML能在精确的同时更加简洁明了?