「BUAA OO」Unit3
第三单元的任务是通过理解JML规格在面向对象设计与构造中的重要意义,并掌握利用JML规规格,实现一个社交网络图,并进一步实现最小生成树和最小路径算法。
一、利用JML规格准备测试数据
1. 数据准备
根据功能行为的不同,我准备测试的数据分为normal_behavior类的正常数据和导致异常行为exceptional_behavior的数据。对于正常数据,具体可以根据指导书的输入要求来进行数据的生成;对于异常数据,则根据官方包的JML源代码的要求进行初始化。
2. 使用Junit对代码进行测试
本单元,通过初步学习了解Junit的使用,使用其对代码进行了单元测试,对于作业中的算法相关指令bug的发现发挥了很大的作用。
二、架构设计
本单元我们是按照JML规格来实现接口,每次代码是在前一次的基础上逐渐增加,架构大体相同,故在此展示的是第三次作业中实现的类和彼此间的联系:
三、性能问题和修复情况
1. 性能问题
第一次作业的qbs
操作(query_block_sum)相对其他操作比较耗时。如果通过二重循环和qci
操作(query_circle),且qci操作通过广度/深度优先搜索实现,会导致较高的时间复杂度。故我们可以使用并查集提高性能。
第二次作业中需要我们实现最小生成树。对于一个连通图GG,其生成树是一个极小的连通子图,它需要包括图中全部的nn个顶点以及构成一棵树的n-1n−1条边。而在一个带权图中,我们称图中边权值和最小的生成树为最小生成树。可采用的算法为Prim算法和Kruskal算法来提高性能。
第三次作业需要实现最短路径,我们可以使用基于堆优化的 dijsktra
算法进行性能的优化。
2. bug分析
本单元由于已经有了官方给出的JML,相比前两个单元,思路上并不容易出错,只需要根据要求进行代码的编写即可。同时,我们可以使用Junit工具进行单元测试。
四、扩展Network
Network新增四个方法:addAdvertiseMessage,sendAdvertiseMessage,addSaleMessage,sendSaleMessage。
/*@ public normal_behavior @ requires !(\exists int i; 0 <= i && i < messages.length; messages[i].equals(message)) && @ (message.getType() == 0) ==> (message.getPerson1() != message.getPerson2()) && @ (message instanceof AdvertiseMessage); @ assignable messages; @ ensures messages.length == \old(messages.length) + 1; @ ensures (\forall int i; 0 <= i && i < \old(messages.length); @ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i])))); @ ensures (\exists int i; 0 <= i && i < messages.length; messages[i].equals(message)); @ also @ public normal_behavior @ requires !(\exists int i; 0 <= i && i < messages.length; messages[i].equals(message)) && @ (message instanceof EmojiMessage) ==> containsEmojiId(((EmojiMessage) message).getEmojiId()) && @ (message.getType() == 0) ==> (message.getPerson1() != message.getPerson2()) && @ !(message instanceof AdvertiseMessage); @ assignable nothing; @ also @ public exceptional_behavior @ signals (EqualMessageIdException e) (\exists int i; 0 <= i && i < messages.length; @ messages[i].equals(message)); @*/ public void addAdvertiseMessage(Message message) throws EqualMessageIdException; /*@ public normal_behavior @ requires !(\exists int i; 0 <= i && i < messages.length; messages[i].equals(message)) && @ (message.getType() == 0) ==> (message.getPerson1() != message.getPerson2()) && @ (message instanceof SaleMessage); @ assignable messages; @ ensures messages.length == \old(messages.length) + 1; @ ensures (\forall int i; 0 <= i && i < \old(messages.length); @ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i])))); @ ensures (\exists int i; 0 <= i && i < messages.length; messages[i].equals(message)); @ also @ public normal_behavior @ requires !(\exists int i; 0 <= i && i < messages.length; messages[i].equals(message)) && @ (message instanceof EmojiMessage) ==> containsEmojiId(((EmojiMessage) message).getEmojiId()) && @ (message.getType() == 0) ==> (message.getPerson1() != message.getPerson2()) && @ !(message instanceof SaleMessage); @ assignable nothing; @ also @ public exceptional_behavior @ signals (EqualMessageIdException e) (\exists int i; 0 <= i && i < messages.length; @ messages[i].equals(message)); @*/ public void addSaleMessage(Message message) throws EqualMessageIdException; /*@ public normal_behavior @ requires containsMessage(id) && getMessage(id).getType() == 2 && @ getMessage(id).getPerson1() != getMessage(id).getPerson2() &&
@ getMessage(id).getPerson1() instanceof Advertiser &&
@ getMessage(id).getPerson2() instanceof Customer && @ ((Customer) getMessage(id).getPerson1()).getPreference == prefertype; @ assignable messages; @ 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 (\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); @*/ public void sendAdvertiseMessage(int id) throws MessageIdNotFoundException; /*@ public normal_behavior @ requires containsMessage(id) && getMessage(id).getType() == 3 && @ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
@ getMessage(id).getPerson1() instanceof Advertiser &&
@ getMessage(id).getPerson2() instanceof Producer && @ getMessage(id).getPerson1() != getMessage(id).getPerson2(); @ assignable messages; @ 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 (\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 (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 3 && @ !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2())); @*/ public void sendSaleMessage(int id) throws RelationNotFoundException, MessageIdNotFoundException;
五、本单元学习体会
第三单元主要是按照规格实现接口,认真阅读JML十分重要,不能放过任何一个细节,另外,还要要结合课程组给出的数据范围以及标程复杂度从全局出发考量方法实现的复杂度。通过本单元的学习,我对于OO的理解更加深入了。