BUAA_OO_UNIT3

BUAA_OO_UNIT3

1.测试数据的准备

本单元的测试数据准备时,需要满足各个指令的前置条件,方法是自动生成并与他人对拍,关键是指令要覆盖全面,尽可能多。本人在对拍过程中发现一些很诡异的bug,例如有些bug体现在复杂度较高的方法输出有异常,但方法的逻辑并未出现问题,而是由于本人误用==比较两个Integer的大小,导致出错,这在指令数较少的情况下无法测试出来。

2.架构设计

本单元作业的核心是实现了 一个多人聊天系统,主要的架构官方JML规格已给出,自己主要实现的就是Network类中几个关键的图算法。

容器选择

由于Person、Group等对象在Network中有互不相同的id,且大量方法是通过id对应到对象,因此容器均选择HashMap,保存id和对象的对应关系,节省查找时间。

private HashMap<Integer, Person> people;
private HashMap<Integer, Group> groups;
private HashMap<Integer, Message> messages;

图模型构建

三次作业中,主要是构建了Person之间的关系图,涉及的复杂度较高的方法有:

boolean isCircle(int id1, int id2):判断id1与id2之间是否可达。

int queryBlockSum():本质是查询图中最大连通分量的个数

int queryLeastConnection(int id): 本质是求出图中所有与id可达的人组成的最小生成树,并返回生成树的边权值之和。

int sendIndirectMessage(int id): 发送消息,并返回发送和接受消息双方的最短路径长度。

在第一次作业中,通过并查集简化queryBlockSumisCircle方法的复杂度,在NetWork中新建两个属性:blocksdepths,类型均为HashMap<Integer, Integer>,分别记录person节点的父子关系和深度。在addPersonaddRelation时对二者进行修改更新。

第二次作业中,利用kruskal算法实现最小生成树的查询。通过TreeSet<MyEdge>保存Person关系图的边,MyEdge保存两个Person节点以及他们之间的value。选择TreeSet保存边是为了缩短找最小边的时间。

第三次作业中,为实现最短路径算法,采用迪杰斯特拉算法+堆优化,通过java提供的PriorityQueue实现堆优化。

异常处理

由于多个异常类的本质都是计数,为了实现代码复用,通过Counter类作为异常类的静态属性,保存异常计数。

private static Counter counter = new Counter();

3.bug分析

三次作业在强测和互测中都没有任何bug,由于设计之初就考虑了性能问题,因此没有出现TLE等情况。

4.Network拓展

题目要求

假设出现了几种不同的Person

  • Advertiser:持续向外发送产品广告
  • Producer:产品生产商,通过Advertiser来销售产品
  • Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
  • Person:吃瓜群众,不发广告,不买东西,不卖东西

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


NetWork中存在四种Person实例,

其中Advertiser可以发送广告给所有Customer。

Producer与特定Advertiser关联,销售给消费者产品,并有查询销售额方法

Customer内有一个关注的广告列表,保存其关注的广告。购买产品时给Advertiser发消息,通知对应的Producer。

下面选择发送广告、查询销量、

发送广告:Advertiser将广告发送给所有Customer

/*@ public normal_behavoir
  @ requires containsAdvertisement(id);
  @ assignable advertisements;
  @ assignable customer[*].subscribledList;
  @ ensures !containsAdvertisement(id) && 
  @         advertisements.length == \old(advertisements.length) - 1 && 
  @         (\forall int i; 0 <= i && i < \old(advertisements.length) && \old(advertisements[i].getId()) != id;
  @         (\exists int j; 0 <= j && j < advertisements.length; advertisements[j].equals(\old(advertisements[i]))));
  @ ensures (\forall int i; 0 <= i && i < customers.length; customers[i].subscribledList.length == \old(customers[i]).subscribledList.length + 1);
  @ ensures (\forall int i; 0 <= i && i < customers.length; 
  @			(\exists int j; 0 <= j && j < customers[i].subscribledList.length;
  @			customers[i].subscribledList[i].getId() == id));
  @ ensures (\forall int i; 0 <= i && i < customers.length; 
  @			(\forall int j; 0 <= j && j < \old(customers[i]).subscribledList.length;
  @			(\exists int k; 0 <= k && k < customers[i].subscribledList.length;
  @			customers[i].subscribledList[k] == \old(customers[i].subscribledList[j]))));
  @*/
public void sendAdvertisement(int id);

查询销量:查询producer的销量

/*@ public normal_behavoir
  @ requires producers.contains(producerId);
  @ assignable \nothing;
  @ ensures \result == producers[producerId].getSaleValue;
  @*/
public int querySaleValue(int producerId);

发送购买产品消息:customer经由advertiser给producer发送购买消息,producer将customer的id保存在purchaseInfo中。

/*@ public normal_behavoir
  @ requires advertisers.contains(advertiserId);
  @ requires consumers.contains(id);
  @ assignable advertisers[advertiserId].getProducer().purchaseInfo;
  @ ensures advertisers[advertiserId].getProducer().purchaseInfo.length == \old(advertisers[advertiserId].getProducer().purchaseInfo).length + 1;
  @ ensures (\forall int i; 0 <= i && i < \old(advertisers[advertiserId].getProducer().purchaseInfo).length;
  @			(\exists int j; 0 <= j && j < advertisers[advertiserId].getProducer().purchaseInfo.length;
  @			advertisers[advertiserId].getProducer().purchaseInfo[j] == \old(advertisers[advertiserId].getProducer().purchaseInfo)[i]);
  @ ensures (\exists int i; 0 <= i && i <= advertisers[advertiserId].getProducer().purchaseInfo.length;
  @			advertisers[advertiserId].getProducer().purchaseInfo[i] == id);
  @*/
public void purchase(int id, int advertiserId, int productId);

5.学习体会

本单元作业主要是通过阅读jml规格完成作业,用到的算法也是大一数据结构学过的有关图论算法。难度不是很大,但也培养了按规格写出优性能代码的能力,同时复习了图论重要的算法。

posted @ 2022-06-01 19:31  KouweiLee  阅读(57)  评论(0编辑  收藏  举报