• 博客园Logo
  • 首页
  • 新闻
  • 博问
  • 会员
  • 闪存
  • 班级
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 简洁模式 ... 退出登录
    注册 登录

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

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  阅读(25)  评论(0)  编辑  收藏  举报

弹尽粮绝,会员救园:会员上线,命悬一线
刷新页面返回顶部
 
Powered by:
博客园
Copyright © 2023 huaimaomao-Official
Powered by .NET 7.0 on Kubernetes