一、JML理论基础及相关工具链

1.JML理论基础

该部分梳理本单元作业中涉及到的JML知识。

1.1注释结构

JML采用javadoc注释的方式来表示规格,且每行以@开头。通过使用//@annotation来进行行注释,使用/*@annotaion@*/来进行块注释。

1.2JML表达式

1.2.1原子表达式
  • \result表达式,在方法规格中使用,通过\result来指代返回值。在谓词中使用\result,来表达放回值的限制条件。

  • \old(expr)表达式,返回表达式expr在方法执行之前的值。
  • \not_assigned(x,y,...)表达式,该表达式为一谓词,当括号中变量在方法执行过程中未被赋值返回true,否则返回false。
  • \not_modified(x,y,...)表达式,该表示为一谓词,当括号中变量在方法执行过程取值为变化返回true,否则返回false。
1.2.2量化表达式
  • \forall表达式。表达式格式为\formall V; P1; P2。其中V定义了在该表达式中要使用的变量,谓词P1、P2为变量需要满足的条件。一般P1表示变量的范围,P2表示变量需要满足的其他条件。该表达式整体也为一个谓词,当V定义的变量的所有满足P1的取值都满足P2时返回true,否则返回false。
  • \exists表达式。表达式格式为\exists V; P1; P2。其中V,P1,P2与\forall表达式中的相同。该表达式整体为一个谓词,当V定义的变量存在一个满足P1的取值也满足P2时返回true,否则返回false。
  • \sum表达式。表达式格式为\sum V; P; N。其中V定义了表达式中要使用的变量,谓词P限制了变量的范围,N表示每一个满足P的情况下的增量。表达式最终返回所有增量的加和。
1.2.3操作符
  • 推理操作符==>,使用格式P1 ==> P2。P1、P2为谓词,整个表达式也为一谓词。当P1为true,P2为false时该谓词返回false,否则返回true。
  • 变量引用操作符。\nothing表示空集,\everything表示全集。

1.3数据规格

1.3.1规格变量

在本单元作业中,通过在接口中定义规格变量,来规定要管理的数据。

例如//@public instance model non_null int id;定义了一个规格可见的、引用不为空的、int型实例变量。同理将instance改为static可定义一个静态变量。

1.3.2 /*@spec_public@*/

通过对类中的字段添加/*@spec_public@*/可以指定该变量为规格可见。

1.1.3不变式invariant

//@invariant P;其中P为一个谓词。该语句规定在所有可见状态下,规格所管理的数据都必须满足谓词P。

1.1.4状态变化约束constraint

//@constraint P;其中P为一个谓词。该语句规定数据在前序可见状态与当前可见状态之间需要满足的约束。

1.4方法规格

1.4.1/*@pure@*/

通过对方法修饰/*@ pure @*/,表示该方法仅为查询方法,不会对类的数据做任何修改,使得该方法为规格内可见。

1.4.2行为

一个方法可以有多个行为,行为分为正常行为与异常行为。一个方法也也可以有多个正常行为与异常行为,但他们的前置条件之间不能有交集。正常行为用normal_behavior表示,异常行为用exceptional_behavior表示。多个行为之间用also连接。

1.4.3前置条件

通过使用requires语句实现。格式为requires P。P为一谓词,表示调用该方法时需要满足的限制。一个行为可以有多个requires语句,这些语句之间为且关系,在该行为下需全部被满足。

1.4.4后置条件

通过使用ensures语句实现。格式为ensures P。P为一谓词,表示该方法调用结束时需要满足的限制。一个行为可以有多个ensures语句,这些语句之间为且关系,在该行为下需全部被满足。

1.4.5副作用范围限定

通过assignable语句或者modifiable语句实现。格式为assignable/modifiable v。其中v表示可以被赋值/修改的变量。特别的,当v为\nothing时表示不能赋值\修改任何变量。

1.4.6 signals

signals语句使用在异常行为下。格式为signals E P。E为要抛出的异常包括异常类型与异常变量名称。P为一谓词,表示当谓词满足时抛出异常。当P与该行为前置条件的P相同时,可简化该语句为signals_only E。

2.相关工具链

2.1OpenJML

2.1.1介绍

根据OpenJML官网(http://www.openjml.org/)上的介绍,OpenJML通过使用SMT solver能够为Java程序验证其是否满足JML规格,分为静态(static)检查以及运行时(runtime)检查。

2.2JMLunitng

2.2.1介绍

JMLunitng能通过JML为Java程序提供测试集。

2.2.1本地部署

参考了这篇博客(https://www.cnblogs.com/starmiku/p/10908745.html)的配置。

执行如下指令后

java -jar jmlunitng.jar test/MyGroup.java
javac -cp jmlunitng.jar test/*.java
java -jar openjml.jar -rac test/MyGroup.java
java -cp jmlunitng.jar test.MyGroup_JML_Test

得到如下结果:

总共进行了55次测试,失败了4次。其中3次都是对addPerson传入null参数引起的,但在我们的作业中应当保证了传入的参数不为null。

而测试的样例中集中对边界数据进行了测试。

二、架构设计

1.第一次作业

第一次作业由于对规格不是特别了解,加上对规格的某些语句的工能有所误解,导致第一次作业完全就是按照规格的语句来完成整个设计的。MyPerson中的acquaintance和value,以及MyNetwork中的people都是采用链表结构,对数据的获取和查找基本上都是采用遍历的方式,所以性能较差,担幸好第一次作业在性能上的要求不是十分严格,所以还是逃过了一劫。

1.1类图

2.第二次作业

由于第二次作业比较强调的一点就是性能问题,所以需要对整个作业需要做一次完全的重构。同时将这个社交网络视为以Person为节点,link关系为边,value为边权的无向图。

2.1类图

2.2MyPerson

考虑到acquaintance与value中的数据有一一对应的关系,以及为了提高查找数据的效率,将他们整合成一个以Person为key以value为value的HashMap。

2.3MyGroup

同样为了提高查找效率上的考虑,使用HashSet来作为people数据的组织形式。

对getRelationSum, getValueSum, getConflictSum, getAgeMean, getAgeVar方法,为了不每次调用这些方法都重新遍历一遍数据,所以缓存了realtionSum, valueSum, ageSum, age2Sum(年龄平方和), conflictSum。其中relationSum以及valueSum需要在addPerson以及Network的addRelation时进行更新,其他数据在addPerson时更新即可。另外需要注意的是,ageMean的计算,为了保证与规格中的公式有相同的精确度,需要使用公式ageVar=(age2Sum - 2*mean*ageSum+n*ageSum**2)/n(摘自第十次讨论区乐洋同学的帖子)。

2.4MyNetwork

共用HashMap组织了三个数据:people, groups, peopleInCircle。people以及groups是为了快速通过id查找Person与Group。peopleInCircle无向图的连通分量,peopleInCircle在addRelation时更新。

通过这些储存结构,MyNetwork中大部分方法都只有几个语句,queryCircle方法可以通过查找他们是否在一个连通分量来实现。主要需要注意的方法就是addRelation,当person1与person2在一个Group中的时候需要为为这个Group更新relationSum以及valueSum,并且还需要注意person1与person2是否在同一连通分量,如果不在则需要合并两个连通分量。

3.第三次作业

第三次作业完全基于第二次作业迭代而来。

3.1类图

3.2MyPerson

与第二次作业保持一致。

3.3MyGroup

与第二次作业相比添加了delPerson方法,该方法同时更新people, ageSum, age2Sum, conflictSum, relationSum, valueSum。

3.4MyNetwork

添加了以HashMap组织的money,通过Person的id来查找其money。同时建立了一个内部类Edge来供图算法使用。

queryMinPath使用dijkstra算法,并用优先队列储存边来进行优化。

queryStrongLinked使用tarjan算法来计算点双连通分量以判断两Person是否stronglinked。

三、Bug分析

本单元的bug出现在第三次作业,强测中有两个点出现了CTLE的情况。后来经查看发现这两个点是针对queryMinPath进行测试的,导致CPU时间紧张。

由于自己偷懒就没有再去优化dijkstra算法,于是就选择在Bug修复中直接提交了之前的代码,幸好在评测机资源不紧张的状况下,出现CTLE的两个点都通过了测试。

四、心得体会

由于对JML的阅读还十分有限,所以也谈不上什么心得体会。主要策略就是先界定清楚不同正常行为以及异常行为,然后对每个行为中方法的逻辑进行分析,尝试用规范的逻辑语言去描述它,最后再转化为JML中的语句。

北航OO(2020)第三单元博客作业的更多相关文章

  1. 北航OO(2020)第四单元博客作业暨学期总结

    一.第四单元架构设计 1.第一次作业 我在本次作业中设置了多个储存结构:Directory,ElementsInName,ElementsInId,Cache. Directory: 顾名思义,这是个 ...

  2. oo第三单元博客作业

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

  3. OO第四单元博客作业

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

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

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

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

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

  6. [2017BUAA软工]第三次博客作业:案例分析

    第三次博客作业:案例分析 1. 调研和评测 1.1 BUG及设计缺陷描述 主要测试博客园在手机端上的使用情况. [BUG 01] 不能后退到上一界面(IOS) 重现步骤:打开博客首页中任意博文,点击博 ...

  7. 北航OO(2020)第二单元博客作业

    第二单元第一次作业 多线程设计策略 第一次作业的想法是设计三个线程:输入线程,调度器线程以及电梯线程.输入线程获取请求并发送给调度器线程:调度器线程通过查询电梯线程的状态(等待.停靠以及移动),并综合 ...

  8. 第三周博客作业<西北师范大学|李晓婷>

    1.助教博客链接:https://www.cnblogs.com/lxt-/MyComments.html 2.学生作业打分要求:   https://www.cnblogs.com/nwnu-dai ...

  9. OO第三次博客作业(第三单元总结)

    (1)梳理JML语言的理论基础.应用工具链情况 Java 建模语言(JML)将注释添加到 Java 代码中,这样我们就可以确定方法所执行的内容,而不必说明它们如何做到这一点.有了 JML,我们就可以描 ...

随机推荐

  1. 「免费开源」基于Vue和Quasar的前端SPA项目crudapi后台管理系统实战之自定义组件(四)

    基于Vue和Quasar的前端SPA项目实战之序列号(四) 回顾 通过上一篇文章 基于Vue和Quasar的前端SPA项目实战之布局菜单(三)的介绍,我们已经完成了布局菜单,本文主要介绍序列号功能的实 ...

  2. 【Linux学习笔记0】-虚拟机运行CentOS(VMware12+CentOS)

    目录 一,资源 二,VMware12安装 记录自己学习linux的过程.这将会是一个系列,本文是该系列的第一部分,主要记录虚拟机(VMware12)及对应操作系统(CentOS)的安装过程. 虚拟机( ...

  3. 面试题:ApplicationContext和BeanFactory两种容器区别

    ApplicationContext和BeanFactory两种容器区别 BeanFactory是ApplicationContext容器的父接口 BeanFactory(多例模式): BeanFac ...

  4. 用pyqt5做一个简易的音乐播放器

    需求 要求可以读取音频文档,有播放和暂停的功能 附上代码(1)UI界面 # -*- coding: utf-8 -*- # Form implementation generated from rea ...

  5. DDOS攻击与防御简单阐述,列出DDOS的攻击方法和防御方法

    参考1:https://www.hi-linux.com/posts/50873.html#%E7%BD%91%E7%BB%9C%E5%B1%82-ddos-%E6%94%BB%E5%87%BB 什么 ...

  6. python基础(补充):python三大器之生成器

    生成器的定义 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后 ...

  7. java面试一日一题:请讲下对mysql的理解

    问题:请讲下对mysql的理解 分析:该问题主要考察对mysql的理解,基本概念及sql的执行流程 回答要点: 主要从以下几点去考虑, 1.mysql的整体架构? 2.mysql中每一个组件的作用? ...

  8. JMeter循环读取CSV文件实现接口批量测试

    首先要理解为什么要进行批量测试,当我们在工作中进行接口测试时,项目的接口肯定不止一个,而是很多很多,而且每个接口都需要进行正确参数,错误参数,参数为空,特殊字符等方式来测试接口是否能够正确返回所需的响 ...

  9. Typora 修改代码块高亮样式

    目录 方法一:下载自己喜欢的样式 方法二:获取Typora自制主题 方法三:自己撰写css样式文件 方法一:下载自己喜欢的样式 Typora的代码块语法高亮使用的是CodeMirror实现的,所以需要 ...

  10. c协程库libco几点体会

    https://www.cnblogs.com/dearplain/p/9820913.html 这里说的是Tencent开源的libco. libco的用途和依赖 主要还是c/c++服务端,相比li ...