BU AAOO 第三单元作业总结
😶😶第三单元作业总结
JML是怎么回事呢?
JML想必大家都很熟悉吧,但JML是怎么回事呢?下面小编就带大家一起了解吧。
JML其实就是让我们程序“规格化”的一个工具,大家可能会感到很惊讶,程序为什么需要规格化呢?但事实就是这样,小编也感到非常惊讶。那么这就是关于JML的全部了,大家有什么想法呢?欢迎在评论区告诉小编一起讨论哦。我们下期再见,拜拜。:)
(小编又回来了
在一、二单元,我们学习理解了面向对象中的层次关系和并发关系,随着代码量的逐渐增大,发量的日益减少,我们越来越需要思考如何对程序进行“管理”。
”管理“一词包括很多内容,在一个大型项目中,代码层面需要我们从“设计”、“分工”、“交流”、“测试”等层面进行管理(套用经管课理论知识),而规格化就是一种可行的管理手段。
-
在设计阶段,核心是解决需求,设计者可以根据实际需求对代码框架进行抽象,用形式化的语言来规定类、方法的功能,但不需要关注具体实现。
-
在实现阶段,程序员拿到某个类或方法的形式化语言描述的表述,就进行代码填写和补全,而无需对整个庞大的项目设计有全方位的理解。一方面方便分工合作,另一方面保证了得到的代码是符合期望的,不会因为不同成员想法的出入而产生不兼容。
-
在测试阶段,除了黑盒测试,测试员还可以根据规格撰写白盒测试。如果测试充分,理论上可以保证程序百分百的正确率。

“没有规矩,不成方圆”,JML就是其中一种形式化的、行为接口规格语言。
JML以注释的形式,出现在方法或成员属性的前面,指导你应该做什么。
其语法十分简单,核心为三个部分:前置条件(requires)、副作用(modifies)、后置条件(effects)
我们在学习离散数学的时候都学过如何严格地描述自然语言,其实JML也是一样的道理,甚至更简单(指level0,更高的我不知道)
-
requires
”前置条件“本质上就是一个条件,类比条件语句,只有满足了条件才能执行接下来的行为。
requires规定了传入数据的范围,作为一种约定,使程序员可以不用担心存在范围外的非法输入。
同时类似条件语句吗,它还可以让程序根据不同的输入执行不同的行为,是否抛出异常等。
-
effects
顾英文名思意,表示产生了什么样的影响。一般规定了返回值\result等于什么。
-
modifies
“副作用”这个翻译我觉得很迷惑,这明明是个动词,一定要给它起一个名词的名字。
比如说很多返回值为void的方法做的事情就是修改数据,因此动了哪些地方,没动哪些地方,都是要说清的(后者容易被遗漏)。
在实际应用中常出现的语句是“assignable”
其余只需要眼熟一些基本的符号如:
\forall 等价于离散数学中的 \(\forall\)
\exits 等价于离散数学中的 \(\exists\)
\sum 等价于离散数学中的 \(\sum\)
简直和latex的语法一模一样
就能立即上手👍
于是很多同学(本人),就误以为第三单元只要无脑照着规格翻译代码就ok啦!
JML工具链是怎么回事呢?
JML上期小编刚讲过,但JML工具链是怎么回事呢?下面小编就带大家一起了解吧。
JML工具链其实就是专门为JML开发的一系列工具,它们都很难用,大家可能会感到很惊讶,JML工具链为什么难用呢?但事实就是这样,小编也感到非常惊讶。:)由于没有较为完善的生态圈,这些工具的完善度以及对jdk版本的兼容度都不太可观。那么课程组既然挑选了这样一个冷门的形式化语言,有没有兴趣自己做个好用一点的工具呢?或者课程组有没有兴趣开发专门教学用的形式化语言buaaml呢?欢迎在评论区讨论留言。
-
关于JUnit:
JUnit是我从第一次作业就开始尝试用的东西,不用工具链的话纯手写非常像计组ISE生成的test bench!
我在第二次作业徒手写了所有方法的各种requires的测试数据,遂卒👻
性价比不!能!再!低!
但是我本着初学JML涨见识的心态,就算这东西性价比低,我也试试它有多低,然后发现它真的低。
我个人觉得在写代码的过程中写一部分测一部分是很好的习惯,JUnit可以帮我们搭个框架。
但最好是有针对性的,不要为了追求覆盖率花费大量时间做意义不大的工作。
比如本单元作业,照着规格写代码再照着规格写测试,如果对规格理解有误,就测不出来!写测试只能帮你再”审一遍题“,如果你第一遍题理解错了,第二遍先入为主也很容易理解错。
所以这种情况下不能太依赖“自己”写的白盒测试,而如果将测试与实现交给不同的人去做或许更有助于发现错误
-
关于OpenJML:
只支持jdk8,我在部署的时候各种出错,十分迷惑。后来专门下载eclipse,借助插件勉强成功。
-
关于JMLUnitNG:
弥补手写JUnit测试的缺陷,自动生成测试样例。如果充分开发的话应该非常好用,但没有如果。
SML Solver
java -jar .\openjml.jar -exec .\Solvers\z3-4.7.1.exe -esc .\src\com\oocourse\spec3\main\Person.java
输入上条指令对 Person 进行检查,没有反应。。。
删了一处JML后

发现错误定位还是很准的
java -jar openjml.jar -check -dir Person.java MyPerson.java
再对全部src经行检查(显然有很多错的),结果如下

总结为该检查出来的没检查出来,不该检查出来的检查出来了
(如果是因为我个人操作不得当,恳请指出)
JMLUnitNG
JMLUnitNG不支持\forall,\exists等语法,所以我只选取了作业中简单的几个方法
经过漫长的部署后得到部分效果如图

可以看到基本上是选取两个边界、零值、空值进行测试。我们作业中如果addPerson等操作的操作对象是零值的话要么是不满足上层调用规(会Failed),要么没有意义(没有边和点的图有什么意义吗),总体感觉意义不大。
架构梳理
本人三次作业都没有考虑架构问题,照着规格写代码。
事实上我并没有意识到课程组推荐自己考虑架构(是我漏掉了课程组的强调还是课程组没有强调),甚至有强迫症尽量避免写规格没命名的方法(每个方法都有规格描述让我觉得很舒服)

第一次
主要考虑了容器的选取,以及isCircle方法的算法选取
第二次
加入了Group类以及一些可能卡时间的query运算,通过缓存age、character等属性避免重复大量运算,特别需要注意数据更新时缓存的更新
第三次
抛开没有存在感的借钱操作,加入了一些图相关运算。
进一步考察了对JML或者说对形式化表述的阅读能力。需要同学们读出形式化语言背后的图论的意义,比如qsl和qbs等。
同时对算法有了进一步的要求,当大家把注意力放在qsl的算法上时,qmp给了很多人会心一击。

可以看到涉及到缓存更新以及算法的地方复杂度都较高,或许良好的架构能够分担算法的负担。
在互测时我看到有些优质代码,确实实现了自己的架构,我认为是很有眼界的。当大多数人陷入JML细节时大佬们能挑出来看整体,是很厉害的。
bug与修复
每份指导书都有些一句话
可以认为,只要代码实现严格满足JML,就能保证正确性。
对此我比较疑惑,一个超时但正确的代码是否满足正确性?照指导书的意思来讲,是满足的。
助教也在讨论区提到不用过分考虑算法,后来我才发现,助教所说的“过分”,是在第九层,评论区里大佬讨论的在第5层,而我在第1层。🙃
所以我根据以上信息,安心地判断我的迪杰斯特拉不需要优化。然后它炸了。我觉得这像极了小马过河的故事。
第一次作业错了一处,虽然进了互测,但分数也不可观。原因在于对JML的轻敌,两下写完了,也没怎么测,酿成了惨案。发现同屋一处bug。
第二次作业无错,有大量对拍,考虑时间的因素而进行缓存优化。同屋似乎都没什么bug。
第三次qmp方法ctle了三个点,发现同屋一个功能性bug,用的是写好的JUnit。
心得体会
感悟还是挺多的。
-
关于JML本身
从 没听说过 到 爱恨交加(随便找了个成语),再次感受到了很多事情在越做越大的时候都需要引入“工程化管理的方法”,这点是不分学科的。JML等规格语言就是一种工具,上手不难,但普及度不高致使工具链发展不完善。
-
关于课程组引入JML
按照课程组的设计,第一单元训练面向对象思想以及层次化设计,第二单元训练并发、多线程,第三单元训练规格化的面向对象设计,第四单元训练模型化设计与管理。
在网上搜索规格化面向对象,能看到的基本上都是学长学姐的博客。我不清楚是只有北航在教这样的东西还是在教这些的学校中只有北航选择让学生进行博客总结。不管怎么样,我的直观感受是课程组很用心的在进行课程设计,选择了“少有人走的路”。
诸彤宇老师在研讨课上提到,以后和面试官可以吹,我是北航毕业的,我可以给程序做形式化验证(大概意思我没记错吧)。
OO课无疑是一门精品课程,规格化面向对象设计或者是JML都是别的地方不一定会教的。OO课,而不是基于JAVA的面向对象程序设计课,向我们展示了各个维度的面向对象设计思想,但我觉得还有很多地方需要改进。
其实在计算机组成原理课上高小鹏院长就像我们初步讲授了“工程化”相关的知识,但和OO课不一样在于计组没有单开一个单元去让大家理解(计组课不是由不同单元组成的,而是迭代式的),而是在用verilog写CPU后提出了这种方法。最典型的是给出用表格做设计的示范,并且要求每个人实验报告中都附上表格,结果是大部分人都确实觉得”工程化“确实是一种降低难度、减少错误、提高效率的方法。
我觉得OO的规格化思想某种程度上其实也差不多,其功效可能更多地体现在便于测试从而减少错误、为合作提供可能从而提高效率。但如果从实现到测试都由一个人完成,JML的功效似乎并不能发挥出来。
课程组一直在鼓励我们使用JUnit测试,但是本次作业中错误主要为两类:规格理解出错、算法超时。
如果是规格理解出错,那写出的测试也是错的。就好比交卷前自己再审一遍题检查一遍,可以找出错吗,当然可以,但效率是在很低。如果读JML写代码的和读JML做测试的不是一个人,效率应该会更高。或许课程组可以考虑加入合作的成分,否则JML显得很多余,远远没有对拍效率高。
如果是算法超时,那跟JML关系就微乎其微了,JML不管这一点,这也是让我很迷惑的一点,这单元对算法的要求是不是有点跑题了,关于此我下文会再述。
与此同时,面向对象思想应该是贯穿整个课程始终的,在这个单元大部分同学似乎都在翻译JML,很少去思考这种工具在面向对象方面起到的作用。一切设计和编写JML都是课程组完成的,很好地训练了助教的相关能力,而同学们就是翻译翻译JML,研究研究算法。可以说是课程组完成了大脑的工作,我们大多数人都扮演了躯干的角色。
可能课程组会说,我们强调过了要自己思考架构,不要一味的照着规格写代码。
首先,我不怎么记得在第三次作业结束前,有针对作业强调过我们的作业本意是让你自己去设计规格的(可能我听漏了吧)
其次,我觉得更好的课程设计是用某种方法引导学生走合理的路,而不是告诉学生有这条路。毕竟现在资源如此之多,谁都可以拿到,而课程组存在的意义就是推我们一把,不知道我理解的对不对。
-
关于本单元中的算法
这点没有怪课程组,应该还是我太菜了。计算机系算法是很基础的东西,要求算法一点也不过分。但我比较迷惑的如上面模块【bug与修复】所述,我觉得就像“小马过河”的故事,课程组传达给我了一种不需要考虑算法的信息。可能课程组觉得tarjan、堆优化迪杰斯特拉基础的不能再基础,不算过分优化,但是事实是我没有那么强。一年前学的数据结构都忘了,虽然拾起来很快,但这些不是信手拈来的东西。当然指导书中清清楚楚地写着数据限制、也写了标程复杂度,为什么这些东西我没看到呢,因为我不太看得懂(捂脸)。我个人在计算复杂度方面道行太浅,当年数据结构只教过大概的概念,我应该自己去学一下,是我又懒又菜。
-
关于JML工具链
我觉得课程组选择JML挺好的,并没有因为它存在感低而放弃这样一个适合上手的工具,也说明是用心找过的,而不是“拿来主义”。但是JML工具链的生态圈实在是太差啦,为什么还一定要求使用呢。
本身用JUnit测试在此次作业中就显得很多余(如上文所述),还一定要用不好用的工具做多余的事情,太痛苦啦。课程组或许可以考虑自己开发开发(站着说话不腰疼)。
计组选了MIPS,提供了够用的MARS;OS继承着使用了MIPS,照着JOS改成了我们BUAA的OS,建议OO课要么不强r制使用工具链,要么抄一抄做个能用的工具链 -
关于课时
我觉得前两个单元不是说写完就能彻底领会面向对象思想的,而JML也占四周显得有些多了,或许可以调一下。好像往届这单元就有不是三次作业的,课程组可能有自己的考虑吧。
-
关于互测
互测其实可以实现写代码和写测试分离的效果,或许可以要求每个人有不同的实现且编写自己的JML。互测时通过阅读别人的JML来测试。但是如果不管怎么设计最终都有个标答的话,黑盒评测姬还是效率更高,不需要都别人的JML都能hack。但如果没有标答,而且还不是多线程那种,而是正确与否只取决于此人写的JML,就又太不现实了。或许可以让数人合作完成整个程序,每个人根据JML进行一部分实现,之后以小组为单位进行对抗。但这使得给分又是一个难题,而且听起来很像软件工程。但是我个人理解JML就是很工程化的东西,不放在工程化的场景里就显得有些小才大用。
以上都是我的胡思乱想,缺乏细致、整体的考量,如果有人看到的话,就当看个乐子吧~
希望OO越来越棒~

浙公网安备 33010602011771号