OO第三单元总结

一、总述

第三单元的主要内容为学习编写符合JML规格的代码,同时学习异常的处理和抛出。从某种意义上来说,本单元几乎没有难度:JML规格虽然像是给我们编写程序带上了镣铐,但它却是舒适的镣铐——我们编写代码只要简单地遵守contract即可(当然,前提是不能错误理解contract)。然而,镣铐始终是镣铐,为了更好地完成任务,满足规定的性能要求,我们必须充分利用JML规格,带着镣铐起舞,因此,本单元也是对我们算法编程水平的一个考验。
下面将逐次分析个人在各次作业对于任务的理解与解决方案,架构设计,以及相应的BUG等问题。

二、逐次作业分析

(一)、第一次作业

1、任务分析

第一次作业主要任务是要构建出Person, Group, Network三大类,为后面作业打下基础,特别地,在query_circle任务中,需要我们给出Person间的联通关系。

2、解决方案及图结构构建

由于本次作业是本人第一次按照JML规格编写代码,因此有意地减少了个人发挥,在构建三个类主体结构时,基本选用ArrayList作为容器,虽然牺牲了一定的性能,但却保证了JML规格的正确性。
对于query_circle任务,采用了并查集算法,在Person类中增加相关信息维护并查集(father节点,秩等),同时采用路径压缩和按秩合并等优化措施,做到了不错的复杂度。

3、BUG分析

BUG描述:在本次作业互测中,发现了TLE问题,造成原因是在queryBlockSum函数中,为了查询连通块的个数,完全按照JML描述的规格,采用双层循环做法,虽然有并查集优化加持,但在极限数据下仍然超时。
修复方法:利用并查集的特性,直接查询并查集中根结点的个数,时间复杂度由n^2降至n。

(二)、第二次作业

1、任务分析

第二次作业在总体结构上丰富了Message类相关内容及异常类,同时增加了若干Network方法。
在所有新增内容中,值得注意的有两个方法:queryGroupValueSum 和 queryLeastConnection。
其中,queryLeastConnection对应图论算法最小生成树。

2、解决方案及图结构构建

对于最小生成树问题,本次作业采用Kruskal算法,采用并查集和优先队列进行优化,获得了不错的复杂度。
具体构建方式与在第一次作业中构建并查集方式类似:首先,在每个Person类中维护其在最小生成树中的并查集信息(注意与第一次作业中的并查集相区分),同时,新增Edge类,由两个Person间的关系生成而来,用于优先队列。
除了以上关于最小生成树的问题之外,本次作业还在queryGroupValueSum的推动下对整体实现做了较多优化,包括但不限于:采用HashMap替代ArrayList、对可动态维护的信息进行动态维护,避免反复查询计算。

3、BUG分析

BUG描述:本次作业BUG在于queryGroupValueSum方法超时,原因是采用了JML描述中暴力循环的计算方式,没有进行动态维护。
修复方法:避免使用暴力循环方式,而是在addPerson,delPerson和addRelation三个方法调用时采用动态维护ValueSum值的方法。

(三)、第三次作业

1、任务内容

第三次作业扩充了Message的子类及相关异常,同时增加了若干Network方法。
再新增内容中,值得注意的是sendIndirectMessage方法,对应图论中最短路算法。

2、解决方案及图结构构建

主要讨论最短路实现:在本次作业中选择堆优化的Dijkstra算法。
在每个Person类中存储dis,found属性,分别表示该节点最短路是否找到,以及该对象在当前查询中的最短路距离。
同时,为了使用堆,新增Node类,每个节点状态被更新时,就新构造一个相应状态的Node放入优先队列。

3、BUG分析

本次作业中出现的BUG来自一个粗心的书写问题,没有分析价值。

三、JML测试

作为一种标准化的规格描述语言,JML最大的有点就在于其准确性和编码以及测试时的的指导意义。因此,本单元中,测试也是一大特色。
个人在本单元中采用的测试方法除了过去两个单元均采用过的随机生成数据、边界构造数据、压力测试外,还在JML指导下,针对其划分的不同情况构造特定样例,以保证做到对JML理解的完整性与正确性。可以说,本单元的测试相比过往更具目标性和科学性。
事实证明,JML描述确实有很强的指导作用,在契约化编程的约束和保证下,测试中基本没有出现正确性上的错误。

四、Network扩展及JML规格

(一)、整体思路

Producer类,Advertiser类和Comsumer类,从Person类继承而来。
在Producer类中,维护其生产的产品列表products;在Advertiser中,维护其销售的产品列表sales;在Comsumer类中,维护其购买的商品列表bought。
同时,在NetWork中维护当前Network中所有产品allProducts,所有广告allAdvertisement,且增加addProduct, addAdvertisement, buyProduct方法,分别用于发布产品,发布广告,购买产品。

(二)、JML描述

1、addProduct

    /*@ public normal_behavior
      @ requires !(\exists int i; 0 <= i && i < allProducts.length; allProducts[i].equals(product));
      @ assignable allProducts;
      @ ensures allProducts.length == \old(allProducts) + 1;
      @ ensures (\forall int i; 0 <= i && i < \old(allProducts.length);
      @          (\exists int j; 0 <= j && j < allProducts.length; allProducts[j] == (\old(allProducts[i]))));
      @ ensures (\exists int i; 0 <= i && i < allProducts.length; allProducts[i] == person);
      @ also
      @ public exceptional_behavior
      @ signals (EqualProductIdException e) (\exists int i; 0 <= i && i < allProducts.length;
      @                          allProducts[i].equals(product));
      @*/
    public void addProduct(Product product) throws EqualProductIdException;

2、addAdvertisement

    /*@ public normal_behavior
      @ requires !(\exists int i; 0 <= i && i < allAdvertisements.length; allAdvertisements[i].equals(advertisement));
      @ assignable allAdvertisements;
      @ ensures allAdvertisements.length == \old(allAdvertisements) + 1;
      @ ensures (\forall int i; 0 <= i && i < \old(allAdvertisements.length);
      @          (\exists int j; 0 <= j && j < allAdvertisements.length; allAdvertisements[j] == (\old(allAdvertisements[i]))));
      @ ensures (\exists int i; 0 <= i && i < allAdvertisements.length; allAdvertisements[i] == advertisement);
      @ also
      @ public exceptional_behavior
      @ signals (EqualAdvertisementIdException e) (\exists int i; 0 <= i && i < allAdvertisements.length;
      @                         allAdvertisements[i].equals(advertisement));
      @*/
    public void addAdvertisement(Advertisement advertisement) throws EqualAdvertisementIdException;

3、buyProduct

   /*@ public normal_behavior
     @ requires containsProduct(product) && containsAdvertisement(advertisement) && containsComsumer(comsumer)
     @ ensures comsumer.getBought.length == \old(comsumer.getBought) + 1;
     @ ensures (\forall int i; 0 <= i && i < \old(comsumer.getBought.length);
     @          (\exists int j; 0 <= j && j < comsumer.getBought.length; comsumer.getBought[j] == (\old(comsumer.getBought[i]))));
     @ ensures (\exists int i; 0 <= i && i < comsumer.getBought.length; comsumer.getBought[i] == product);
     @ also
     @ public exceptional_behavior
     @ signals (ProductIdNotFoundException e) (\forall int i; 0 <= i && i < allProducts.length;
     @                         !allProducts[i].equals(product);
     @ signals (AdvertisementIdNotFoundException e) (\forall int i; 0 <= i && i < allAdvertisement.length;
     @                   !allAdvertisements[i].equals(advertisementId);
     @ signals (PersonIdNotFoundException e) (\forall int i; 0 <= i && i < people.length;
     @                         !people[i].equals(comsumer);
     @*/
   public void buyProduct(Product product, Advertisement advertisementId, Comsumer comsumer) throws
   		ProductIdNotFoundException, AdvertisementIdNotFoundException, PersonIdNotFoundException;

五、心得体会

本单元带领我走近了JML这一规格描述语言,学习了契约化编程。总的来说,通过这次学习经历,对于我个人的编码能力、测试能力以及编程思想都有所提升。
JML初衷是很好的,利用逻辑来抽象化、规范化代码的编写过程,能够帮助我们省下很多精力。当然,我们也不能照搬着JML进行CRUD,需要在满足规格条件下充分利用规格。
可能JML自身尚有不完全之处,但我们仍然可以从中学习到关于编程的抽象思想。无论如何,本单元都是一次宝贵的学习经历。

posted @ 2022-06-02 11:07  ydy2001  阅读(27)  评论(0编辑  收藏  举报