热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

BUAA_2022_OO_Unit3总结

2022_OO第三单元总结一、架构分析1.hw9基础架构和基本功能的搭建  hw9作为第三单元的第一次作业,基本上还是带我们熟悉jml的基础语法,搭建好整体的框架,实现一些基础功能

2022_OO第三单元总结

一、架构分析


1.hw9 基础架构和基本功能的搭建

  hw9作为第三单元的第一次作业,基本上还是带我们熟悉jml的基础语法,搭建好整体的框架,实现一些基础功能,并在此基础上进行一些简单的优化。


基础架构

  hw9中每个类的结构大致如下:

//MyNetwork
public class MyNetwork implements Network {
private HashMap people;
private HashMap groups;
private DisjointSetUnion dsu;
public MyNetwork() {
people = new HashMap<>();
groups = new HashMap<>();
dsu = new DisjointSetUnion();
}
...
}
//MyGroup
public class MyGroup implements Group {
private int id;
private ArrayList

people;
public MyGroup(int id) {
this.id = id;
this.people = new ArrayList<>();
}
...
}
//MyPerson
public class MyPerson implements Person {
private int id;
private String name;
private int age;
private ArrayList

acquaintance;
private ArrayList value;
public MyPerson(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
this.acquaintance = new ArrayList<>();
this.value = new ArrayList<>();
}
...
}

性能优化

  hw9中出现了 query_circle 和 query_block_sum 两种指令。query_circle 就是询问两个点是否联通。query_block_sum 是询问图里的连通块数量。考虑到没有删改边的指令,我选择了实现并实时维护一个并查集。

  这样在接收到 query_circle 命令时,直接调用并查集,判断两个person的父节点是否相同即可;而且维护并查集时也能顺便解决 query_block_sum 命令,只要在并查集中存储一个int类型的值sum,初始为0,每加入一个节点sum就+1,每合并一个节点sum就-1,这样该命令的时间成本就均摊在维护并查集的过程中,相比jml给的基本策略,只需要O1就能获取sum值,大大提高了效率。

  当然并查集中也有相应的优化,比如路径压缩和按秩合并等等,我使用的是按两边节点个数合并,原理与按秩合并大致相同。


2.hw10 加入message并实现进阶功能

  hw10沿用了hw9搭建的架构,增加了MyMessage类,实现了与message相关的一些更高级的功能。


基础架构

  hw10中每个类的结构大致如下:

//MyNetwork
public class MyNetwork implements Network {
private HashMap people;
private HashMap groups;
private HashMap messages;
private MyDisjointSetUnion dsu;
private ArrayList values;
public MyNetwork() {
people = new HashMap<>();
groups = new HashMap<>();
messages = new HashMap<>();
dsu = new MyDisjointSetUnion();
values = new ArrayList<>();
}
...
}
//MyGroup
public class MyGroup implements Group {
private int id;
private HashMap people;
public MyGroup(int id) {
this.id = id;
this.people = new HashMap<>();
}
...
}
//MyPerson
public class MyPerson implements Person {
private int id;
private String name;
private int age;
private int money;
private int socialValue;
private ArrayList

acquaintance;
private ArrayList value;
private ArrayList messages;
public MyPerson(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
mOney= 0;
socialValue = 0;
this.acquaintance = new ArrayList<>();
this.value = new ArrayList<>();
this.messages = new ArrayList<>();
}
...
}
//MyMessage
public class MyMessage implements Message {
private int id;
private int socialValue;
private int type;
private Person person1;
private Person person2;
private Group group;
public MyMessage(int messageId, int messageSocialValue,
Person messagePerson1, Person messagePerson2) {
type = 0;
group = null;
id = messageId;
socialValue = messageSocialValue;
person1 = messagePerson1;
person2 = messagePerson2;
}
public MyMessage(int messageId, int messageSocialValue,
Person messagePerson1, Group messageGroup) {
type = 1;
person2 = null;
id = messageId;
socialValue = messageSocialValue;
person1 = messagePerson1;
group = messageGroup;
}
...
}

  可以看出,类的变化大部分是围绕message相关。


性能优化

  hw10中出现了 query_least_connection 指令。这个指令的含义是询问图中包含某个点的最小生成树的边权和。

  基于hw9中已经实现了并查集,并且没有负数边,所以这里可以直接使用kruskal生成最小生成树。由于指令可以随时加入新的边,如果要动态维护最小生成树的话,则需要每加一个边就重新生成一个树,时间成本比较高,所以这里我选择了现用现生成的策略,接收到 query_least_connection 指令后才生成树,进行最小边的计算。


3.hw11 message相关子类高级功能的实现

  hw11引入了几种不同的message子类,如通知消息、红包消息等,在此基础上将sendMessage等方法进一步细分,并对优化有了更高的要求。


基础架构

//MyNetwork
public class MyNetwork implements Network {
private HashMap people;
private HashMap groups;
private HashMap messages;
private HashMap emojiList;
private MyDisjointSetUnion dsu;
private ArrayList values;
public MyNetwork() {
people = new HashMap<>();
groups = new HashMap<>();
messages = new HashMap<>();
dsu = new MyDisjointSetUnion();
values = new ArrayList<>();
emojiList = new HashMap<>();
dsu.addValues(values);
}
...
}
//MyGroup 同hw10
//MyPerson 同hw10
//MyMessage 同hw10
//MyNoticeMessage
public class MyNoticeMessage extends MyMessage implements NoticeMessage {
private String string;
public MyNoticeMessage(int messageId, String noticeString,
Person messagePerson1, Person messagePerson2) {
super(messageId, noticeString.length(), messagePerson1, messagePerson2);
string = noticeString;
}
public MyNoticeMessage(int messageId, String noticeString,
Person messagePerson1, Group messageGroup) {
super(messageId, noticeString.length(), messagePerson1, messageGroup);
string = noticeString;
}
...
}
//MyEmojiMessage
public class MyEmojiMessage extends MyMessage implements EmojiMessage {
private int emojiId;
public MyEmojiMessage(int messageId, int emojiNumber,
Person messagePerson1, Person messagePerson2) {
super(messageId, emojiNumber, messagePerson1, messagePerson2);
emojiId = emojiNumber;
}
public MyEmojiMessage(int messageId, int emojiNumber,
Person messagePerson1, Group messageGroup) {
super(messageId, emojiNumber, messagePerson1, messageGroup);
emojiId = emojiNumber;
}
...
}
//MyRedEnvelopeMessage
public class MyRedEnvelopeMessage extends MyMessage implements RedEnvelopeMessage {
private int money;
public MyRedEnvelopeMessage(int messageId, int luckyMoney,
Person messagePerson1, Person messagePerson2) {
super(messageId, luckyMoney * 5, messagePerson1, messagePerson2);
mOney= luckyMoney;
}
public MyRedEnvelopeMessage(int messageId, int luckyMoney,
Person messagePerson1, Group messageGroup) {
super(messageId, luckyMoney * 5, messagePerson1, messageGroup);
mOney= luckyMoney;
}
...
}

  可以看出,本次作业的架构和优化,大多都是基于新的message子类要求的。


性能优化

  hw11中出现了 send_indirect_message 指令,需要求消息的双方的最短路。由于数据要求符合,可以直接使用Dijkstra算法。

  除此之外,Dijkstra算法还能进行堆优化,直接使用java自带的容器PriorityQueue即可,很简单,但是可惜当时没有想到优化,也没注意仔细看讨论区,早早过了中测就没再打开,强测丢了一个点,很可惜。


二、测试数据


手动测试

  仔细看代码,寻找jml中的一些坑点,同时不能放过异常处理,全面编写数据进行测试;

  另外还要对性能进行测试,如最小生成树算法等等,有的指令不进行细致的优化就有可能TLE。


自动测试

  随机生成强测代码,可以一次生成很多针对一个点的数据,进行集中测试,也可以生成全面数据,全面测试,个人感觉还是集中测试更容易发现bug。


三、Bug分析

  hw9中强测没有发现bug,但互测中发现了一个bug,具体原因是判断是否存在id前就进行了get操作,导致取到了空指针报错。

  hw10中强测互测均未发现bug。

  hw11中强测互测都发现了一个bug,就是Dijkstra算法忘记优化导致了TLE,不再赘述。


四、NetWork扩展


题目描述

假设出现了几种不同的Person



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

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

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

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

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


拓展

  Advertiser、Producer 和 Customer 可以设计为 Person 的子类,继承 Person 的属性;Advertisement 和 BuyMessage 可以设计为 Message 的子类,继承 Message 的属性。


发送广告:

image


生产商生产产品:

image


购买商品:

image


五、心得体会

  本单元三次作业相对前两单元难度降低了不少,主要是让我们了解规格化设计,熟悉基于jml的规格模式。jml确实给了很明确的目标和架构,不用自己搭建架构和设计方法,少走了很多弯路,也让我对jml有了更深的理解。

  这几次作业也带我们回顾了大一程设和数据结构里学到的一些知识,涉及了时间复杂度的计算,甚至到后来有的方法在优化时直接换了架构,比如有的方法原先需要调用子类中的方法,但优化后直接在父类中解决即可,也就是说抛弃了子类中的某些方法。这让我更加体会到,在给定的规格下,内部的实现可以是多种多样的。

  经过一单元的学习,在我看来,jml优缺点都很明显。jml的优点就体现在本单元的难度上,由于jml清晰地传递了需求甚至具体方法,没有自然语言的二义性,比其他oo作业(还有os)的要求明确的多,不用猜测含义,即使第一次没看明白也可以通过反复阅读解决;另外,jml缺点也很明显,没有二义性带来的副作用就是表达啰嗦,有时候一句话的要求,读jml中能要看几十行才能看明白是要干嘛(比如sendMessage),所以后来有的描述过长的方法是通过看方法名猜目的,然后再阅读jml检查自己的判断准不准确,这对jml编写者和使用者都是一个很大的挑战。

  由此看来,如果要想把所有的函数的效果全部用JML描写,则会带来巨大的复杂性,所以在实际应用中到底是选择效率高但是可能有歧义的自然语言,还是使用极其准确但可能相对复杂得多的jml,还是有待考虑。



推荐阅读
author-avatar
mobiledu2502932321
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有