BUAA_OO_第三单元总结
BUAA_OO_第三单元总结
本次作业主要针对JML规格进行建模与实现。
数据构造
本单元进行自测数据的构造主要通过随机数据和边界数据来进行测试。
测试的基础是利用JML规格重新构建一个测评机。通过JML的规格来生成不同的类,然后构建类中的属性。调用满足JML的前置条件的方法来进行数据的增删,然后通过与JML相同或者等价的操作来生成标准答案,进行比对。
随机数据即利用随机数来生成JML的指令,加入并查询各个属性。
边界数据的构造主要通过手动来测试。例如在加入人员后进行一千条左右的查询关系的指令。通过相同的指令重复出现来测试程序的速度,保证在规定时间内运行得出结果。
构建和维护
MyNetwork
- 维护了整个社交网络。保存了网络中得各种组织、个体和消息。
- 利用了并查集UnionFind类来维护人员之间的联通关系,实现了连通块的查找。
- 利用了Edge类来储存人员之间的关联,保存接收人员和关系价值,以此来方便最小生成树和最短路径的查询。
- 在方法中维护了消息的传递,例如表情的热度、红包的发送等信息。
MyPerson
- 维护了与单个人员相关的关系、金钱、年龄、社交价值和消息等基本信息。
MyGroup
- 维护了社交群体中的人员等基本信息。
- 在人员的增删、关系变动和消息传递时维护了总体的年龄、年龄平方和、总的社交关系、金钱等信息。
MyMessage
- 维护了消息的信息、表情、红包、提醒、社交价值等基本信息。
Bug与修复
第九次作业
第九次作业主要是在维护并查集时,利用了HashMap来维护节点关系。因而在查询连通块时,使用了entry.getKey()==entry.getValue的方式来判断是否为连通块的根节点。但是由于key和value都是Integer类型,直接进行了比较导致了错误。
修复时使用了entry.getKey().intValue()==entry.getValue().intValue()的方式来进行修复,利用基本类型的数据进行比较。
第十次作业
第十次作业出现的问题为qgav。在Group类中查询年龄方差的时候,进行了直接计算,因而导致了超时。
修复时,采用了在增删人员时维护相关的信息。保存群体的年龄总和和年龄平方和两个数据,在qgav时进行计算,减小了时间复杂度。
第十一次作业
第十一次作业没有出现明显的性能问题。
Network拓展
public interface Network {
/*@ public instance model non_null Advertiser[] advertisers;
@ public instance model non_null Producer[] producers;
@ public instance model non_null Customer[] customers;
@ public instance model non_null Goods[] goods;
@*/
public void produce(int id1, int id2);
public boolean hasProducer(int id);
public boolean hasGoods(int id);
public boolean hasAdvertiser(int id);
public boolean hasCustomer(int id);
public Producer getProducer(int id);
public Advertiser getAdvertiser(int id);
public Goods getGoods(int id);
public Customer getCustomer(int id);
/*@ public normal_behavoir
@ assignable producer[*].money, advertiser[*].money, advertiser[i].goods;
@ requires hasProducer(id1) && hasAdvertiser(id2) && hasGoods(id3) && (\exists int i; 0 <= i && i < getProducer(id1).goods.length; getProducer(id1).goods[i].equals(getGood(id3)));
@ ensures getProducer(id1).money = \old(getProducer(id1).money) - money;
@ ensures getAdvertiser(id2).money = \old(getAdvertiser(id2).money) + money;
@ ensures (\exists int i; 0 <= i && i < getAdvertiser(id2).goods.length; getAdvertiser(id2).good[i].equals(getGoods(id3)));
@ ensures (\forall int i; 0 <= i && i < \old(getAdvertiser(id2).good.length; (\exists int j; 0 <= j && j < getAdvertiser(id2).good.length; getAdvertiser(id2).goods[j].equals(\old(getAdvertiser(id2).goods[j])))));
@ ensures getAdvertiser(id2).goods.length = \old(getAdvertiser(id2).goods.legth) + 1;
@*/
public void advertise(int id1, int id2, int id3, int money);
/*@ public normal_behavoir
@ assignable producer[*].money, customer[*].money, goods[*].sale;
@ requires hasProducer(id1) && hasAdvertiser(id2) && hasGoods(id3) && hasCustomer(id4) && (\exists int i; 0 <= i && i < getProducer(id1).goods.length; getProducer(id1).goods[i].equals(getGood(id3))) && (\exists int i; 0 <= i && i < getAdvertiser(id2).goods.length; getAdvertiser(id2).goods[i].equals(getGoods(id3)));
@ ensures getProducer(id1).money = \old(getProducer(id1).money) + getGoods(id3).money;
@ ensures getCustomer(id4).money = \old(getCustomer(id4).money) + getGoods(id4).money;
@ ensures getGoods(id3).sale = \old(getGoods(id3).sale) + 1;
@*/
public void purchase(int id1, int id2, int id3, int id4);
/*@ public normal_behavoir
@ assignable nothing;
@ requires hasGoods(id);
@ ensures \result = getGoods(id).sale;
@*/
public int querySale(int id);
public int[] querySalePath(int id);
}
学习体会
- 一个确定的规格对于代码工程来说十分重要。本单元确定的JML规格对于代码的书写十分有利。不仅是具有确定而且规范的输入和输出,同时一个良好的规格还体现了整个工程的思路。使得各个方法描述严谨,功能清晰,有助于厘清整体思路。
- 类的内聚和耦合会改变整个代码的观感和理解。本次的接口设计都将类划分的极为详细,每个类功能明确,互不相干。但是在调用方面又能够较好组合完成所需要的功能。这在整体理解和使用上都有很大的帮助。
- 许多的测试不仅仅局限在一些困难的方法上。在很多不起眼的地方也容易出现失误和性能问题。有些方法虽然简短,但是经过多层循环仍会达到很大的时间复杂度。许多简单且直白的代码也可能有许多bug隐藏。因而代码测试应考虑整体,对于各个部分都进行充分的强度测试。