JML语言梳理

JML理论基础

JML是一种契约式设计,一种形式化java建模语言。契约式设计的核心是将代码实现和设计本身分离。设计者只考虑设计层面的问题。

注释结构

  • 行注释  //@ annotation

  • 块注释 /*@ annotation*/

JML表达式

原子表达式
  • \result 表示非void类型的方法执行结果

  • \old(expr) 表示表达式expr在相应方法执行前的取值

  • \not_assigned(x,y,...) 表示括号内变量是否被赋值

  • \not_modified(x,y,...) 表示括号内变量是否被修改

  • nonnullelements(container) 表示container对象存储对象不会有null

  • type(type) 表示type对应的类型

  • typeof(expr) 返回expr对应的准确类型

量化表达式
  • \forall 全称量词

  • \exists 存在量词

  • \sum 返回给定表达式的和

  • \protect 返回给定表达式的连乘

  • \max 返回给定表达式的最大值

  • \min 返回给定表达式的最小值

  • \num_of 返回给定变量满足条件的个数

集合表达式
  • 集合构造表达式 new JMLObjectSet {Integer i | s.contains(i) && 0 < i.intValue() }

操作符
  • 子类型关系操作符 E1<:E2

  • 等价关系操作符 b_expr1<==>b_expr2

  • 推理操作符 b_expr1==>b_expr2

  • 变量引用操作符 \nothing,\everything

方法规格

  • pre-condition( requires P )

  • post-condition( ensures P )

  • side-effects( assignable,modifiable )

  • pure 关键字

  • singals 子句

类型规格

  • 不变式invariant

  • 状态变化约束constraint

工具链

  • jml-launcher (the launcher   for the graphical user interface tools).

  • jml and jml-gui (the checker).

  • jmlc and jmlc-gui (compile for runtime assertion checking).

  • jmldoc and jmldoc-gui   (a version of javadoc that includes JML specification information).

  • jmle    (compile for executing or prototyping specifications).

  • jmlrac (a version of java, the VM, that   includes the bin/jmlruntime.jar file in the CLASSPATH, for   running files compiled with jmlc).

  • jmlre (a version of java, the VM,   that includes the runtime support needed for executing   specifications, for running files compiled with    jmle).

  • jmlspec and jmlspec-gui   (generate skeleton specification files from Java source files).

  • jmlunit and jmlunit-gui   (produce unit testing code stubs for use with    JUnit).

  • jtest   (combines the work of jmlc and    jmlunit)

  • jml-junit (a version of   JUnit's swing user interface that   includes the bin/jmlruntime.jar and bin/jmljunitruntime.jar files   in the CLASSPATH, for running JML   and JUnit based tests on files   compiled with jmlc and test cases   generated by jmlunit).

  • openJML

参考链接:http://www.eecs.ucf.edu/~leavens/JML/documentation.shtml

JMLUnitNG/JMLUnit

在同学们的帮助之下,我也体验了一把JMLUnitNG的自动化测试。

我直接采用了讨论区中大佬的java文件进行简单的测试。

 1 // demo/Demo.java
 2 package demo;
 3 ​
 4 public class Demo {
 5     /*@ public normal_behaviour
 6       @ ensures \result == lhs - rhs;
 7     */
 8     public static int compare(int lhs, int rhs) {
 9         return lhs - rhs;
10     }
11 ​
12     public static void main(String[] args) {
13         compare(114514,1919810);
14     }
15 }

具体的过程如下

1 java -jar jmlunitng.jar demo/Demo.java
2 javac -cp jmlunitng.jar demo/*.java
3 javac -cp jmlunitng.jar demo/Demo_JML_Data/*.java
4 java -jar openjml.jar -rac demo/Demo.java
5 java -cp jmlunitng.jar demo/Demo_JML_Test

可以看到JMLUnitNG自动测试几乎选取了在临界边缘的数据,即只会选取边缘值和0。并且做一些关于null和{}的测试。上述代码出现了减法溢出的问题,所以WA了三个点。

个人认为这个JMLUnitNG如果只能这样测试,而且又对JML语法要求如此严格,那它还挺划的。也就是测试其实并不算充分。

架构设计

第一次作业

架构分析

第一次作业需求简单,算是初入JML的一点小试炼。要实现的部分不算太多,针对每个方法只要读懂JML规格,完成逻辑实现即可。

需要注意的是CPU时间,三次作业都严格要求了CPU时间,这就要求了我们将查询方法的复杂度分散均摊,同时选用更好的算法实现。此次作业还没有牵扯到算法,所以只要在图结构变更时,及时更新并保存数据,保证在查询的复杂度为O(1)。

这次作业也让我切实体会了Hash的意义。

类图及复杂度分析

Main.main(String[]) 1.0 1.0 1.0
MyPath.compareTo(Path) 4.0 3.0 4.0
MyPath.containsNode(int) 1.0 1.0 1.0
MyPath.equals(Object) 5.0 2.0 6.0
MyPath.getDistinctNodeCount() 1.0 1.0 1.0
MyPath.getNode(int) 1.0 1.0 1.0
MyPath.getNodeCount() 1.0 3.0 3.0
MyPath.hashCode() 1.0 1.0 1.0
MyPath.isValid() 1.0 1.0 1.0
MyPath.iterator() 1.0 1.0 1.0
MyPath.MyPath(int...) 1.0 1.0 1.0
MyPath.size() 1.0 1.0 1.0
MyPathContainer.addPath(Path) 3.0 3.0 4.0
MyPathContainer.containsPath(Path) 1.0 1.0 1.0
MyPathContainer.containsPathId(int) 1.0 1.0 1.0
MyPathContainer.getDistinctNodeCount() 1.0 1.0 1.0
MyPathContainer.getPathById(int) 2.0 1.0 2.0
MyPathContainer.getPathId(Path) 2.0 3.0 4.0
MyPathContainer.MyPathContainer() 1.0 1.0 1.0
MyPathContainer.nodeCountAdd(Path) 1.0 3.0 3.0
MyPathContainer.nodeCountRemove(Path) 1.0 3.0 3.0
MyPathContainer.removePath(Path) 2.0 3.0 4.0
MyPathContainer.removePathById(int) 2.0 1.0 2.0
MyPathContainer.size() 1.0 1.0 1.0
Total 37.0 39.0 49.0

作业给好了框架,只要负责填充JML规格对应的方法,因此本次类图和复杂度格外可观(

第二次作业

架构分析

这一次作业在第一次作业上新增的核心问题是最短路,是否连通其实也是最短路。所以在CPU时间的要求下,我选择了无权图的BFS算法直接得到单源点的最短路径。另外增加了类似cache机制的询问方法。若询问最短路时未命中,则进行BFS算法并存储得到的结果,且在每次进行图结构变更时,更新最短路缓存区。

另外有意思的一点是,得益于大舞台巨佬的想法——采用映射机制分配静态数组。建立了一个空闲链表管理数组的索引,数组索引与真实结点进行映射,很有OS的感觉!

然而这一次的JML好像不是自己的重点,自己思考算法和实现的时间已经不再关注JML规格。我觉得我跑偏了(

类图和复杂度分析

Main.main(String[]) 1.0 1.0 1.0
MyGraph.addPath(Path) 3.0 3.0 4.0
MyGraph.bfs(int) 1.0 5.0 6.0
MyGraph.bzero() 1.0 1.0 4.0
MyGraph.containsEdge(int,int) 2.0 2.0 3.0
MyGraph.containsNode(int) 1.0 1.0 1.0
MyGraph.containsPath(Path) 1.0 1.0 1.0
MyGraph.containsPathId(int) 1.0 1.0 1.0
MyGraph.edgecount(Path,int) 1.0 2.0 3.0
MyGraph.floyd() 1.0 4.0 4.0
MyGraph.getDistinctNodeCount() 1.0 1.0 1.0
MyGraph.getPathById(int) 2.0 1.0 2.0
MyGraph.getPathId(Path) 2.0 3.0 4.0
MyGraph.getShortestPathLength(int,int) 4.0 1.0 4.0
MyGraph.isConnected(int,int) 3.0 2.0 4.0
MyGraph.MyGraph() 1.0 2.0 4.0
MyGraph.nodeCountAdd(Path) 1.0 4.0 4.0
MyGraph.nodeCountRemove(Path) 1.0 3.0 4.0
MyGraph.removePath(Path) 2.0 4.0 5.0
MyGraph.removePathById(int) 2.0 2.0 3.0
MyGraph.size() 1.0 1.0 1.0
MyPath.compareTo(Path) 4.0 3.0 4.0
MyPath.containsNode(int) 1.0 1.0 1.0
MyPath.equals(Object) 5.0 2.0 6.0
MyPath.getDistinctNodeCount() 1.0 1.0 1.0
MyPath.getNode(int) 1.0 1.0 1.0
MyPath.getNodeCount() 1.0 3.0 3.0
MyPath.hashCode() 1.0 1.0 1.0
MyPath.isValid() 1.0 1.0 1.0
MyPath.iterator() 1.0 1.0 1.0
MyPath.MyPath(int...) 1.0 1.0 1.0
MyPath.size() 1.0 1.0 1.0
Total 51.0 61.0 85.0
Average 1.59375 1.90625 2.65625

这一次的类图还是一样的简单,复杂度还是一样的可观。也许是我最OO的一次(预示着下一次要翻车)

第三次作业

架构分析

第三次作业还是和别的单元第三次一样,都是一堆魔鬼需求。核心问题是动态权(表现在换乘模型),而且还是各种花式带权。

针对换乘的建模,是对我最大的挑战。经过了很长时间的思虑,得到了两种算法——拆点和wjy算法。拆点算法把换乘点全部分离,这样就把动态权转化成为了静态权,然后归约到最短路径算法。但是耐不住我觉得wjy算法惊为天人,而且貌似更好实现,所以还是采用了wjy算法(wjynb!)

这个算法的最大问题在于建图,我尝试了很多方法来贪CPU时间,结果到最后都失败了。所以最终还是采取了直接Floyd全图莽的莽夫算法。每次图结构变更,就对全图进行重构,再Floyd出各种权的最短路,确保了查询的O(1)。虽然计算下来貌似我复杂度炸掉了,但是常数很小,反而跑的很快(???)

类图和复杂度分析

Floyd.unplea(int,int) 1.0 1.0 2.0
Main.main(String[]) 1.0 1.0 1.0
MyPath.compareTo(Path) 4.0 3.0 4.0
MyPath.containsNode(int) 1.0 1.0 1.0
MyPath.equals(Object) 5.0 2.0 6.0
MyPath.getDistinctNodeCount() 1.0 1.0 1.0
MyPath.getNode(int) 1.0 1.0 1.0
MyPath.getNodeCount() 1.0 3.0 3.0
MyPath.getUnpleasantValue(int) 2.0 1.0 2.0
MyPath.hashCode() 1.0 1.0 1.0
MyPath.isValid() 1.0 1.0 1.0
MyPath.iterator() 1.0 1.0 1.0
MyPath.MyPath(int...) 1.0 1.0 1.0
MyPath.size() 1.0 1.0 1.0
MyRailwaySystem.addPath(Path) 3.0 3.0 4.0
MyRailwaySystem.bfs(int) 1.0 5.0 6.0
MyRailwaySystem.bfs(Path,HashMap>) 1.0 11.0 14.0
MyRailwaySystem.bfsconnect() 1.0 6.0 6.0
MyRailwaySystem.bzero() 1.0 1.0 4.0
MyRailwaySystem.complete(Path) 1.0 4.0 4.0
MyRailwaySystem.containsEdge(int,int) 2.0 2.0 3.0
MyRailwaySystem.containsNode(int) 1.0 1.0 1.0
MyRailwaySystem.containsPath(Path) 1.0 1.0 1.0
MyRailwaySystem.containsPathId(int) 1.0 1.0 1.0
MyRailwaySystem.edgecount(Path,int) 1.0 2.0 3.0
MyRailwaySystem.floyd() 1.0 4.0 6.0
MyRailwaySystem.getConnectedBlockCount() 1.0 2.0 2.0
MyRailwaySystem.getDistinctNodeCount() 1.0 1.0 1.0
MyRailwaySystem.getLeastTicketPrice(int,int) 1.0 2.0 2.0
MyRailwaySystem.getLeastTransferCount(int,int) 1.0 2.0 2.0
MyRailwaySystem.getLeastUnpleasantValue(int,int) 1.0 2.0 2.0
MyRailwaySystem.getPathById(int) 2.0 1.0 2.0
MyRailwaySystem.getPathId(Path) 2.0 3.0 4.0
MyRailwaySystem.getShortestPathLength(int,int) 4.0 1.0 4.0
MyRailwaySystem.getUnpleasantValue(Path,int,int) 1.0 1.0 1.0
MyRailwaySystem.isConnected(int,int) 3.0 2.0 4.0
MyRailwaySystem.MyRailwaySystem() 1.0 2.0 4.0
MyRailwaySystem.nodeCountAdd(Path) 1.0 4.0 4.0
MyRailwaySystem.nodeCountRemove(Path) 1.0 3.0 4.0
MyRailwaySystem.reconstr() 1.0 2.0 2.0
MyRailwaySystem.removePath(Path) 2.0 3.0 4.0
MyRailwaySystem.removePathById(int) 2.0 1.0 2.0
MyRailwaySystem.size() 1.0 1.0 1.0
MyRailwaySystem.throwexcep(int,int) 4.0 1.0 4.0
Total 67.0 94.0 128.0
Average 1.5227272727272727 2.1363636363636362 2.909090909090909

可以看到第三次作业只用了四个类,所以复杂度有点小炸,尤其是MyRailwaySystem类466行(在500行的边缘疯狂试探)。

我对于这样的规格一大起来的代码完全丧失了把控能力。可以和前两次作业做出很鲜明的对比,几乎是写了一个模块就忘记了上一个模块。能够把每个模块有效的组织起来,这也许就是OO的作用。(而我仿佛没有得到这样的灵丹妙药)

迭代中对架构的重构

三次作业下来几乎没有大型重构,只有第二次作业的BFS转换成了第三次作业的Floyd。没有重构加上我的弱鸡代码水平,最终的结果就是第三次的框架的的确确非常烂。虽然它也顺利完成了逻辑实现,但是不得不承认,我自己甚至都没有完全把控住每一个模块之间的层次关系。尤其是三种权值问题,我也没有进行抽象,只是进行了完全一致的“代码重用”。

作业中bug情况

第一次作业

强测

很不幸,虽然这一次作业真的极其简单,但是我确实在这一次作业炸掉了。这一次作业完成的过于轻松,因此几乎没有进行任何有意义的测试。最后的结果就是一个bug炸翻,强测60的美好结局(

这个bug其实是在path_add时,私心想封装一个统计结点个数的方法,结果手抖封装到了if的外面,具体代码如下。

 1 //错误示范
 2 nodeCountAdd(path);
 3 if (!containsPath(path)) {
 4     pathListInt.put(count, path);
 5     pathListPath.put(path, count);
 6     count++;
 7     return (count - 1);
 8 }
 9 //正确代码
10 if (!containsPath(path)) {
11     pathListInt.put(count, path);
12     pathListPath.put(path, count);
13     count++;
14     nodeCountAdd(path);
15     return (count - 1);
16 }

可以说一行代码,就造成了车祸级别bug,这就提醒了我

  • 严格检查代码实现逻辑

  • 任何情况下都不可以不做测试

互测

互测屋里估计都是一群翻车老哥,恰好碰上了五一,所以也几乎没有怎么刀人(所以我甚至没有被刀)。结果随便交个空刀却刀中了人。

具体bug都表现在compare方法上,很多同学compare会出现字典序比较错误或者溢出的问题。我认为也是测试不充分的锅。

第二次作业

强测

吃一堑长一智,老老实实测试。虽然我还是没有掌握OpenJML的使用方法(且用户体验极差),但是和室友对拍还是简单粗暴的。果真也没有挂强测点)

且这一次本地测试也没有发现什么玄学bug,算是比较友好的一次作业。

互测

互测也没有被找出bug。

互测屋里一位同学被对拍器揪出了bug,事实上这位同学好像也被屋里的人快乐的刀了很多次。不过具体分析对拍器6000行的代码确实不好找bug。。。

第三次作业

强测

OpenJML还是没有学会,JUnit也没有用上。仍然是那个对拍器,仍然是那个室友。实名感谢一票巨佬。

这一次完成的很仓促,对拍器帮助我找到了很多bug。

  • remove时没有及时更新矩阵

  • 更新矩阵时遗漏了部分矩阵没有reset

  • 建图时需要仅对这一部分path做Floyd再进行更新

  • 缓存机制需要时刻维护信号isnew

互测

互测也没有被找出bug。

互测屋里的同学们都经受住了我对拍器的考验。但是Altergo一挑四,还是四个不同的bug,实在是tql。这也侧面说明了对拍器其实覆盖面不够。

心得体会

  • JML的资料其实比较少,北航走在了时代前沿。JML是一种形式建模语言,所以追求的应该是精确的描述设计需求,杜绝自然语言的二义性问题。优秀的JML能有效将代码模块化,不会出现把控不住架构的问题。

  • 写出一手优秀的JML其实比写代码还要难,而且一旦方法规模变大,JML的复杂程度甚至会超过代码本身。而且撰写JML其实对写者的挑战很大,需要有非常严密的逻辑。我认为撰写JML的核心其实是尽可能把设计思路展现出来,所以JML其实是给人看的。而不是另一种编程语言。

  • JML可以给出实现设计的思路,但不是码代码指南。很多情况下,我们都不需要完全按照JML按部就班完成自己的代码。

  • 这一单元的第三次作业,我对于JML就几乎没有关注,仅仅只是看了下。因为算法问题耗费了我大量的精力,虽然我确实菜,但感觉还是有些许的偏题。

  • 虽然单元测试有些许麻烦,但是确实能够以覆盖面更大的形式进行测试。单纯的黑盒对拍其实完全取决于数据生成是否足够强力。

  • OO方面,我还是没有很好掌握把各个模块抽象分层的能力。第三次作业,自己最直观的感受就是,所有的方法混为一谈,根本谈不上什么继承封装。我觉得自己还是不OO(,需要学习的东西太多了,实际码代码的水平也有待提高。

  • 数据结构的债还是还上了。。。

  • JML工具链的用户体验其实并不好,这一块由于我太菜了,所以决定自己赶快去学了。

第三单元也结束了,尽管我并不OO,但还是努力在往OO的方向追赶

最后一单元——冲压!

转载于:https://www.cnblogs.com/xiangxj/p/10905217.html

菜鸡谈OO 第三单元总结相关推荐

  1. OO第三单元——规格化设计与地铁系统——总结

    OO第三单元--JML的使用与地铁系统 梳理JML语言的理论基础.应用工具链情况 JML The Java Modeling Language (JML) is a specification lan ...

  2. OO第三单元JML规格总结分析

    OO 第三单元博客作业 1. JML语言概述 1.1 理论基础 综述 Java建模语言(Java Modeling Language,JML)是一种进行详细设计的符号语言,使人们用一种全新的方式来看待 ...

  3. OO第三单元单元总结

    OO第三单元单元总结 测试过程 黑箱操作和白箱操作 黑箱操作我认为就是通过随机构造合法数据对代码进行测试,在测试过程中应当着重对边界条件.前置条件.结果正确性等方面进行判断.不需要具体到代码的逻辑,通 ...

  4. OO第三单元总结性博客

    OO第三单元总结性博客 JML理论基础与工具链 JML理论基础 JML是一种将java程序规格化表达的一种语言,其描述的是行为的规格,而非具体的代码实现. 通过对程序开展规格化设计,当然前提是使用JM ...

  5. BUAA OO 第三单元 规格化设计

    黑箱测试 黑箱测试将被测软件看做一个打不开的黑盒,主要根据功能需求设计测试用例进行测试. 可以检测软件功能能否按照需求规格说明书的规定正常工作,是否有功能遗漏,行为.性能是否满足要求等. (个人感觉像 ...

  6. OO第三单元作业总结

    OO第三次作业总结 一.JML (一)JML语言理论基础 (1)JML表达式: JML表达式包括以下几种: 原子表达式如\result(方法执行后的返回值).\old(表达式在相应方法执行前的取值): ...

  7. JML规格编程——BUAA OO第三单元作业总结

    整体概述 这个单元整体围绕Java Model Language(JML)展开,通过学习JML规格了解契约式编程的过程,课上实验中进行了JML规格的简要编写,课下实验主要通过阅读规格并按照规格的要求正 ...

  8. OO第三单元总结:JML

    目录 第三单元--jml.junit与图 第三单元--jml.junit与图 〇.问题描述 ​ 本单元主题为JML的学习,问题载体为一个无向图路径管理系统.在三次作业种,情景不变,需求递增.因此需要在 ...

  9. OO第三单元JML总结

    目录 目录一.JML语言的理论基础二.应用工具链三.部署SMT Solver四.部署JMLUnitNG/JMLUnit五.三次作业分析第一次作业第二次作业第三次作业六.总结与心得体会 一.JML语言的 ...

最新文章

  1. ehcache.xsd
  2. HDU - 3709 Balanced Number(数位dp)
  3. echarts 树图样式美化_echarts树图tree改为流程图
  4. 穿戴式设备的用户体验设计-郝华奇
  5. iOS 11.3 显示:Apple ID 或将实现微信式扫码登陆
  6. Centos解决ppp: compressor dropped pkt
  7. 天线接口:SMA、TNC 有什么差别?
  8. linux vi 排序命令,10 个你必须掌握的超酷 VI 命令技巧
  9. 省市县乡村五级行政区域数据2021(国家统计局)
  10. frm考试可以用计算机,FRM考试,能用哪些金融计算器?(内含用法功能全解读)...
  11. 你应该知道的原型图工具Mockplus(摩客)
  12. html把图片放到文章右边,怎么在文章中把图片放在文字的左边、右边、中 – 手机爱问...
  13. 【Python】个人所得税
  14. 如何理解变量的声明和定义?
  15. 【系统辨识】辨识理论基础及古典辨识方法(随机过程+白噪声+基本概念)
  16. python 图片锐化_Python图像处理介绍--图像模糊与锐化
  17. 区块链投资机构风起云涌 Mixed Elements为何能更胜一筹!
  18. OPENJUDGE 1.7 11:潜伏者
  19. Python 神仙姐姐图像手绘效果实现
  20. 网易云 短信验证码+验证+tp5

热门文章

  1. 1617_MIT 6.828 JOS boot代码分析
  2. 超简单的豆瓣电影Top前250影片基本信息爬取
  3. 邮件的发送和接收过程——STMP、POP、IMAP、MIME
  4. 一文教你学会社交论坛小程序开发
  5. 百度贴吧引流必须知道的七大技巧
  6. 电商行业市场行情解析,shopee虾皮平台讲解
  7. spring注解驱动开发-7 Spring声明式事务
  8. 从零开始的神经网络构建历程(二,用全连接前馈神经网络识别手写数字mnist)
  9. 比较PostgreSQL与MySQL两大开源关系数据库管理系统
  10. arduino 舵机接线图_arduino+16路舵机驱动板连接测试(示例代码)