OO_Unit3_Summary

OO第三单元总结

利用JML规格构造测试数据

首先,这一单元在写程序的时候,肯定是一步一个脚印,对程序作出修改时保证其遵循所给的JML。在对程序进行测试时,首先对照JML的assignable将方法中对容器做出修改的地方进行检查,保证不添不漏。在符合上述条件的前提下,只需对方法的返回值进行检查。在构造数据时,尽量全面地覆盖正常与异常情况下的数据,保证方法中每一种require的情况均有出现,以保证数据的全面性,另外对于测试中出现的不符合我们预期中的情况,我们应进行溯源处理,观察是哪个方法的前置条件得到满足但后置条件没能满足。结合JUnit测试,找到问题是相对容易的。

架构设计

本单元的作业主要是实现一个简单的社交关系网络。

首先有一个管理社交网络的类MyNetWork,其中存储了所有的人、群组和信息,MyMessage, MyPerson, MyGroup则用于管理信息,人,群组的具体内容。除了实现JML规格要求的方法外,为了便于维护和查找相关信息,我们在MyMessage, MyPerson, MyGroup中增加了对于需要查询的信息的计算和维护方法,并增加了相应的get方法以便在请求查找时能直接获取在相应类中进行维护的结果。此外,对于MyNetwork类中要求实现的高复杂度方法,将其从MyNetwork类中分离出去以工具类的静态方法实现,以均衡各个类的行数。

 

 

为了实现低复杂度的查找,除了对顺序有要求的Message的存储,本单元作业中的容器几乎全部使用HashMap。在图的构建上,先遍历需要查询的点集(MyPerson的集合),取得每个Person的acquaintance构建图的边(采用Pair的Arraylist储存),例如在queryLeastConnect方法中,现在方法中完成图的构建,再将图传入GetMin类的getmin方法以获得最小生成树的边权和,getMin方法采用朴素的Dijkstra算法,先对图中的边进行排序,再依次判断每一条边是否需要加入生成树中。这样的话,由于建图和查找的复杂度的复杂度均在On至On^2之间,总体复杂度还在可控范围之内。

出现的性能问题和修复情况

三次作业的JML中均有高复杂的方法,若按规格实现复杂度至少为On^2。因此,我们需要选择合适的方法降低其复杂度。

第一次作业

第一次作业出现的高复杂度方法分别为isCircle和queryBlockSum。

对于isCircle,一开始采取暴力维护的方式,创建HashSet存储所有与其有关联的人,每新加人和关系时,重新遍历所有HashSet并更新,这种做法需要使用addAll方法,实际上该方法本身已经有了On的复杂度,导致addRelation方法的复杂度达到了On^2。然而在第一次作业的强测和互测中,由于数据较弱,我全部侥幸通过。这也为后续作业埋下了隐患。在第二次作业的DDL前,我对这一存储方式,做了优化,采用类似并查集的方式,用一个Hashmap维护每个人的祖先,若调用isCIrcle时祖先一样则返回真。

HashMap<Integer, Integer> map; Person -> Ancestor

对于queryBlockSum的需求,我们在MyNetwork中动态维护一个变量block,当有新人加入社交网络,增加block,当addRelation时,判断两人之间isCircle是否为真,若为真则不需要修改block,反之block-1。

第二次作业

第二次作业的高复杂度方法为getValueSum和queryLeastConnection。

getValueSum方法易于实现,在每个Group里动态维护value变量,将查找方法的复杂度分散到别的方法中去,查找时直接获取即可,复杂度为O1。

关于queryLeastConnection的实现,已经在上文中提到了。

第三次作业

第三次作业的高复杂度方法为sendIndirectMessage,这需要我们找到两个人之间的最短路径。思路仍与queryLeastConnection一致,先建图,但不同的是,对图的遍历从边的遍历变成了对点的遍历,这也在无形中增高了复杂度,我用亲身经历证明这种做法会在强测中出现一个CTLE。解决方法是对Dijkstra算法进行堆优化。

对Network进行扩展

假设出现了几种不同的Person

  • Advertiser:持续向外发送产品广告

  • Producer:产品生产商,通过Advertiser来销售产品

  • Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息

  • Person:吃瓜群众,不发广告,不买东西,不卖东西

如此Network可以支持市场营销,并能查询某种商品的销售额和销售路径等 请讨论如何对Network扩展,给出相关接口方法,并选择3个核心业务功能的接口方法撰写JML规格(借鉴所总结的JML规格模式)

MyAdvertiser、MyProducer、MyCustomer三个类继承MyPerson类,分别实现sendAdvertisement、makeProduct/makeAdvertisement(区别于sendAdvertisement,该方法是Producer发送到Advertiser的信息,而sendAdvertisement是Advertiser发送给Customer)、buyProduct方法。另外新增MyBuyMessage, MyAdvertisementMessage(内含Product属性)类表示新增的信息类型,MyProduct类实现商品,具有money, productHeat等属性。另外,新增InvalidMessageTypeException表示消息不合法的异常。

核心业务功能的接口方法

sendAdvertisement

/*@ public normal_behavior
     @ requires containsMessage(id) && getMessage(id).getType() == 1 &&
     @         getMessage(id).getGroup().hasPerson(getMessage(id).getPerson1());
     @ assignable people[*].socialValue, 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 Person p; \old(getMessage(id)).getGroup().hasPerson(p); p.getSocialValue() ==
     @         \old(p.getSocialValue()) + \old(getMessage(id)).getSocialValue());
     @ ensures (\forall int i; 0 <= i && i < people.length && !\old(getMessage(id)).getGroup().hasPerson(people[i]);
     @         \old(people[i].getSocialValue()) == people[i].getSocialValue());
     @ also
     @ public exceptional_behavior
     @ signals (MessageIdNotFoundException e) !containsMessage(id);
     @ signals (PersonIdNotFoundException e) containsMessage(id) && getMessage(id).getType() == 1 &&
     @         !(getMessage(id).getGroup().hasPerson(getMessage(id).getPerson1()));
     @ signals (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0;
     @*/
public void sendAdvertisement(int id) throws
           MessageIdNotFoundException, PersonIdNotFoundException,InvalidMessageTypeException;
     

makeAdvertisement

/*@ public normal_behavior
     @ requires containsMessage(id) && getMessage(id).getType() == 0 &&
     @         getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
     @         getMessage(id).getPerson1() != getMessage(id).getPerson2();
     @ assignable messages, getMessage(id).getPerson2().messages;
     @ assignable getMessage(id).getPerson1().socialValue, getMessage(id).getPerson1().money;
     @ assignable 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()) - ((MakeAdvertisementMessage)\old(getMessage(id))).getMoney() &&
     @         \old(getMessage(id)).getPerson2().getMoney() ==
     @         \old(getMessage(id).getPerson2().getMoney()) + ((MakeAdvertisementMessage)\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 (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
     @         !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()));
     @ signals (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 1;
     @*/
public void makeAdvertisement(int id) throws
           RelationNotFoundException, MessageIdNotFoundException, InvalidMessageTypeException ;

buyProduct

/*@ public normal_behavior
 @ requires containsMessage(id) && contains(id1);
 @ assignable messages;
 @ assignable getMessage(id).getPerson1().socialValue, getMessage(id).getPerson1().money;
     @ assignable getMessage(id).getPerson2().socialValue, getMessage(id).getPerson2().money;
     @ assignable getMessage(id).getPerson2().products[],
     @ 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()) - ((MakeAdvertisementMessage)\old(getMessage(id))).getMoney() &&
     @         \old(getMessage(id)).getPerson2().getMoney() ==
     @         \old(getMessage(id).getPerson2().getMoney()) + ((MakeAdvertisementMessage)\old(getMessage(id))).getMoney());
     @ ensures (\forall int i; 0 <= i && i < \old(getMessage(id).getPerson2().getproducts().size());
     @         \old(getMessage(id)).getPerson2().getproducts().get(i+1) == \old(getMessage(id).getPerson2().getproducts().get(i)));
     @ ensures \old(getMessage(id)).getPerson2().getproducts().get(0).equals(\old(getMessage(id).getProduct()));
     @ ensures \old(getMessage(id)).getPerson2().getproducts().size() == \old(getMessage(id).getPerson2().getproducts().size()) + 1;
 @ also
     @ public exceptional_behavior
     @ signals (MessageIdNotFoundException e) !contains(mid);
     @ signals (PersonIdNotFoundException e) !containsProduct(id1) || !containsProduct(id2);
     @*/
public void buyProduct(int id1, int id2, int mid) throws
           PersonIdNotFoundException, MessageIdNotFoundException;

 

本单元学习总结

前两个单元的关键词,架构和协同,这一单元的关键词无疑是规格。尤其是在团队工作时,符合规格的代码是必要的。撰写规格虽然麻烦,但很好地避免了不同的人写的代码无法对接的问题。另外在对安全要求较高的行业中,规格也使代码的安全性有所保证,对开发工作是大有裨益的。

 
posted on 2022-06-06 14:11  shuo1337  阅读(11)  评论(1编辑  收藏  举报