菜鸡谈OO 第三单元总结
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 第三单元总结相关推荐
- OO第三单元——规格化设计与地铁系统——总结
OO第三单元--JML的使用与地铁系统 梳理JML语言的理论基础.应用工具链情况 JML The Java Modeling Language (JML) is a specification lan ...
- OO第三单元JML规格总结分析
OO 第三单元博客作业 1. JML语言概述 1.1 理论基础 综述 Java建模语言(Java Modeling Language,JML)是一种进行详细设计的符号语言,使人们用一种全新的方式来看待 ...
- OO第三单元单元总结
OO第三单元单元总结 测试过程 黑箱操作和白箱操作 黑箱操作我认为就是通过随机构造合法数据对代码进行测试,在测试过程中应当着重对边界条件.前置条件.结果正确性等方面进行判断.不需要具体到代码的逻辑,通 ...
- OO第三单元总结性博客
OO第三单元总结性博客 JML理论基础与工具链 JML理论基础 JML是一种将java程序规格化表达的一种语言,其描述的是行为的规格,而非具体的代码实现. 通过对程序开展规格化设计,当然前提是使用JM ...
- BUAA OO 第三单元 规格化设计
黑箱测试 黑箱测试将被测软件看做一个打不开的黑盒,主要根据功能需求设计测试用例进行测试. 可以检测软件功能能否按照需求规格说明书的规定正常工作,是否有功能遗漏,行为.性能是否满足要求等. (个人感觉像 ...
- OO第三单元作业总结
OO第三次作业总结 一.JML (一)JML语言理论基础 (1)JML表达式: JML表达式包括以下几种: 原子表达式如\result(方法执行后的返回值).\old(表达式在相应方法执行前的取值): ...
- JML规格编程——BUAA OO第三单元作业总结
整体概述 这个单元整体围绕Java Model Language(JML)展开,通过学习JML规格了解契约式编程的过程,课上实验中进行了JML规格的简要编写,课下实验主要通过阅读规格并按照规格的要求正 ...
- OO第三单元总结:JML
目录 第三单元--jml.junit与图 第三单元--jml.junit与图 〇.问题描述 本单元主题为JML的学习,问题载体为一个无向图路径管理系统.在三次作业种,情景不变,需求递增.因此需要在 ...
- OO第三单元JML总结
目录 目录一.JML语言的理论基础二.应用工具链三.部署SMT Solver四.部署JMLUnitNG/JMLUnit五.三次作业分析第一次作业第二次作业第三次作业六.总结与心得体会 一.JML语言的 ...
最新文章
- ehcache.xsd
- HDU - 3709 Balanced Number(数位dp)
- echarts 树图样式美化_echarts树图tree改为流程图
- 穿戴式设备的用户体验设计-郝华奇
- iOS 11.3 显示:Apple ID 或将实现微信式扫码登陆
- Centos解决ppp: compressor dropped pkt
- 天线接口:SMA、TNC 有什么差别?
- linux vi 排序命令,10 个你必须掌握的超酷 VI 命令技巧
- 省市县乡村五级行政区域数据2021(国家统计局)
- frm考试可以用计算机,FRM考试,能用哪些金融计算器?(内含用法功能全解读)...
- 你应该知道的原型图工具Mockplus(摩客)
- html把图片放到文章右边,怎么在文章中把图片放在文字的左边、右边、中 – 手机爱问...
- 【Python】个人所得税
- 如何理解变量的声明和定义?
- 【系统辨识】辨识理论基础及古典辨识方法(随机过程+白噪声+基本概念)
- python 图片锐化_Python图像处理介绍--图像模糊与锐化
- 区块链投资机构风起云涌 Mixed Elements为何能更胜一筹!
- OPENJUDGE 1.7 11:潜伏者
- Python 神仙姐姐图像手绘效果实现
- 网易云 短信验证码+验证+tp5
热门文章
- 1617_MIT 6.828 JOS boot代码分析
- 超简单的豆瓣电影Top前250影片基本信息爬取
- 邮件的发送和接收过程——STMP、POP、IMAP、MIME
- 一文教你学会社交论坛小程序开发
- 百度贴吧引流必须知道的七大技巧
- 电商行业市场行情解析,shopee虾皮平台讲解
- spring注解驱动开发-7 Spring声明式事务
- 从零开始的神经网络构建历程(二,用全连接前馈神经网络识别手写数字mnist)
- 比较PostgreSQL与MySQL两大开源关系数据库管理系统
- arduino 舵机接线图_arduino+16路舵机驱动板连接测试(示例代码)