// 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. ionic2踩坑之文本域自适应高度(自定义指令,适用angular2)

    话不多说,看代码: import { Directive, ElementRef, HostListener,Input, Renderer, Component } from '@angular/c ...

  2. textarea 是否换行的问题解决

    需求:判断当前textarea是否已经换行(这个换行有2种方式:1.不断输入文字直到超过指定宽度后自动换行:2.按了回车以后进行换行) 单纯的解决第二种换行很简单.网上提供了很多常规的解决方案. De ...

  3. Hadoop的存储架构介绍

    http://lxw1234.com/archives/2016/04/638.htm 该文章介绍了Hadoop的架构原理,简单易懂. 目前公司提供Hadoop的运算集群BMR,可以直接申请集群资源.

  4. nginx代理php项目的websocket

    在http段添加 http { ... map $http_upgrade $connection_upgrade { default upgrade; '' close; } ... } 在serv ...

  5. Mongdb创建 连接过程

    在bin目录下启动后 Show dbs 显示库,库里有内容才会显示库名称 Use admin 切换库 没有的默认创建 show users显示用户信息 Show collections显示表名 db. ...

  6. Ubuntu18.04制作本地源

    Ubuntu 18.04 制作本地源 1. 在可联网的Ubuntu18.04上制作源 创建目录 mkdir /opt/debs 最好在目标电脑上创建相同的目录,以免 apt-get install 时 ...

  7. OSX安装Mysql8.0

    OSX下MySQL的安装非常方便,可以通过官网的dmg包进行安装,也可通过brew进行安装.以下介绍如何通过brew如何安装MySQL. 0X00.安装前的准备 既然要通过brew安装,那么就需要确保 ...

  8. Oops 的栈信息分析

    MTK MT55 F3600 平台 现象:播放MP4文件不断快退或者快进系统重启. 关键log: Kernel panic - not syncing: x_msg_q_receive(): not ...

  9. 传智播客学习之Android运行原理 (转)

    传智播客学习之Android运行原理 (2010-03-20 22:45:15) 转载▼ 今天终于忙里偷闲,和大家探讨一下android技术,第一次听到3G应该追溯到大学三年级的时候了,记得当时现代通 ...

  10. [开源福利] Arithmetic Generator

    Arithmetic Generator Built with ❤︎ by Simon Ma ✨ A powerful arithmetic generator