OO第三
作者:互联网
OO第三单元总结
一、测试数据准备
在本单元的作业中,采用了随机构造数据+对拍的方式进行测试
随机数据的生成
- 针对每次作业中可能会出错的一些方法,在充分理解其JML规格的基础之上对每个方法分别构造数据生成器并进行覆盖
- 对方法单独测试完毕之后,对一些方法进行组合进行综合测试,进一步提高覆盖率
边界数据的生成
在边界数据的生成中,JML规格限定的边界起到了重要作用,主要有以下几类边界数据
-
题目特殊规定
在第九次作业中限定了每个group的人数不能超过1111,针对这一条件手动构造了数据进行测试
-
TLE数据
在本单元的三次作业中都有图论相关的最短路、最小生成树、并查集的内容,考察了一定的算法知识,而且评测机对算法复杂度有一定的限制,因此对于涉及到此类算法的方法构造可能会导致TLE的数据
-
产生异常数据
该部分数据主要针对删除类型的方法,考察容器为空,传递数据为null等特殊情况下方法执行是否正确
例如:针对RedMessage
相关请求的数据生成器
import random
from tokenize import group
name = ["cry", "mxy", "jjw", "wzm", "gzb", "wgr"]
agepool = [0,198,45,9,20,100,140,88,37]
pnum = 800
gnum = 4
ap = "ap"
ar = "ar"
ag = "ag"
atg = "atg"
am = "am"
sim = "sim"
arem = "arem"
sm = "sm"
qm = "qm"
relation = set()
g = dict()
with open("./HW11test/stdin.txt", "w") as f:
# add person
for i in range(pnum):
s = ap + " " + str(i) + " " + random.sample(name, 1)[0] + " " + str(random.randint(0,200)) + "\n"
f.write(s)
# add group
for i in range(gnum):
s = ag + " " + str(i) + "\n"
f.write(s)
# add to group
for i in range(pnum):
gi = random.randint(0, gnum - 1)
s = atg + " " + str(i) + " " + str(gi) + "\n"
g[i] = str(gi)
f.write(s)
# add relation
for i in range(2*pnum):
a = str(random.randint(0, pnum - 1))
b = str(random.randint(0, pnum - 1))
while((a, b) in relation or (b, a) in relation):
a = str(random.randint(0, pnum - 1))
b = str(random.randint(0, pnum - 1))
relation.add((a, b))
s = ar + " " + a + " " + b + " " + str(random.randint(0,1000)) + "\n"
f.write(s)
# add RedMessage
for i in range(200):
t = random.randint(0, 1)
if t == 0:
p = random.sample(relation, 1)
s = arem + " " + str(i) + " " + str(random.randint(0,200)) + " " + "0" + " " + p[0][0] + " " + p[0][1] + "\n"
else:
p = random.randint(0, pnum - 1)
gg = g[p]
s = arem + " " + str(i) + " " + str(random.randint(0,200)) + " " + "1" + " " + str(p) + " " + gg + "\n"
f.write(s)
# send message
for i in range(200):
s = sm + " " + str(i) + "\n"
f.write(s)
# query money
for i in range(pnum):
s = qm + " " + str(i) + "\n"
f.write(s)
二、架构设计及模型维护
架构设计
容器的选择
- 在本单元的作业中涉及到很多查询、插入和删除的操作,因此容器的选择至关重要,不仅关系到代码编写的复杂度,而且影响着方法的效率。
- 在本单元作业中我主要使用HashMap容器进行数据存储,因为对于数据的索引主要是通过其id进行,而且HashMap的查询时间复杂度较为理想,综合考虑选择HashMap进行数据存储。
- 在图论相关的算法中,由于用到了堆优化,选择了优先队列PriorityQueue进行数据管理
图模型构建
- 封装一个新的数据类型
Edge
,将MyPerson
作为图的结点,value
作为图的边 - 利用
PriorityQueue
对最短路、最小生成树相关算法进行优化
图维护策略
- 对于最短路、最小生成树等查询方法,每次查询的时候重新建图
- 对于其它情况,每次进行添加、删除操作的时候,如果涉及到图结构的改变,则及时进行更新
三、性能问题和修复情况
性能问题
- 在第十次作业中想要优化qgvs方法的时间效率但是更新策略出现错误,出现bug
- 在第十一次作业中最短路没有采用堆优化导致TLE错误
修复情况
- 每当有改动group内的people的相关操作时就需要对
valueSum
成员变量进行更新 - 利用优先队列对图论算法进行优化
四、Network拓展
假设出现了几种不同的Person
- Advertiser:持续向外发送产品广告
- Producer:产品生产商,通过Advertiser来销售产品
- Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
- Person:吃瓜群众,不发广告,不买东西,不卖东西
如此Network可以支持市场营销,并能查询某种商品的销售额和销售路径等 请讨论如何对Network扩展,给出相关接口方法,并选择3个核心业务功能的接口方法撰写JML规格
Advertiser、Producer、Customer都可以继承自Person类,实现的思路为生产者生产产品,广告商向外发送Message
,消费者和生产者以广告商为媒介购买和生产产品
选择实现的接口规格如下
addAdvertisementMessage()
/*@ public normal_behavior
@ requires !containsMessage(message.getId());
@ assignable messages;
@ ensures messages.length == \old(messagess.length) + 1;
@ ensures (\forall int i; 0 <= i && i < \old(messages.length);
@ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
@ ensures (\exists int i; 0 <= i && i < messages.length; messages[i].equals(message));
@ also
@ public exceptional_behavior
@ signals (EqualMessageIdException e) containsMessage(message.getId());
@ also
@ public exceptional_behavior
@ signals (AdvertiserNotFoundException e)
@ !(\exists int i; 0 <= i && i < messages.length; messages[i].equals(message))
@ && !(messages[i].getPerson1() instanceof Advertiser);
*/
public void addAdvertisementMessage(Message message) throws EqualMessageIdException, AdvertiserNotFoundException;
sendAdvertisementMessage()
/*@ public normal_behavior
@ requires containsMessage(id) && getMessage(id) instanceof AdvertisementMessage;
@ assignable messages;
@ ensures (\forall int i; 0 <= i && i < people.length &&
@ getMessage(id).getPerson1().isLinked(people[i])
@ ensures (\forall int i; 0 <= i && i < people.length &&
@!getMessage(id).getPerson1().isLinked(people[i]);
@ people[i].getMessages().equals(\old(people[i].getMessages()));
@ 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]))));
@ also
@ public exceptional_behavior
@ signals (MessageIdNotFoundException e) !containsMessage(id);
@*/
public void sendAdvertisementMessage(int id) throws MessageIdNotFoundException;
createProduct()
/*@ public normal_behavior
@ requires contains(peid) && (getPerson(peid) instanceof Producer);
@ ensures getProducer(peid).getProductNumById(ptid) ==
@ \old(getProducer(peid).getProductNumById(ptid)) + 1;
@ also
@ public exceptional_behavior
@ signals (PersonIdNotFoundException e) !contains(peid);
@*/
public void createProduct(int peid, int ptid) throws PersonIdNotFoundException;
五、心得体会
- 本单元主要学习了JML以及契约式编程方法,JML相对于自然语言表述而言能够更加准确地描述出对象的行为和方法
- 在初次根据JML规格撰写代码时,会习惯性的根据JML中的语句来实现功能,例如循环遍历等,但是实际上开发者仅仅需要保证JML中的限制条件能够满足,功能具体的实现过程完全由自己设计
- 对于一些功能比较简单的方法,通过JML基本上能够准确地理解和实现其行为和功能。但是对于涉及到一些复杂算法的方法,例如本单元作业中的最短路、最小生成树等,可能是由于自己阅读的代码量不够多的缘故,我认为仅仅通过阅读其JML规格是很难准确理解其行为的
- 通过本单元作业,意识到在初具规格的项目中算法复杂度会对整个系统的性能产生很大的影响,因此选择合适的结构和算法来组织数据对于完成一个项目来说是非常重要的一环
- 本单元作业看似简单,但是由于对于细节理解的不到位在一些不易察觉的地方出现了bug,通过这些教训更加体会到了测试的重要性
标签:OO,pnum,JML,randint,random,第三,write,str 来源: https://www.cnblogs.com/Tsundoku13/p/16341159.html