北航OO(2020)第三单元博客作业
一、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)第三单元博客作业的更多相关文章
- 北航OO(2020)第四单元博客作业暨学期总结
一.第四单元架构设计 1.第一次作业 我在本次作业中设置了多个储存结构:Directory,ElementsInName,ElementsInId,Cache. Directory: 顾名思义,这是个 ...
- oo第三单元博客作业
JML语言理论基础 Java建模语言(Java Modeling Language,JML)是一种进行详细设计的符号语言,他鼓励你用一种全新的方式来看待Java的类和方法.JML是一种行为接口规格语言 ...
- OO第四单元博客作业
OO第四单元博客作业 BUAA_1706_HugeGun 目录 第四单元作业架构设计 四个单元架构设计及OO方法理解 四个单元测试理解与实践演进 课程收获 一点建议 第四单元作业架构设计 ### 第十 ...
- [BUAA OO]第三次博客作业
OO第三次博客作业 1. 规格化设计的发展 我认为,规格化设计主要源自于软件设计的两次危机.第一次是由于大量存在的goto语句,让当时被广泛应用的面向过程式的编程语言臃肿不堪,在逻辑性上与工程规模上鱼 ...
- OO第三次博客作业——规格
OO第三次博客作业——规格 一.调研结果: 规格的历史: 引自博文链接:http://blog.sina.com.cn/s/blog_473d5bba010001x9.html 传统科学的特点是发现世 ...
- [2017BUAA软工]第三次博客作业:案例分析
第三次博客作业:案例分析 1. 调研和评测 1.1 BUG及设计缺陷描述 主要测试博客园在手机端上的使用情况. [BUG 01] 不能后退到上一界面(IOS) 重现步骤:打开博客首页中任意博文,点击博 ...
- 北航OO(2020)第二单元博客作业
第二单元第一次作业 多线程设计策略 第一次作业的想法是设计三个线程:输入线程,调度器线程以及电梯线程.输入线程获取请求并发送给调度器线程:调度器线程通过查询电梯线程的状态(等待.停靠以及移动),并综合 ...
- 第三周博客作业<西北师范大学|李晓婷>
1.助教博客链接:https://www.cnblogs.com/lxt-/MyComments.html 2.学生作业打分要求: https://www.cnblogs.com/nwnu-dai ...
- OO第三次博客作业(第三单元总结)
(1)梳理JML语言的理论基础.应用工具链情况 Java 建模语言(JML)将注释添加到 Java 代码中,这样我们就可以确定方法所执行的内容,而不必说明它们如何做到这一点.有了 JML,我们就可以描 ...
随机推荐
- 周期串(JAVA语言)
package 第三章习题; /* * 如果一个字符可以由某个长度为k的字符串重复多次得到,则称该串以k为周期. * 例如:abcabcabcabc 以3为周期(注意:它也以6和12为周期) * ...
- 攻防世界 reverse 进阶 16-zorropub
16.zorropub nullcon-hackim-2016 (linux平台以后整理) https://github.com/ctfs/write-ups-2016/tree/master/nu ...
- 在ASP.NET Core中用HttpClient(五)——通过CancellationToken取消HTTP请求
用户向服务器发送HTTP请求应用程序页面是一种非常可能的情况.当我们的应用程序处理请求时,用户可以从该页面离开.在这种情况下,我们希望取消HTTP请求,因为响应对该用户不再重要.当然,这只是实际应用 ...
- Spring Native 项目,把 Spring 项目编译成原生程序!
Spring Native 是什么 优点 缺点 原生镜像(native image)和常规 JVM 程序的区别 前置条件:GraalVM 支持的语言 关键特性 GraalVM 下的 Java 微服务 ...
- JS中dom操作的事件
Click--点击事件 优先级:dom.onclick 高于标签上的onClick属性 监听事件 --不会覆盖前面的事件效果 dom.addEventListener() 括号里面有三个参数 1 ...
- GitlabCI/CD&Kubernetes项目交付流水线实践
GitlabCI实践 GitLabCI/CD基础概念 为什么要做CI/CD? GitLab CI/CD简介 GitLabCI VS Jenkins 安装部署GitLab服务 GitLabRunner实 ...
- 关于MySQL日志,我与阿里P9都聊了些啥?
写在前面 周末,我与阿里P9资深技术专家(这里就不说名字了),聊起了MySQL这个话题,为啥会聊这个呢?因为他看到我出版了一部<MySQL技术大全:开发.优化与运维实战>,对书籍的评价也是 ...
- Spring Security OAuth 2.0 发放令牌接口地址自定义
OAuth 2.0 如何获取令牌 以密码模式为例,获取 Token curl --location --request POST 'http://oauth-server/oauth/token' \ ...
- 【macOS】Homebrew & Homebrew cask macOS软件包管理神器
Homebrew Homebrew 与 Homebrew Cask Homebrew 是基于 OS X 的套件管理工具,是一个开源的 Ruby 脚本,专门用于快速下载软件.更通俗地讲,Homebrew ...
- position:sticky 粘性定位的几种巧妙应用
背景:position: sticky 又称为粘性定位,粘性定位的元素是依赖于用户的滚动,在 position:relative 与 position:fixed 定位之间切换.元素根据正常文档流进行 ...