OO第三单元总结

1. 理论基础与应用工具链

1.1. 理论基础

1)注释结构

以@开头,行注释//@,块注释/*@ @*/,纯粹查询方法/*@pure@*/。规格中每个子句都必须以;结尾。

model规格层次的描述,不是此类的声明组成部分。

non_null对象引用不能为null。

前置条件,requires。

副作用范围限定,assignable,此方法能够修改的类成员属性。\nothing关键词。

后置条件,ensures。

规格变量:静态static,实例instance。Interface中声明规格变量,明确变量类别。

2)表达式

JML断言不可使用带有赋值语义的操作符。

2.1)原子表达式

\result关键词,方法执行的返回结果。

\old(expr)表达式expr相应方法执行前的取值。针对一个对象引用而言,只能判断引用本身是否发生变化,不能判断引用所指对象实体内容是否变化。

\not_assigned(x,y...)括号中变量是否在方法执行过程中被赋值。

\not_modified(x,y...)限制括号中的变量在方法执行期间取值不变。

2.2)量化表达式

\forall全称量词

\exists存在量词

\sum返回给定范围内的表达式的和

\product返回给定范围内的表达式的连乘结果

\num_of返回指定变量中满足相应条件的取值个数

2.3)集合表达式,集合构造表达式

2.4)操作符

子类型关系操作符,等价关系操作符,推理操作符,变量引用操作符

3)方法规格

正常行为规格normal_behavior

异常行为规格exceptional_behavior    signals语句抛出异常

also:父类方法定义了规格,子类重写方法补充规格;一个方法,多个功能规格描述。

4)类型规格

针对Java中数据类型设计的限制规格,静态成员可直接通过类型引用,实例成员只能通过类型的实例化对象引用。

针对类型中定义的数据成员:不变式限制invariant,约束限制constraint

5)契约式设计

设计者为函数定义正式的、精确的并且可验证的接口,这样,为传统的抽象数据类型又增加了先验条件、后验条件和不变式。调用者满足被调用者的要求,被调用者为调用者提供满足规格的服务。

1.2. 工具链

JML,对Java规格化设计,行为接口规格语言。

junit是一个Java语言的单元测试框架。Junit是一套框架,继承TestCase类,就可以用junit进行自动测试,主要使用断言进行结果判断。一般流程为:创建Test类,写测试方法并用断言进行结果判断,运行测试。junit还可以判断测试代码对被测试代码的覆盖性,但被覆盖的代码不一定在功能上是正确的。

2. 测试

 使用jmlunitng和openjml针对Group接口实现自动生成测试用例

JMLUnitNG/JMLUnit可以实现自动生成测试用例。文件树如下:

 

测试结果如下:

 

 

 生成的测试数据大多是特殊数据或者边界数据,比如null,0,极大值,极小值。

 没有通过测试的函数,有的是没有处理null这种特殊情况,有的是过大、过小的数据超出了数据类型的范围。

3. 架构设计

3.1. 第一次作业

 

 第一次作业一共实现了三个类:实现Network接口的MyNetwork类,实现Person接口的MyPerson类和Main类。其中Main类通过Runner调用MyNetwork类和MyPerson类的构造方法,实现新的实例,并处理输入数据进行相应操作。MyPerson类主要功能有判断邻接,比较,查询权值,返回Id等等。MyNetwork类的主要功能有增加用户、增加关系、查询连通、查询排名等等。模拟了一个只有用户好友列表和部分基本信息的社交网络。

3.2. 第二次作业

 

 第二次作业在第一次作业的基础上加入了实现接口Group的新类MyGroup。Main类功能与上一次相似。MyPerson类添加新的功能加入群。MyNetwork新增功能加群,查询平均年龄,年龄方差,总权值,关系总数等等,但具体功能实在MyGroup类中实现的。MyGroup类中有添加用户、计算性格、计算总关系数等方法。关于新加方法的实现,我采用存储、更新的方式,设置相应的成员变量,每次操作使相应值发生变化时,进行更新。在原有社交网络的基础上加入了群组,同一用户可以加入不同群组,并丰富了社交网络的功能。

3.3. 第三次作业

 

 第三次作业在第二次作业的基础上新增Item类用于实现优先队列求最短通路。Main类功能与之前类似,MyPerson类功能与之前相同。MyGroup新增从群中把用户删除的方法。MyNetwork中新增了属性金额和查询修改操作。此外还增加了查询连通块个数、强连通、最短路径,从群中删除用户操作。在原有社交网络上增加借款关于金额的操作,用户可以退群。

 

关于架构设计,我完全按照官方代码给出的基本结构实现的,有四个类MyPerson、MyGroup、MyNetwork、Main没有自己再次进行架构设计。在第三次作业时,为了在最短路径中使用优先队列,加入了新类Item。老师提过对于网络的很多操作,实际上用到最频繁、最便利的不是点而是边,可以存储边来进行一种新的架构设计。

4. 关于bug

4.1. 第一次作业

第一次作业出现了CPU超时的情况。在实现isCircle的过程中由于课下测试不充分,导致递归方法错误陷入环路的死循环,导致超时。对递归方法的标记位进行修改后,避免了环路死循环的情况,但是设置标记、删除标记的过程使得同一个点可以被多次经过。用这种方法虽然可以判断是否连通,但是实质上并不是“找出能到达的所有点”,而是“找出能到达的所有通路”,这就使得方法过于复杂、运行时间过长,最终出现CPU超时。最终在递归过程中只设置标记,不再删除标记,每次调用isCircle新建一个容器用于存储标记位,超时问题得以解决。

4.2. 第二次作业

第二次作业出现了WA和CPU超时问题。在实现计算性格和年龄均值、方差,权值,关系数时,采用储存数据并更新的方法。要注意addPerson和addRelation均会影响权值和关系数,且群组中自己与自己的关系数+1而自己与别人的关系数+2,自己与自己的权值+0,自己与别人的关系数+2*value。在最初计算性格时,我维护了一个与MyGroup大小相等的数组,用来记录每一步的性格情况,后来改成只记录当前的性格值也能满足要求。存储数据并更新的方法虽然效率可能会高一点,但是需要修改的类与方法很多,还会加大被修改方法的复杂度。

第二次作业又出现了超时问题,但我实际上已经把可能会出现问题的方法的算法进行优化了。最终发现,在MyNetwork类中我使用容器ArrayList存储Person信息,将ArrayList换为HashMap后,超时问题得到解决。此外,我将除了用于标记存储的所有ArrayList均换成了HashMap。

4.3. 第三次作业

第三次作业出现了WA和CPU超时的问题。在判断强连通的时候,没有考虑到两个点不连通的情况导致WA。在实现查询最短路径方法的时候,最初采用删标记的dfs方法,后来发现这样会遍历所有点,但不能遍历所有通路,会导致有的路径没有计算。于是开始考虑删标记的dfs,但是这样虽然遍历了每一条通路,却导致时间很长,复杂度过高,容易超时。于是采用了O(n^2)的迪杰斯特拉算法,但是由于复杂度太高,仍会超时。于是引入优先队列和新类Item,采用优先队列优化的迪杰斯特拉算法,但是仍然超时。查看测试数据,猜想是因为addPerson和addRelation方法复杂度过高,进行优化修改,但是结果仍然不理想。最终将存储标记用的容器类型从ArrayList改为HashMap,超时问题解决。

关于isCircle和BlockSum问题,一开始均采用不删标记的dfs方法。后来因为频繁超时却又找不到原因,开始对两个方法进行优化。尝试过一点并查集方法后来放弃。最终isCircle仍采用不删标记的dfs算法,但是维护一个记录连通块的HashMap,当dirty被置为1时,调用isCircle或BlockSum方法时,就会对HashMap进行更新,同时把dirty位置0。但是超时的问题并没有得到解决。

关于强连通的超时问题,一开始采用删除标记的dfs遍历包含id1的所有环路,如果存在一条环路经过id2则返回true。复杂度过高,容易超时。于是改为暴力枚举方法,将每个与id1和id2都连通的点依次删除,如果删除后id1与id2不再连通则返回false。但还是超时。而且id1和id2直接isLinked的情况要单独考虑。将存储标记用的容器类型从ArrayList改为HashMap,超时问题解决。

5. 心得体会

在撰写规格时,我们要考虑的只有架构设计,而不需要花费精力于具体如何实现。规格的设计既不能太简单也不能太复杂,尽量保证不同规格之间不要过于互相依赖。我们平时不会在意的一些内容在撰写规格的过程中都要显示地标明,不如添加元素不只是简简单单的加入新的元素,还要保证原有的每一个元素不被修改。

规格实现了设计与实现的分离,同时还使得代码逻辑的正确性能够被精密的验证,这就为代码的测试带来了很多便利。与此同时在写代码的过程中不必费心于这么写逻辑会不会有问题、是不是正确的这类关于设计的问题。但是也正因如此,导致代码和规格之间如果不注意会出现不同。一种规格有多种代码实现方式,而我们需要在规格正确的前提下,保证所实现的代码是完全满足规格的。

在实现规格的过程中,不仅要考虑逻辑功能的实现,还要考虑数据如何存储。规格给出的只是抽象的数据存储,关于具体的实现可能会有更巧妙、更灵活的算法,不要只专注于各种各样的算法去正确、快速的实现功能,基础的数据也很重要。

posted @ 2020-05-23 18:25  18231002  阅读(118)  评论(0编辑  收藏  举报