面向对象第三单元博客(JML)

 // 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(124, 376);
     }
 }

前言

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

执行

1  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(-2147483648, -2147483648)
 Failed: static compare(0, -2147483648)
 Failed: static compare(2147483647, -2147483648)
 Passed: static compare(-2147483648, 0)
 Passed: static compare(0, 0)
 Passed: static compare(2147483647, 0)
 Failed: static compare(-2147483648, 2147483647)
 Passed: static compare(0, 2147483647)
 Passed: static compare(2147483647, 2147483647)
 Passed: static main(null)
 Passed: static main({})
 ?
 ===============================================
 Command line suite
 Total tests run: 13, Failures: 3, Skips: 0
 ===============================================

测试结果的第一行是 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的规格并不是很理解,感觉对于程序可以写规格,但是没必要。不是说没必要写架构类注释,而是规格本身并不容易写,因为这两次作业的规格甚至比实现的代码还多、比源码还难以理解,给我一种还不如用伪代码来写。但是现在通过这单元的学习,我对规格有了更进一步的了解——还是不理解为什么要写,可能是境界还未达到。希望自己以后学习更多的知识,开阔自己的视野,继续努力!

原文地址:https://www.cnblogs.com/lucien98/p/10908674.html

时间: 2024-10-29 08:07:40

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

OO第三单元博客总结

原文地址:https://www.cnblogs.com/xiongmaoage/p/10903350.html

第三次博客

第三次博客 一. 规格发展历史 从20世纪60年代开始,就存在着许多不同的形式规格说明语言和软件开发方法.在形式规格说明领域一些最主要的发展过程列举如下: 1969-1972 C.A.R Hoare撰写了"计算机编程的公理基础(An Axiomatic Basis for Computer Programming)"和"数据表示的正确性证明"两篇开创性的论文,并提出了规格说明的概念. 1974-1975 B.Liskow/S.N. Zilles和J. Guttag引

第三版博客装订完毕

html: <div class="header"> <div class="wrap"> <div class="logo"> <a href="http://www.cnblogs.com/qiuge227/"> <img src="http://images.cnblogs.com/cnblogs_com/qiuge227/565381/o_logo.png

第三周博客问题总结

1.学会了```的用法 一开始并没有成功,后来发现是因为输入法没有切换成英文,今后这样的问题要避免出现.--20145106 2.问题:教材86页,Guess的代码,猜数字,不知道原先没有设定好一个数字,为什么可以在0-9之间猜中一个数 解答:百度了(Math.random()10)这行代码的意思,知道了(Math.random())的意思是在[0,1)之间随机产生一个数字,10,所以变成了[0,10)之间随机产生一个数字,所以可以进行猜数字 --20145123 3.IntegerCache.

第三版博客修改计划

为了让自己在学习中不断进步,随时保持学以致用的心态是尤为重要的.这样,一来让人有一种成就感的同时,也有一种满足感. 二来让你永远保持着一种心态,那就是谦逊,释然. 什么是进步,进步就是在每天的点点滴滴中,有所获,有所得. 什么是付出,付出就是每天你都知道自己在做什么,为什么要做,并为之努力的做. 什么是收获,收获就是即使你看不到它微小的变化,但在不知不觉中让自己仿佛变了一个人似的. 记得,记忆中感受最深的一本书是斯宾塞·约翰逊著作的<谁动了我的奶酪?>(Who Moved My Cheese?

Android 博客园客户端 (三) 博客列表和内容显示

项目地址:https://github.com/ZhangTingkuo/AndroidCnblogs.git 经过很多天的努力,终于完成了博客界面的三个Fragment列表显示,分别是,首页.推荐.阅读排行.其实,距离上次,并没有增加更多的功能. 下一步,添加新闻界面的列表和内容显示.

面向对象第四次博客

测试与正确性论证差异对比 测试指为检测目标是否符合标准而采用特殊工具和方法进行验证.测试的优点在于你可以不用知道程序是如何实现的,直接输入测试样例,然后比较程序运行结果和正确结果,如果两者不同,就说明程序肯定有BUG.从中我们知道测试是快速方便的,它可以检测并定位一个程序的BUG.但是测试的效果很大程度取决于测试样例是否完善,如果不完善,就会遗漏一些潜在的致命的BUG,这也是测试最大的缺点.若要达到很好的测试效果,需要构造规模庞大的测试用例,这会消耗很多精力,而且你并不能保证这个庞大的测试用例已

oo第三次博客总结

1.规则化发展历史 形式化方法的研究高潮始于20世纪60年代后期,针对当时所谓"软件危机",人们提出种种解决方法,归纳起来有两类:一是采用工程方法来组织.管理软件的开发过程:二是深入探讨程 序和程序开发过程的规律,建立严密的理论,以其用来指导软件开发实践.前者导致"软件工程"的出现和发展,后者则推动了形式化方法的深入研究.经过30多 年的研究和应用,如今人们在形式化方法这一领域取得了大量.重要的成果,从早期最简单的形式化方法一阶谓词演算方法到现在的应用于不同领域.不

面向对象第三单元(JML)总结体会

一.JML语言 定义 Java建模语言(JML)是一种行为接口规范语言,可用于指定Java模块的行为 .它结合了Eiffel的契约方法设计 和Larch 系列接口规范语言的基于模型的规范方法 . 理论基础 JML是契约式语言的一种具体表现形式. 契约(Contact):声明一个函数/方法的时候,对函数的输入和输出所具备的性质是有所期望和规定的.有时候这种性质会被我们明确的写出来,有时候会被我们忽略掉.这些期望和规定就是Contract. 而契约设计的核心便是断言(assertion) :永远为真