BUAA_OO_2022 第三单元总结
一、单元目标
模拟实现一个社交网络系统,实现此社交网络成员、组、消息、成员关系的管理、查询和处理;熟悉JML规格化语言;巩固图论知识,如最短路径、最小生成树;了解JAVA异常处理机制
二、第九次作业
2.1 难点
query_circle: 判断成员之间是否连通
query_block_sum: 判断连通块块数
2.2 架构设计
总体架构:按照官方包中的架构,实现MyNetwork、MyGroup、MyPerson、Main、以及异常类
数据的存放和管理:JML规格中并没有规定数据容器的类型,因此我选择了便于查询的HashMap,以PersonID为索引保存people,groups,acquaintance和value
自定义异常类:在每个自定义异常类中加入静态属性ID_CNT实现对每个ID触发异常的次数进行计数,用HashMap实现
并查集:本次作业要查询节点之间的连通性,且不涉及删边操作,适合使用并查集。在合并两棵树时,对树的深度进行比较,优化合并,使树的深度不超过log n,可以降低查找树顶的时间复杂度。为了以O(1)复杂度响应qbs指令,我将每个连通块对应的顶部节点存在数组rootNodes中,rootNodes的长度就等于连通块块数,并在增加节点和树合并时对数组进行维护
2.3 性能BUG
无性能bug。
三、第十次作业
3.1 难点
query_least_connection:查询最小生成树的边权和
3.2 架构设计
总体架构:额外增加了Edge类,并使用edges数组保存所有边的端点、权值
最小生成树:并查集加Kruscal算法。Kruscal算法需要每次取权值最小的边,为了提高速度,edges数组必须保持有序,按照权值低到高的顺序排列,因此在增加边时要根据权值插入到适当的位置。使用并查集是为了在Kruscal算法中快速判断边的端点是否分属两个不同的连通块,类似第九次作业的query_circle
3.3 性能BUG
query_group_value_sum指令出现性能bug。原因是每次都要遍历group中的所有成员取其value,再进行计算。改为在group对象中维护一个value_sum值,bug消失
四、第十一次作业
4.1 难点
delete_code_emoji: 根据热度阈值对emoji进行筛选、删除
send_indirect_message: 查询两个节点之间的最短路径长度
4.2 架构设计
总体架构:只增加了指导书中要求增加的类
dce指令:维护了一个热度有序的emoji数组HeatList,从头开始遍历删除emoji,直到emoji热度不小于阈值即可
最短路径:实现了Dijkstra算法
4.3 性能BUG
sim指令出现超时情况,原因是Dijkstra算法复杂度太高,应该使用堆优化的Dijkstra算法,将边加入小顶堆,按照端点与当前点集间距离进行比较,降低寻找下一个节点的时间复杂度
五、自测
本来考虑使用JMLUnitNG结合jml规格代码进行自动测试,但是JMLUnitNG生成的样例太特殊,覆盖率也不高,改为使用JUnit自行编写测试数据。首先在requires条件范围内构造随机数据和特殊数据,对应结果进行ensures条件验证。其中简单的ensures条件可以直接转化为assert语句,复杂的ensures条件如:保证最小生成树的边权和最小,则需要自己根据对规格的理解另行编程验证
六、Network扩展
6.1 扩展需求
假设出现了几种不同的Person
Advertiser:持续向外发送产品广告
Producer:产品生产商,通过Advertiser来销售产品
Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
Person:吃瓜群众,不发广告,不买东西,不卖东西
如此Network可以支持市场营销,并能查询某种商品的销售额和销售路径等 请讨论如何对Network扩展,给出相关接口方法,并选择3个核心业务功能的接口方法撰写JML规格(借鉴所总结的JML规格模式)
6.2 扩展实现
新增类:Advertiser、Producer、Customer继承自Person;Advertisement继承自Message
// 发送广告 /*@ public normal_behavior @ requires contains(productId) && containsMessage(advertisementId); @ assignable advertisements; @ ensures advertisements.length == \old(advertisements.length) + 1; @ ensures (\forall int i; 0 <= i && i < \old(advertisements.length); @ (\exists int j; 0 <= j && j < advertisements.length; advertisements[j] == (\old(advertisements[i])))); @ ensures (\exists int i; 0 <= i && i < advertisements.length; advertisements[i].getId() == productId && advertisements[i].getAdvertiserId == advertiserId); @ also @ public exceptional_behavior @ signals (MessageIdNotFoundException e) !containsMessage(productId);
@ signals (PersonIdNotFoundException e) !contains(advertiserId);
@*/
public void sendAdvertisement(int advertiserId, int productId) throws PersonIdNotFoundException, MessageIdNotFoundException;
// 购买商品
/*@ public normal_behavior
@ requires contains(personId);
@ requires containsProduct(productId);
@ requires containsProducer(producerId);
@ ensures getPerson(personId).money = \old(getPerson(personId).money) - getProduct(productId).getValue();
@ ensures getProducer(producerId).getProduct(productId).getGoodsNum() = \old(getSaler(salerId).getProduct(productId).getGoodsNum()) - 1;
@*/
public /*@ pure @*/void purchaseProduct(int personId, int productId, int producerId);
// 查询商品价值
/*@ public normal_behavior
@ requires containsProduct(productId);
@ ensures (\exists int i; 0 <= i && i < product.length;
@ product[i].getId() == productId && \result == product[i].getValue());
@ also
@ public exceptional_behavior
@ signals (ProductNotFoundException e) !containsMessage(productId);
@*/
int getProductValue(int productId) throws MessageIdNotFoundException
七、心得体会
收获:1. 掌握了JML规格的基本读写能力,理解了规格在合作开发中的意义,JML规格消除了自然语言的多义性,使用JML可以降低交流成本,保证规格提供者与具体实现者对类或方法的理解高度一致。2. 了解了自定义异常类以及JAVA异常处理机制,通过自己对异常类的实现和官方包RUNNER类源码的阅读,知道了异常是由被调用的下层方法抛出,上层方法捕获或继续抛出,最后被用户处理或被JVM捕获
小反思:缺乏对指令数、程序运行时间、算法复杂度进行评估的能力,不知道题目所能容忍的时间复杂度是哪个级别,导致出现性能bug
posted on 2022-06-06 15:49 huaimaomao-Official 阅读(31) 评论(0) 编辑 收藏 举报