buaa_oo_第三单元总结
如何根据JML规格构造自测数据:
本单元自测环节我没有使用Junit工具,我采用的是最笨的方法--枚举每个指令的执行效果构造不同数据。首先我找到每条指令执行时会调用哪些函数,然后阅读JML规格中这些函数的执行效果,为各个执行方向构造不同的数据。函数的异常情况相对简单,且各个函数抛出异常情况有相似,主要的时间花在了验证如图的最短路径,优化性能时采用的缓冲保存值的算法正确性上。有关图的算法数据构造我是先纸上设计好相应的图,再转换成输入的文字信息,缓冲值法则是用EXCEL的公式等和程序进行对拍。构造过程还会把不同指令的边界易错情况捏合在一次测试中进行检验。由于本单元只要函数实现符合JML规格就能得分,我在自测方面确实是耐着性子每条指令反复构造各种情况,期间也发现了很多程序上的bug,最终强测都顺利通过。
架构设计和图模型构建和维护策略:
第一次作业相对简单,我只实现了要求的MyPerson,MyGroup,MyNetwork类,第二,三次作业中加入了路径path类来处理最短路径问题和最小生成树问题。本次作业中使用最多的容器就是HashMap,在Person中保存熟人及value,Group中保存人,Network中保存人,群,信息等,将要求的指令内容拆分,有关数据维护内容在Person和Group中具体实现,在Network中只需考虑指令逻辑进行调用。有关图模型构建及维护,在第一次作业中试着用并查集的方法来解决连通块问题。分析指令会对图造成什么影响,如addPerson和addToGroup和addRelation这种改变图结构的指令,在对应函数的书写时就要进行图的维护,改变一些值和结构等。第二,三次作业采用了Path和队列来解决最小生成树和最短路径问题,设立新的路径Path即图中的“边”来参与图模型构建。
代码实现出现的性能问题和修复情况
第一次作业最容易出现性能问题的应该是query circle ,query block sum等。我采用的是并查集算法,首先构造一个HashMap<Integer, HashSet<Integer>>circles和HashMap<Integer, Integer> parentMap,circles是这个人和所有和他能达到的人,parentMap则指向这个人的父节点,在addPerson中向circles中加入这个人映射这个人,在addRelation中如果二者有着不同的祖先结点,则合并二者的circles,由祖先ID小的进行继承,在circles中删除祖先Id大结点的映射。最终得到的结果是circles中为图的全部连通块,在查询是否相连时只需要查询二者是否都在某一个Set中,查询连通块个数就是circles的大小。
第二次作业的主要问题是如何解决查询值时如果每次都重新运算,复杂度将是n方的问题,我采用的是缓冲储存法,每个群组中记录维护年龄和,年龄的平方再加和的结果在每次addToGroup,delFromGroup时修改群组中年龄和,年龄平方的和,每次查询时只需除以总人数得到平均值,再使用方差的化简公式通过平均年龄,年龄和,年龄平方的和计算得到对应结果即可,复杂度为O(1).
第三次作业和第二次昨业中最短路径和最小生成树问题也可能造成性能问题,我采用的是堆优化的迪杰拉斯特拉方法,在java中为PriorityQueue,在设计好如何进行大小比较后,自动维护队列,非常方便。
通过上述实现,本次作业在强测中并没有出现性能导致的RE或TLE问题。
Network扩展
Network类增加addAdvertiseMessege,addPurchaseMessege,queryWantPurchase等,Producer向Advertiser寄信息表达打广告的需求和产品信息,Advertiser向所有Person类或某个群体中的Person寄去AdvertiseMessege,Customer通过方法添加改变感兴趣的产品,通过某指令使其想去购买,其在其信箱内检索第一条或某一条感兴趣的广告信息,向Advertiser寄去PurchaseMessege和钱,再由advertiser寄给producer。
/*@ public normal_behavior
@ requires containsMessage(id) && getMessage(id).getType() == 0 &&
@ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
@ getMessage(id).getPerson1() != getMessage(id).getPerson2()&&
getMessage(id).getPerson1() instance of producer &&
getMessage(id).getPerson2() instance of advertiser &&
getMessage(id)) instanceof AdvertiseMessageByProducer;
@ assignable messages;
@ assignable getMessage(id).getPerson1().socialValue, getMessage(id).getPerson1().money;
@ assignable getMessage(id).getPerson2().messages, getMessage(id).getPerson2().socialValue, getMessage(id).getPerson2().money;
@ 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().getSocialValue() ==
@ \old(getMessage(id).getPerson1().getSocialValue()) + \old(getMessage(id)).getSocialValue() &&
@ \old(getMessage(id)).getPerson2().getSocialValue() ==
@ \old(getMessage(id).getPerson2().getSocialValue()) + \old(getMessage(id)).getSocialValue();
@ ensures
@ (\old(getMessage(id)).getPerson1().getMoney() ==
@ \old(getMessage(id).getPerson1().getMoney()) - ((AdvertiseMessageByProducer)\old(getMessage(id))).getMoney() &&
@ \old(getMessage(id)).getPerson2().getMoney() ==
@ \old(getMessage(id).getPerson2().getMoney()) + ((AdvertiseMessageByProducer)\old(getMessage(id))).getMoney());
@ ensures (\forall int i; 0 <= i && i < \old(getMessage(id).getPerson2().getMessages().size());
@ \old(getMessage(id)).getPerson2().getMessages().get(i+1) == \old(getMessage(id).getPerson2().getMessages().get(i)));
@ ensures \old(getMessage(id)).getPerson2().getMessages().get(0).equals(\old(getMessage(id)));
@ ensures \old(getMessage(id)).getPerson2().getMessages().size() == \old(getMessage(id).getPerson2().getMessages().size()) + 1;
@ also
@ public exceptional_behavior
@ signals (MessageIdNotFoundException e) !containsMessage(id);
@ signals (MessageTypeWrongException e) containsMessage(id) && !getMessage(id) instanceof AdvertiseMessageByProducer;
@ signals (PersonTypeWrongException e) containsMessage(id) && getMessage(id) instanceof AdvertiseMessageByProducer&&
!(getMessage(id).getPerson1() instanceof Producer || getMessage(id).getPerson2() instanceof Advertiser;
@ signals (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
&&(getMessage(id).getPerson1() instanceof Producer || getMessage(id).getPerson2() instanceof Advertiser
&&getMessage(id) instanceof AdvertiseMessageByProducer;
@ !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()));
*/
public void sendAdvertiseMessageByProducer(int id) throws MessageTypeWrongException , PersonTypeWrongException
RelationNotFoundException, MessageIdNotFoundException;
/*@ public normal_behavior
@ requires containsMessage(id) && getMessage(id).getType() == 1 &&
@ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
@ getMessage(id).getPerson1() != getMessage(id).getPerson2()&&
getMessage(id).getPerson1() instance of Advertiser ;
@ assignable messages;
@ assignable getMessage(id).getPerson1().socialValue;
@ assignable getMessage(id).getPerson2().messages;
@ 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().getSocialValue() ==
@ \old(getMessage(id).getPerson1().getSocialValue()) + \old(getMessage(id)).getSocialValue() ;
@ ensures \old(getMessage(id)).getPerson1().getSocialValue() ==
@ \old(getMessage(id).getPerson1().getSocialValue()) + \old(getMessage(id)).getSocialValue() ;
@ ensures (\forall person i; getMessage(id).getGroup.hasPerson(i);
@ (\forall int j; 0 <= j && j < \old(i.getMessages().size());
@ && i.getMessages().get(j+1) ==\old(i.getMessages().get(j)))&&
@ i.getMessages().size() == \old(i.getMessages().size()) + 1);
@ also
@ public exceptional_behavior
@ signals (MessageIdNotFoundException e) !containsMessage(id);
@ signals (MessageTypeWrongException e) containsMessage(id) && !getMessage(id) instanceof AdvertiseMessageByAdvertiser;
@ signals (PersonTypeWrongException e) containsMessage(id) && getMessage(id) instanceof AdvertiseMessageByAdvertiser&&
!(getMessage(id).getPerson1() instanceof Advertiser;
@ signals (PersonIdNotFoundException e) containsMessage(id) &&
getMessage(id).getPerson1() instanceof Advertiser&&getMessage(id) instanceof AdvertiseMessageByAdvertiser&&
!(getMessage(id).getGroup().hasPerson(getMessage(id).getPerson1()));
*/
public void sendAdvertiseMessageByAdvertiser(int id)throws MessageTypeWrongException , PersonTypeWrongException
MessageIdNotFoundException, PersonIdNotFoundException;
/*@ public normal_behavior
@ requires containsMessage(id) && getMessage(id).getType() == 0 &&
@ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
@ getMessage(id).getPerson1() != getMessage(id).getPerson2()&&
getMessage(id).getPerson1() instance of Customer &&
getMessage(id).getPerson2() instance of advertiser &&
getMessage(id)) instanceof PurchaseMessageByCustomer;
@ assignable messages;
@ assignable getMessage(id).getPerson1().socialValue, getMessage(id).getPerson1().money;
@ assignable getMessage(id).getPerson2().messages, getMessage(id).getPerson2().socialValue, getMessage(id).getPerson2().money;
@ 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().getSocialValue() ==
@ \old(getMessage(id).getPerson1().getSocialValue()) + \old(getMessage(id)).getSocialValue() &&
@ \old(getMessage(id)).getPerson2().getSocialValue() ==
@ \old(getMessage(id).getPerson2().getSocialValue()) + \old(getMessage(id)).getSocialValue();
@ ensures
@ (\old(getMessage(id)).getPerson1().getMoney() ==
@ \old(getMessage(id).getPerson1().getMoney()) - ((AdvertiseMessageByProducer)\old(getMessage(id))).getMoney() &&
@ \old(getMessage(id)).getPerson2().getMoney() ==
@ \old(getMessage(id).getPerson2().getMoney()) + ((AdvertiseMessageByProducer)\old(getMessage(id))).getMoney());
@ ensures (\forall int i; 0 <= i && i < \old(getMessage(id).getPerson2().getMessages().size());
@ \old(getMessage(id)).getPerson2().getMessages().get(i+1) == \old(getMessage(id).getPerson2().getMessages().get(i)));
@ ensures \old(getMessage(id)).getPerson2().getMessages().get(0).equals(\old(getMessage(id)));
@ ensures \old(getMessage(id)).getPerson2().getMessages().size() == \old(getMessage(id).getPerson2().getMessages().size()) + 1;
@ also
@ public exceptional_behavior
@ signals (MessageIdNotFoundException e) !containsMessage(id);
@ signals (MessageTypeWrongException e) containsMessage(id) && !getMessage(id) instanceof PurchaseMessageByCustomer;
@ signals (PersonTypeWrongException e) containsMessage(id) && getMessage(id) instanceof PurchaseMessageByCustomer&&
!(getMessage(id).getPerson1() instanceof Customer || getMessage(id).getPerson2() instanceof Advertiser2;
@ signals (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
&&(getMessage(id).getPerson1() instanceof Customer || getMessage(id).getPerson2() instanceof Advertiser
&&getMessage(id) instanceof PurchaseMessageByCustomer;
@ !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()));
public void sendAdvertiseMessageByProducer(int id) throws MessageTypeWrongException , PersonTypeWrongException
RelationNotFoundException, MessageIdNotFoundException;
本单元学习体会
本单元JML的学习让我体会到了代码规范化的好处,用生活中自然语言不方便表达而程序语言又比较不便的情况下二者相互融合得到这种新的JML语言。一开始阅读JML语言时常常感到疑惑不解,觉得有些内容明明用别的方法能表达的更加清楚,而JML语言却非常的冗长不好阅读,出现的一些新的JML用语也不太熟悉,偶有混淆,但我还是慢慢尝试去理解,碰到不会的查询手册,继而在实验课上自己书写使用,慢慢地自己能够看懂更多内容,在第二次作业最小生成树JML规格阅读时,一开始并没有看懂具体要实现什么,听同学说可能是最小生成树,但我仍然决定要自己进行阅读,通过三四的反复阅读尝试,将关键段落黏贴至别的编辑器中,将关键的括号对齐。并用空行等换一种格式让其更清晰,终于弄懂了函数的意思,当时十分的喜悦。在本单元作业的完成上,由于有JML规格的指导,坚信只要按规则完成就一定能正确,心里踏实了许多。回想一下JML规格看似繁琐,其实严谨,通过各种情况的划分考虑了不同条件下函数的执行逻辑,能够做到代码的实现在约定条件下的高正确率实现,按照正确的规格写出的代码,其功能一定是正确的,也可以按照规格自动化的构造大量测试数据,在实际项目中将发挥重要作用。