// demo/Graph.java
package demo;

import java.util.ArrayList;

public class Demo {
/*@ public normal_behaviour
@ ensures \result == lhs - rhs;
*/
public static int compare(int i, int j) {
return i - j;
}

public static void main(String[] args) {
compare(, );
}
}

前言

Java 建模语言(Java Modeling Language, JML) 是一种用来进行详细设计的表示法,它倡导一种思考方法和类的新思路。

我们所学习的面向对象分析和设计的原则之一就是应当尽可能地将过程实现往后推,因此我们在进行编程时,会先从框架开始,完成类和方法的定义;当需要过程实现时,我们转向过程设想。设计架构时,我们应该将预期的功能记录下来以便实现方进行实现,注释是一种表达方式。但是,每个人的注释可能都不一样,自己的理解不一定符合他人的思路,这种“语言”隔阂无疑将架构和实现分离,造成信息不对称,从而影响作业效率甚至导致错误。而 JML 统一了注释的风格及含义,使得 JML 类型注释对所有人达意,帮助了我们完成设想与实现的无阻塞连接。


JML语言

理论基础

Java建模语言(JML)是一种行为接口规范语言,可用于指定Java模块的行为。它结合了Eiffel的契约式设计方法和Larch接口规范语言家族的基于模型的规范方法,以及一些细化演算的元素。

在使用方面,有以下规格:

  • 注释结构:JML以javadoc注释的方式来表示规格,每行都以@起头

  • JML表达式: JML的表达式是对Java表达式的扩展,新增了一些操作符和原子表达式.

    • \result表达式:表示一个非void 类型的方法执行所获得的结果.

    • \old( expr )表达式:用来表示一个表达式expr 在相应方法执行前的取值.

    • \forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束.

    • \exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。

    • ...

  • 方法规格:方法规格的核心内容包括三个方面,前置条件、后置条件和副作用约定。

    • 前置条件通过requires子句来表示: requires P; 。其中requires是JML关键词,表达的意思是“要求调用者确保P为
      真”。

    • 后置条件通过ensures子句来表示: ensures P; 。其中ensures是JML关键词,表达的意思是“方法实现者确保方法执
      行返回结果一定满足谓词P的要求,即确保P为真”。

    • 副作用指方法在执行过程中会修改对象的属性数据或者类的静态成员数据,从而给后续方法的执行带来影响。从方法规格的角度,必须要明确给出副作用范围。

工具链

Openjml : OpenJML项目的目标是为JML和当前Java实现一个完整的工具,便于实践者和学生使用该工具指定和验证Java程序。

单元测试工具(JMLUnitNG) : 一个用于jml注释的Java代码的自动化单元测试生成工具,包括使用Java 1.5+特性(如泛型、枚举类型和增强的for循环)的代码。与最初的JMLUnit一样,它使用JML断言作为测试预言。


JUnit--验证

按照讨论区帖子安装后,进行以下验证

准备阶段

生成前的文件树

 demo
└── Demo.java

执行

  jmlunitng demo/Demo.java

生成后的文件树

 demo
├── Demo_InstanceStrategy.java
├── Demo.java
├── Demo_JML_Data
│ ├── ClassStrategy_int.java
│ ├── ClassStrategy_java_lang_String1DArray.java
│ ├── ClassStrategy_java_lang_String.java
│ ├── compare__int_lhs__int_rhs__0__lhs.java
│ ├── compare__int_lhs__int_rhs__0__rhs.java
│ └── main__String1DArray_args__10__args.java
├── Demo_JML_Test.java
├── PackageStrategy_int.java
├── PackageStrategy_java_lang_String1DArray.java
└── PackageStrategy_java_lang_String.java
编译
  • 用 javac 编译 JMLUnitNG 的生成文件

    执行

     javac -cp jmlunitng-1_4.jar demo/**/*.java
  • 用 jmlc 编译自己的文件,生成带有运行时检查的 class 文件

    这一步直接使用 openjml 自带的 jmlc,执行

     openjml -rac demo/Demo.java
测试

执行

 java -cp jmlunitng-1_4.jar demo.Demo_JML_Test

运行结果

 
[TestNG] Running:
Command line suite

Passed: racEnabled()
Passed: constructor Demo()
Passed: static compare(-, -)
Failed: static compare(, -)
Failed: static compare(, -)
Passed: static compare(-, )
Passed: static compare(, )
Passed: static compare(, )
Failed: static compare(-, )
Passed: static compare(, )
Passed: static compare(, )
Passed: static main(null)
Passed: static main({})

===============================================
Command line suite
Total tests run: , Failures: , Skips:
===============================================

测试结果的第一行是 racEnabled 的测试,意在检测我们的主文件是否带有 JML 的运行时检查,若没有则跳过所有测试。


作业架构设计

第三次集合了第二次和第一次,不再赘述


代码实现的bug和修复情况

第一次作业

第一次作业没有什么难度,但是有一点还是值得注意的。

对于指令的输入,有以上几点限制。总上限3000条,PATH_ADD最多300条,每条路径最大节点数为1000,这意味着最多是300000个节点。此外,cpu时间限制为5s。一开始会被这次作业简单的外表迷惑,但是算下来要处理的数据挺多的,尤其是对于DISTINCT_NODE_COUNT,如果每次都要算一遍的话,一定会超时,因此在进行添加删除操作时需要计算该值,减少一次运行的压力。此外对于PATH,如果只采用list容器,对遍历操作有利,但是查询却是线性的,不利于PATH_CONTAINS_NODE类指令,因此考虑采用加入hash容器提高效率。

第二次作业

第二次作业在第一次作业的基础上增加了图的功能,增加了边、连通、路径等概念,对应CONTAINS_EDGE、IS_NODE_CONNECTED、SHORTEST_PATH_LENGTH。对于CONTAINS_EDGE,只需增加存储边的容器即可,采用hash容器,因为对edge主要涉及查询操作。对IS_NODE_CONNECTED指令,我采用了bfs(广度优先遍历)的方式计算;因为边没有权值,对于SHORTEST_PATH_LENGTH我也采用了bfs;

这次的数据限制如下:

bfs最佳复杂度接近O(n),最差情况接近O(n^2),在程序的最大运行cpu时间为20s的限制下,7000条bfs也是不太能够吃得消的。因此在这次的作业中我才用了缓存的机制——在进行bfs(i, i+n)时,我们实际上找到了 i 到达 i + 1、i + 2...等点的最短路径,因此可以使用容器存储这些可能用到的数据,以优化时间。当PATH_ADD/PATH_REMOVE的时候,需要注意以前的缓存已经失效,应设置版本更新机制。

第三次作业

第三次作业增加了Raiway的实现,即将第二次实验的图具现为地铁路线图,据此来得到LEAST_TICKET_PRICE、LEAST_TRANSFER_COUNT、LEAST_UNPLEASANT_VALUE等值,数据限制如下:

由于此次图的边有了TICKET_PRICE、TRANSFER_COUNT、UNPLEASANT_VALUE的值,所以这次问题抽象为为带权图之间的最短路径。此次作业我才用了dijkstra算法来查找最短路径。但由于个人的理解问题,导致没处理好换乘问题,没有通过中测。我本想着换乘可由上一条路径和下一条路径确定,比如1-2-4,2-3-5,那么1-5的历程为1-2-3-5。由我的算法,1-2-3为换乘因为1-2在PATH1上,2-3在PATH2上。但是我忽略了1-2-3,2-3-4,从1到4,历程1-2-3-4的情况,根据我的算法是没有换乘的,因此导致了这次作业的失败,我没有意识到地铁站的id即便相同,其换乘的含义也不一定是一样的。修复时采用了讨论区dalao的拆点的算法,将地铁站的换乘意义分开了。

对规格心得体会

这单元我们面向对象的课程主要讲了关于规格的一些内容。一开始我对java的规格并不是很理解,感觉对于程序可以写规格,但是没必要。不是说没必要写架构类注释,而是规格本身并不容易写,因为这两次作业的规格甚至比实现的代码还多、比源码还难以理解,给我一种还不如用伪代码来写。但是现在通过这单元的学习,我对规格有了更进一步的了解——还是不理解为什么要写,可能是境界还未达到。希望自己以后学习更多的知识,开阔自己的视野,继续努力!

面向对象第三单元博客(JML)的更多相关文章

  1. 北航OO(2020)第三单元博客作业

    一.JML理论基础及相关工具链 1.JML理论基础 该部分梳理本单元作业中涉及到的JML知识. 1.1注释结构 JML采用javadoc注释的方式来表示规格,且每行以@开头.通过使用//@annota ...

  2. oo第三单元博客作业

    JML语言理论基础 Java建模语言(Java Modeling Language,JML)是一种进行详细设计的符号语言,他鼓励你用一种全新的方式来看待Java的类和方法.JML是一种行为接口规格语言 ...

  3. oo第三次博客-JML规格

    这三周的作业主要是围绕以JML来约束代码开发,以确保程序的正确性与鲁棒性. Part 1:三次作业的实现与bug 第一次作业没有任何算法和数据结构上的难度,对于Path和PathContainer的各 ...

  4. OOP第三章博客

    OO第三单元博客 • (1)梳理JML语言的理论基础.应用工具链情况: 理论基础: 网络资料上面介绍JML有两种主要的用法: 开展规格化设计.这样交给代码实现人员的将不是可能带有内在模糊性.二义性的自 ...

  5. OO第四单元博客

    第四单元博客 这个单元的作业,emmmm助教们做的工作还是一如既往的多,我们只负责添一添代码,最后一次作业了,感谢各位助教和老师,同时也希望我能顺利通过这最后一关. 架构设计 第一次作业架构展示 第一 ...

  6. OO第四单元博客作业

    OO第四单元博客作业 BUAA_1706_HugeGun 目录 第四单元作业架构设计 四个单元架构设计及OO方法理解 四个单元测试理解与实践演进 课程收获 一点建议 第四单元作业架构设计 ### 第十 ...

  7. 第四单元博客总结——暨OO课程总结

    第四单元博客总结--暨OO课程总结 第四单元架构设计 第一次UML作业 简单陈述 第一次作业较为简单,只需要实现查询功能,并在查询的同时考虑到性能问题,即我简单的将每一次查询的结果以及递归的上层结果都 ...

  8. [BUAA OO]第三次博客作业

    OO第三次博客作业 1. 规格化设计的发展 我认为,规格化设计主要源自于软件设计的两次危机.第一次是由于大量存在的goto语句,让当时被广泛应用的面向过程式的编程语言臃肿不堪,在逻辑性上与工程规模上鱼 ...

  9. OO第三次博客作业——规格

    OO第三次博客作业——规格 一.调研结果: 规格的历史: 引自博文链接:http://blog.sina.com.cn/s/blog_473d5bba010001x9.html 传统科学的特点是发现世 ...

随机推荐

  1. 吴裕雄--天生自然 R语言开发学习:图形初阶

    # ----------------------------------------------------# # R in Action (2nd ed): Chapter 3 # # Gettin ...

  2. python列表推导式(扫盲)

    1) 简单了解: 所谓的列表推导式,就是指的轻量级循环创建列表. 格式: 列表推导式的常见形式: my_list = [ item for item in iterable] my_list: 列表名 ...

  3. fiddler 针对单个接口打断点

    在命令行输入相关指令: 以慕课网为例: 请求前设置断点:bpu 实例: bpu https://www.imooc.com/index/getstarlist 请求 https://www.imooc ...

  4. 接轨国际,碰撞更多科研火花——第八届ChinaSys大会专访微软亚洲研究院首席研究员张霖涛

    作者:微软亚洲研究院实习生 徐祎雪 卢思奇 2015年6月5日至6日,由中国科学院深圳先进技术研究院先进计算与数字工程研究所主办的第八届中国计算机系统(ChinaSys)学术研讨会在厦门大学召开.来自 ...

  5. Hexo next主题安装algolia

    一直在使用hexo写自己的博客,最近博客刚刚搬家重新搞了下博客: 1.为hexo添加站内搜索 我用的是algolia,在next主题5.x以上的版本集成了algolia站内搜索功能,我们只需要简单的配 ...

  6. Vue.observable()使用方法

    前言 随着组件的细化,就会遇到多组件状态共享的情况, Vuex当然可以解决这类问题,不过就像 Vuex官方文档所说的,如果应用不够大,为避免代码繁琐冗余,最好不要使用它,今天我们介绍的是 vue.js ...

  7. C++走向远洋——52(十三周阅读程序)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  8. 小白自学机器学习----3.令人头秃的pytorch安装 (No module named 'tools.nnwrap' 错误)

    tensorflow 刚刚会写基础的模块了,今天找到研究方向的代码是pytorch实现的 总是看到这句话,人生苦短,我用pytorch 看来pytorch应该比tensorflow好学,但是!! py ...

  9. notepad++ 快捷键运行python程序目录存在空格的问题

    通常情况下 cmd /k (python.exe文件所在路径) "$(FULL_CURRENT_PATH)"&PAUSE&EXIT 就ok了,路径里有空格就不一样了 ...

  10. nes 红白机模拟器 第4篇 linux 手柄驱动支持

    小霸王学习机的真实手柄,实测CPU 占用 80% 接线图: 手柄读时序: joypad.c 驱动: 普通的字符设备驱动. #include <linux/module.h> #includ ...