CSDCounter

OO第三单元总结

OO第三单元总结

JML梳理###

JML语言理论####

身为规格描述语言,JML有着严格的语法规范。

  • 注释
    行注释: //@annotation ,
    块注释: /* @ annotation @*/

  • 方法规格
    前置条件(pre-condition) :require
    后置条件(post-condition) :ensure
    副作用范围限定(side-effects) :assignable/modifiable

  • 类型规格
    不变式invariant P:不管当前类处在什么状态,都必须满足谓词P的要求
    状态变化约束constraint :类的状态在每一次变化时,都必须满足谓词P的要求

  • 表达式
    首先,JML的表达式是对Java表达式的一种扩展,Java的语法一般都可以在JML中使用。
    \result表达式:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值。
    \old(expr)表达式:用来表示一个表达式 expr 在相应方法执行前的取值。
    \not_assigned(x,y,...)表达式:用来表示括号中的变量是否在方法执行过程中被赋值。
    \not_modified(x,y,...)表达式:与上面的\not_assigned表达式类似,该表达式限制括号中的变量在方法执行期间的取值未发生变化。
    \nonnullelements(container)表达式:表示 container 对象中存储的对象不会有 null 。
    \type(type)表达式:返回类型type对应的类型(Class)。
    \typeof(expr)表达式:该表达式返回expr对应的准确类型。
    \forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。
    \exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。
    \sum表达式:返回给定范围内的表达式的和。
    \product表达式:返回给定范围内的表达式的乘积。
    \max表达式:返回给定范围内的表达式的最大值。
    \min表达式:返回给定范围内的表达式的最小值。
    \num_of表达式:返回指定变量中满足相应条件的取值个数。

相关工具链####

OpenJML:通过-check选项来检查JML规格的规范性
SMT Solver:用来检查代码与规格的等价性
JMLUnitNG:可以用来生成数据代码,可根据规格自动化生成测试样例,进行单元测试。
JML compiler:进行运行时断言检查

部署JMLUnitNG/JMLUnit,针对Graph接口的实现自动生成测试用例###

根据讨论区大佬的帖子下载安装openjml以及jmluniting的jar包
测试样例使用基本的减法:

具体使用过程如下:


测试效果符合预期,少数情况由于未考虑溢出而错误。

架构设计###

  • 第九次作业

    本单元的第一次作业,功能较为简单,主要完成一些结点和路径的增删查等基本操作。在处理节点时,我使用ArrayList来存储一条Path的节点序列,因为其操作非常类似静态数组,同时其又是变长的,非常适合节点序列这种不变的序列。本次架构我失败的地方在于对于路径的存储依然想当然的使用了arraylist而不是大多数人使用的hashmap,导致本次作业我超出了CPU时间时间限制,我的架构是直接使用了两个arraylist进行暴力查找,所以这次作业我的架构其实是非常失败的。

  • 第十次作业

    这次作业较上次作业相比,增加了图这一概念,递进式的完善此路径结点系统。我吸取上次作业的教训,对本次作业彻底进行了重构。首先就是存储结构上我使用了两个hashmap,一个key为路径id,值为Mypath,用来存储图,另一个cueentNodes,key为结点id,值为该结点在所有路径中一共出现了几次,主要用来记录结点的数量。每次增加或删除路径时都需要更新currentNodes,同时因为其中的值是已知的,所以在删除时我可以明确知道要不要将该结点从hashmap中删去,查找结点数量时直接返回currentNodes的size即可,大大简化了时间复杂度。关于图的运算,我使用静态数组邻接矩阵来存储图,注意到同一时间最多只有250个结点,因此可以开一个hashmap以结点id为key,0到249的映射值作为value将所有结点都映射到0到249这一区间上来,这也使得用邻接矩阵存储图成为了可能。在计算最短路径方面,我使用floyd算法,时间复杂度是n的3次方,但因为我一次执行就可以得到当前所有结点的最短路径,而且只在指令个数很少的增删指令更新结果,所以我认为这么做是完全值得的。

  • 第十一次作业

    这次作业又在上次图的基础上进一步深入,模拟一个地铁线路管理系统,主要增加了四项功能:计算连通块个数,计算最少换乘,最低票价以及最低不满意度。为实现此要求,在上次作业的基础上我新增了一种图,此图中所有在一条路径上的点之间都有一条边,因此每多走一条边就代表多换乘一次,最少换乘次数在这个图中就转换为两节点之间的最短路径减1,用类似的思想也可以解决最低票价与最低不满意度的问题,唯一不同的是邻接矩阵中存储的权值不同,对于最低票价而言权值是所有单一路径中两结点的最低票价,对于最低不满意度而言权值是所有单一路径中两结点之间的最低不满意度,再用floyd遍历时加上换乘的代价即可。这里需要注意的是对于环路的处理,可以将单一路径也按照同样的方法建成图,用floyd来求出最低票价与最低不满意度,再将所有结果进行整合,如此一来就不需要考虑环路了。这种架构的好处是只在指令数较少的增删指令更新结果,其他指令查询的复杂度都是常数,缺点是删除路径时难以维护,只能采用重构的方法重新建图。这次作业我的设计风格也不是很好,写了一个将近500行的类。。

BUG与BUG修复###

主要遇到了两种bug,WA与TLE。
第一次作业由于不熟悉hashmap选择了arraylist导致在计算空间内结点个数时超过了CPU时间限制。修复的话可以使用hashmap来存储数据,在MyPathContainer类中维护两个Hashmap,即Path到Id和Id到Path的两个映射,这样保证了查询的效率。此外,由于结点序列是不会修改的,只要算出不同结点个数,就把它存下来,以后再次查询就不需要重新计算了。
第二次作业WA主要是在计算最短路径时没有考虑结点自身到自身的最短路径,如果起点和终点一样那么应该直接输出0,而我默认结点只能走不等于自身的点,这也就使得我自身到自身的最短路径全部是2,还是自己考虑问题不够全面细致,非常不应该。

心得体会###

本单元的主题是JML规格化设计,我认为JML就是一种使用规范化的语言来描述程序中模块的功能与机制。不需要肉眼去读代码,只需要阅读相应模块的JML规格说明我们就可以准确的知道该模块的功能,出bug时也可以根据各模块功能准确定位bug可能发生的场所。可以想见在大规模的工程开发中可以通过JML将项目合理明确分工,不仅严谨准确而且可以帮助程序员更好的理解代码。
但在本单元的学习过程中我也感到JML存在的一些缺点。若方法的功能比较复杂不易描述,那么往往JML就需要使用五六行冗长的并列量化表达式,不利于书写规格描述的同时,也大大增加了阅读负担甚至让规格说明与实际代码有点本末倒置的意思。因此我认为我们也不能一味的拘泥于JML,规格说明与代码本就是相辅相成的,只有合理使用才能发挥最好的效果。
无论如何,使用规格化的方式来描述代码的思想是十分重要的,今后写代码时也一定要有书写规格的意识,这对我们对代码的理解以及写代码的能力都有着潜移默化的影响。

posted on 2019-05-22 19:23  CSDCounter  阅读(181)  评论(0编辑  收藏  举报

导航