307
社区成员
发帖
与我相关
我的任务
分享说实话,第三单元真的是目前最简单的一个单元了,接下来我首先说一下我对JML和规格驱动开发的理解。
在完成了这三次作业之后,我对JML最突出的感受就是在某些几个字就可以说请的问题上,JML要花一大堆篇幅,看半天才能看懂,比如说hw11中的recommendNthUp方法,我看了很多遍才看懂是“排第几”的意思。
/*@ public normal_behavior
@ requires containsUser(userId) && rank > 0 && videos.length > 0 &&
@ (\num_of int i; 0 <= i && i < users.length; users[i].getId() != userId && !getUser(userId).isFollowing(users[i])) >= rank;
@ assignable \nothing;
@ ensures containsUser(\result) && \result != userId && !getUser(userId).isFollowing(getUser(\result));
@ ensures (\num_of int i; 0 <= i && i < users.length;
@ users[i].getId() != userId && !getUser(userId).isFollowing(users[i]) &&
@ (getUser(userId).computeUpScore(users[i], videos.length) > getUser(userId).computeUpScore(getUser(\result), videos.length) ||
@ (getUser(userId).computeUpScore(users[i], videos.length) == getUser(userId).computeUpScore(getUser(\result), videos.length)) &&
@ users[i].getId() < \result))) == rank - 1;
相对于我们习惯的自然语言,JML在描述一些要求时非常的复杂,很绕。但是在看懂之后可以发现它非常严谨,且相对于自然语言更贴近代码实现,具体而言,在看懂上面的JML之后我要在函数里写什么几乎呼之欲出了。首先是根据ensures的第3,4行构建比较方法,然后因为\result的限制条件(非参数本人或关注者)与下面的users[i]相同,所以就是要在这个限制条件下找出一个user,条件是按照这个计较方法排序的第n个。
另一方面,JML非常适合用来进行Junit测试,因为已经将所有需要检测的点一行行列出来了,在编写Junit时几乎就是一行行进行检测。
在这三次作业中,我中测全都是代码本体一遍过,但Junit提交了五六次才过,可以说就完成而言比JML实现困难很多。
在这些测试中,我感觉频繁出错的点有两个:一个就是关于assignable的检测,因为随着属性多起来,在检测的时候容易测得不全面;另一个就是极限情况,就是错误函数虽然在大多数情况下符合ensures,但是像空串、0这样的特殊情况很容易实现错误,也就是说对于某些特别的输入要专门检测。
有关已有方法/容器在迭代中的变化,我直接将前后两版JML规格交给AI来实现。
客观而言,很多次我是在看到强测结果时发现了程序的性能问题。所以从hw10开始我对于每个方法力求做到效率最高,对于每个容器都查看一边设计的操作,并选择最快的容器。比如对于维护user,video的容器,因为保证每个对象有唯一确定的标识id,且会频繁通过id查询对象,因此Map最合理。
除了性能不足导致的超时之外,我的bug主要是边界情况的特判。具体而言,我在hw10的Video.cleanSpamComments方法中使用了KMP算法,但是没有对空串进行特判。
在Unit3第二次研讨课上进行了一个JML“击鼓传花”游戏,基本上在过程中每一步我都会发现bug(包括我自己写的),下面举出几个印象深刻的例子。
我在第一次写JML时忘记写public exception_behavior了,我写完之后还在那看着别人发呆,传出去之后才意识到,太尴尬了,给我留下了巨大的心理创伤。
我写的第二个自然语言描述就发现我看到的JML绝对不是第一个人(设计者的本意),首先是返回一个容器却只保证了在里面的元素都满足条件,没有写满足条件的元素都在结果里,这样的话全部返回空串完美符合JML,然后就是没有对重复元素行为做出保证,导致出现了未定义行为。
然后最明显的就是我检查的那一份,前面几棒是正确的,但是再把自然语言转化为JML时就出现了问题,就是我在研讨课上进行的发言,JML没有严格“距离”与“最小距离”,只判断了“有距离为一的边”,导致遗漏了大部分结果。
我认为在今后的组队编程中,可以让提出需求的人同时给出NL和JML要求,实现者确保二者表达的是一个意思后在开始设计,如果感觉二者并不是一个意思就找到了二者理解的偏差点,从而在编程前明确需求。至于减少信息差,我认为最好的方法是在二者总结好需求文档可能的问题后直接视频解决,然后开始实现。