2020级北航OO第三单元作业总结
一、如何自测
这个单元提出了一种Junit的测试方法,但是后面发现测试数据仍旧需要自己构造,于是作罢。在这个单元的自测主要是针对一些jml描述中的基本功能以及异常行为进行构造数据,测试程序的正确性。构造数据的思路主要是先测基本功能的正确性与异常情况,然后通过增加复杂度,并且根据自身的代码分析最耗时的查询指令,构造可能会产生TLE的测试数据。除了自己构造数据以外,也可以和同学对拍。讨论区也有大佬写数据生成器,不妨试试。
由于在照着Jml写代码的时候有很多细节需要考虑,不妨在觉得有坑点的地方及时做好标记,有助于之后数据的构造。
二、图模型构建与维护策略
在MyPerson类中,相当于是存储节点信息,为了存储人与人之间的value,就用了一个accHashMap<Person,Integer>用来对应认识的人与对应的value权值。
在MyGroup类中,相当于是对结点进行划分,并记录这个划分的信息,同样也用了一个HashMap<Integer,Person>用来存储人。
在MyNetWork类中,相当于是对人、划分、信息等构建的一个网络,同样用多个HashMap用来分别存储人、group、messages等。
在存储好了上述基本的数据之后,需要考虑的主要有一下几点:
- isCircle判断方法:用一个HashMap用来存储每个点的最远端,每次加入一个点,就采用递归的方法求出这个点的最远端。此处我用的是neMap存储每个点的最远端。值得注意的是,在每次addRelation的时候,不要忘记更新最远端。因此只要两个点的最远端相同,说明他们是连通的,在同一个连通子图中。
- 最小生成树算法:在第二次作业中要运用到最小生成树算法,因此我又新建了两个工具类,如下图所示:
- 最短路径算法:在第三次作业中要运用到最短路径Dijkstra算法,因此我新建了一个工具类,如下图所示:
三、性能问题和修复Bug
在第二次强测与互测中出现的bug我以为是我queryBlockSum那块超时了,没想到,竟然是最小生成树那块,我判断两个数相等的时候用的Integer,然后因为不等就重新put进了hashmap里面,导致后面的cpu超时,感觉还是自己没有好好测试的原因。在第三次作业中我用了一个二维数组,由于节点数量的限制,所以可以用一个固定的容器来用来存储距离矩阵,不过每次遍历都是超级耗时的。
对于queryBlockSum的查询,我的做法是维护一个blockSum,当每次addPerson的时候,blockSum++。在addRelation的时候,如果两个点本身就是连通的,则blockSum不变,否则减一。
四、新增NetWork相应规格
Advertiser、Customer以及Producer都可以继承Person类。具体实现的jml如下所示:
需要实现的接口如下:
public void sendMessage2Buy(int id) throws RelationNoeFoundException,MessageIdNotFoundException; public void sendAdvertisement(int id) throws MessageIdNotFoundException; public void makeProduct(int producerId, int productId) throws PersonIdNotFoundException;
需要实现的方法如下:
//Costomer
/*@ public normal_behavior @ requires containsMessage(id) && getMessage(id) instanceof BuyMessage && @ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) && @ getMessage(id).getPerson1() != getMessage(id).getPerson2(); @ assignable messages, BuyMessageList; @ assignable getMessage(id).getPerson1(), getMessage(id).getPerson2(); @ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 && @ (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id; @ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i])))); @ ensures \old(getMessage(id)).getPerson1().getBuyMoney() == @ \old(getMessage(id).getPerson1().getBuyMoney()) + \old(getMessage(id)).getMoney() && @ \old(getMessage(id)).getPerson2().getSocialValue() == @ \old(getMessage(id).getPerson2().getBuyMoney()) - \old(getMessage(id)).getMoney(); @ also @ public exceptional_behavior @ signals (MessageIdNotFoundException e) !containsMessage(id); @ signals (RelationNotFoundException e) containsMessage(id) && !(getMessage(id).getType() instanceof BuyMessage) && @ !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2())); @*/ public void sendMessage2Buy(int id) throws RelationNotFoundException, MessageIdNotFoundException;
//Advertise
/*@ public normal_behavior @ requires containsMessage(id) && getMessage(id) instanceof Advertisement; @ assignable messages; @ ensures (\forall int i; 0 <= i && i < people.length && getMessage(id).getPerson1().isLinked(people[i]) @ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 && @ (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id; @ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i])))); @ ensures (\forall int i; 0 <= i && i < people.length && !getMessage(id).getPerson1().isLinked(people[i]); @ people[i].getMessages().equals(\old(people[i].getMessages())); @ also @ public exceptional_behavior @ signals (MessageIdNotFoundException e) !containsMessage(id); @*/ public void sendAdvertisement(int id) throws MessageIdNotFoundException;
//Producer
/*@ public normal_behavior @ requires contains(producerId) && (getPerson(producerId) instanceof Producer); @ assignable getProducer(producerId); @ ensures getProducer(producerId).getProductNum(productId) == @ \old(getProducer(producerId).getProductNum(productId)) + 1; @ also @ public exceptional_behavior @ signals (PersonIdNotFoundException e) !contains(producerId); @*/ public void makeProduct(int producerId, int productId) throws PersonIdNotFoundException;
五、心得与体会
本单元由于一些细节没注意到,以及不太清楚要怎么有效地测试,自测方面有点无从下手,导致一些bug没有注意到。虽然说这单元的难度不大,课下还是要好好测一测。对于Jml,感觉还是要更细心才是,其次是性能方面,我觉得这单元考得更多的是如何有效地用一些容器来高效地查询与存储,比如采用多个hashmap实现多种映射关系,能够更快地获得想要的值。
如有错误请纠正!