OO-2021-buaa 第三单元

OO第三单元博客总结

第三单元主要考察对JML的阅读和实现。相对之前的实验来说,较为简单。除了需要仔细阅读JML代码外,还需要注意复杂度的问题。因为每一次的作业都是对前一次的增加,因此将第三次作业作为重点分析。

JML简介

JML(Java Modeling Language) 是用于对 Java 程序进行规格化设计的一种表示语言。

表达式
表达式 含义
\result 方法的返回值(非void类型),在谓词中使用表达放回值的限制条件
\old(expr) 返回表达式expr在方法执行之前的值。
\not_assigned(x, y, ...) 当括号中变量在方法执行过程中未被赋值返回true,否则返回false
\not_modified(x, y, ...) 当括号中变量在方法执行过程取值为变化返回true,否则返回false
\forall 全称量词,表示对于给定范围内的元素,每个元素都满足相应的约束
\exists 存在量词,表示对于给定范围内的元素,存在某个元素满足相应的约束
\sum 返回给定范围内的表达式的和
\product 返回给定范围内的表达式的连乘结果
\max 返回给定范围内的表达式的最大值
\min 返回给定范围内的表达式的最小值
\num_of 返回指定变量中满足相应条件的取值个数
<: 子类型关系操作符,如果类型E1是类型E2的子类型(sub type)或相同类型,则该表达式的结果为真,否则为假
<==> <=!=> 等价关系操作符
==> 推理操作符,相当于离散的->,只有(1,0)是false
\everything 全集
\nothing 空集
方法规格
方法规格 含义
requires 表达的意思是“要求调用者确保P为真”
ensures 表达的意思是“方法实现者确保方法执行返回结果一定满足谓词P的要求,即确保P为真”
assignable/modifiable 副作用范围限定,副作用指方法在执行过程中会修改对象的属性数据或者类的静态成员数据,从而给后续方法的执行带来影响。assignble表示可赋值,而modifiable则表示可修改,虽然二者有细微的差异,在大部分情况下,二者可交换使用
(/*@ pure @ */) 纯粹访问性的方法,不会对对象的状态进行任何改变,也不需要提供输入参数
public normal_behavior 正常功能,一般指输入或方法关联this对象的状态在正常范围内时所指向的功能
public exceptional_behavior 与正常功能相对
signals 结构为signals E P,E为要抛出的异常包括异常类型与异常变量名称。P为一谓词,表示当谓词满足时抛出异常

设计策略

这一单元的作业主要是对JML的阅读,并将JML所要达成的目的通过代码表现出来。因此,在这一单元作业中,我先将异常类实现,然后根据JML的内容,实现相应的myPerson、myGroup、myMessage、myNetwork、myEmojiMessage、myNoticeMessage、myRedEnvelopeMessage,并且在此基础上对一些方法进行一些修改,增加一些自己的方法,来优化复杂度。

测试方法

本单元的作业主要的测试方法为黑箱测试,我先将作业提供的测试案例测试后,自己在构造一些数据来进行测试,最后是通过和同学的对拍来进行测试。

同时,我还使用了同学的测评机来随机生成大量数据,对运行时间进行相对的测试。

容器选择

在本单元中,容器的选择关乎到是否可以通过强测。

在第一次的作业中,我全部使用ArrayList来作为容器,轻松通过了强测。

但第二次作业强测全灭,考虑到这次的作业需要很多查询,修改,对遍历,删除的要求较小;同时很多类都具有一个id,因此决定使用hashmap;并且在异常处理的时候,如果使用数组的话,容易出现数组越界的现象,因此在统计异常出现速度的时候也使用hashmap

性能问题

  • isCircle:这个方法用于判断无向图中两点是否处于同一个连通块,可以通过dfs(深度优先搜索)或并查集来实现,我使用了dfs

  • queryBlockSum:如果完全按作业给的JML规格,这个方法是一个\(O(n^2)\)的复杂度,很容易会CPU_TIME_LIMIT_ERROR。因此,需要对其进行优化,考虑到这是一个查询图中连通块数量的方法,可以设置一个全局变量blocksum,在addPerson时相当于加一个节点,则blocksum++,当addRelation时相当于加一条边,通过对isCircle的判断来决定是否blocksum--,因此最终这个方法只需要返回blocksum即可。

  • getValueSum:这个方法是返回边的权值。同样按照作业给的JML规格,是一个\(O(n^2)\)的复杂度,因此将第二个遍历改变为对权值的遍历。

  • getAgeMeangetAgeVar:这两个方法分别是查询group中的平均年龄和年龄的方差,可以使用更新时迭代的方式,即在每次addPerson是总年龄agesum、age2sum增加,在delPerson时总年龄sumAge、age2sum减小,再在对应方法中计算即可。

  • deleteColdEmoji:该方法是删除热度小于limit的表情,可以使用

    emojiHeatlist.values().removeIf(e -> e < limit);
            messages.values().removeIf(e -> e instanceof EmojiMessage &&
                    !containsEmojiId(((EmojiMessage) e).getEmojiId()));
    

    进行遍历删除

  • sendIndirectMessage:这个方法就是求两点之间的最短路径,可以使用堆优化的dijistra算法,复杂度为\(O(nlog^n)\).

因此,这次作业中的所有操作复杂度在\(O(n^2)\)以下,符合时间要求。

架构设计

这一单元的作业抓哟来说就是构建并维护图的模型,通过对边和节点的操作来构建图。Person是节点,Relation是边,Value是边上的权重。通过addPerson来增加点,addRelation来增加边,用于维护图。

心得体会

这一单元的作业感觉友好多了,虽然出现了两次恭喜你,强测得到0分,但是,通过对bug的修复,可以对JML的认识更加深刻,同时也学会了一些优化复杂度的方法。同时,我也考虑到,在这一次的作业中,MyNework过于脓肿,或许可以重新构造一个类,来优化架构。

posted @ 2021-05-28 00:29  czlyt  阅读(58)  评论(0编辑  收藏  举报