面向对象2022-第三单元作业总结

面向对象2022-第三单元作业总结

  1. 分析自测过程中如何利用JML规格来准备测试数据

  根据JML代码,可以枚举出所有的分支,从异常情况再到普通操作,构造具有针对性的测试数据,再检验输出结果是否与预期结果相同。下方的queryValue是一个相对简单的例子。

/*@ public normal_behavior
    @ requires contains(id1) && contains(id2) && getPerson(id1).isLinked(getPerson(id2));
    @ ensures \result == getPerson(id1).queryValue(getPerson(id2));
    @ also
    @ public exceptional_behavior
    @ signals (PersonIdNotFoundException e) !contains(id1);
    @ signals (PersonIdNotFoundException e) contains(id1) && !contains(id2);
    @ signals (RelationNotFoundException e) contains(id1) && contains(id2) &&
    @         !getPerson(id1).isLinked(getPerson(id2));
    @*/
  public /*@ pure @*/ int queryValue(int id1, int id2) throws
          PersonIdNotFoundException, RelationNotFoundException;

  这个函数存在4条分支,首先是不包含id1,其次是不包含id2,然后是id1和id2两者并不相连,最后才是输出正确结果。针对这四种可能,可以构造一个包含3个人的例子,1和2相互连接,1和3不连接。前两种情况就可以对应queryValue(0, 1)以及queryValue(1, 0),后两种情况是queryValue(1, 3)以及queryValue(1, 2)。如此一来,这个函数中所有的情况都被列出,通过测试后则可以保证本函数自身功能无误。当然,也要保证函数不能有多余的副作用。

  再复杂一些的函数,同样也是根据JML来遍历可能性构造测试数据,只是有时读起来要费神一些。

 

  1. 梳理本单元的架构设计,分析自己的图模型构建和维护策略

  得益于社交网络在现实生活中的形象性,本单元的架构也简单易懂。在代码运行后会根据MyNetWork生成一个社交网络图,第一个单元只在这个图中添加了MyPersonMyGroup,你可以想象MyGroup就是网络中的俱乐部,MyPerson则是里面的成员。当然,MyPerson可以不是任意一个俱乐部的成员,也可以在多所俱乐部中做客。二三单元则是让这个社交网络更加热闹起来,彼此之间可以发送邮件,而邮件的形式也变得多样。

  总体的架构已经很清楚了,不过针对要求指令,代码中还实现了信息维护。如query_value,要求返回一个俱乐部中所有成员的相互价值(若两个成员是熟人,则存在一定价值)之和。JML代码中对此给出了一个n2复杂度的代码,实现功能完全没有问题。但是,由于query_value指令没有被限制使用次数,多次调用该指令将使得评测超时。其余的一些量,比如俱乐部成员的平均年龄,由一个人展开生成的最小生成树,也需要进行维护。

  不过维护并没有听起来那么简单,一是需要考虑维护成本和实用性,二是维护的正确性。维护的成本是指每次条件修改时都需要修改有关变量,而这种修改其实是不属于JML说明的副作用,使得单条指令的性能下降了。维护的实用性则是这样的维护是否有价值,比如由英国科学家林柯卡特提出的LCT算法,可以实现对最小生成树的维护。但如果评测指令中请求输出最小生成树的次数很小,比如10000条指令中最多只有100次输出,那么这种维护可能并不值得。

  还有一点就是维护的正确性,要求考虑到条件改变的所有情况。比如你作为club的管理员,那么就不仅要在成员入会的时候修改信息,在成员离开的时候也要进行修改。

 

  1. 按照作业分析代码实现出现的性能问题和修复情况

  多亏了和舍友的讨论以及往年学长的博客,对于可能出现的性能问题都有效规避了。此处谈一下看到的别人中弹的情况吧。第一次代码中有些同学只看到了较为复杂的isCircle函数,忽略了不限次数的看似简单无害的query_value,没有在有限时间内完成。不过在经过这一次的经验学习后,也转换为使用维护策略,后期通过了测试。

 

  1. Network进行扩展,并给出相应的JML规格

假设出现了几种不同的Person

- Advertiser:持续向外发送产品广告
- Producer:产品生产商,通过Advertiser来销售产品
- Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
- Person:吃瓜群众,不发广告,不买东西,不卖东西

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

  “天下熙熙,皆为利来;天下攘攘,皆为利往。“ ——司马迁 《货殖列传》

  此处我们假设4种Person均是通过原有的addPerson加入到网络中,在解析时进行分类到不同的容器中。此处或许还要额外设计商品Product类,并且也要存在NetWork中,类似于表情包的存在。AdvertiserProducerCustomer也会有涉及到Product的属性。不过也有可能被简化,只是作为记录。

 

  核心业务功能1号:int querySales(int id),实现销售额查询

/*@ public instance model non_null int[] productIdList;
@ public instance model non_null int[] productSalesList;
@*/

/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < productIdList.length; productIdList[i] == id);
@ ensures (\exists int i; 0 <= i && i < productIdList.length; productIdList[i] == id &&
@         \result == productSalesList[i]);
@ also
@ public exceptional_behavior
@ requires !(\exists int i; 0 <= i && i < productIdList.length; productIdList[i] ==     @id);
@ signals_only ProductIdNotFoundException;
@*/
public /*@ pure @*/ int querySales(int id) throws ProductIdNotFoundException;

  核心业务功能2号:void buyProduct(int customerId, int advertiserId, int ProducerId, int productId),购买产品,客户向广告商发送请求购买产品,然后广告商从生产者处转移商品。其实这个函数有点依赖于架构,因为如果只是各方的金钱数目变化,则本质上和产品没有关系。而且是否需要考虑货存的数目,生产者可以生产什么种类的产品也需要考虑。鉴于此,我们简化的认为生产者一定可以给出相应产品,并不把产品这个概念真正的纳入代码中,而仅是从逻辑变化上来理解这一过程。(甚至不考虑消费者钱够不够)

/*@ public normal_behavior
@ requires customerContains(customerId) && advertiserContains(advertiserId) &&
@ ProducerContains(ProducerId) && ProductContains(productId);
@ ensures getCustomer(customerId).getMoney() ==
@ \old(getCustomer(customerId).getMoney()) - getProduct(ProducerId).getPrice();
@ ensures getAdvertiser(advertiserId).getMoney() ==
@ \old(getAdvertiser(advertiserId).getMoney()) + 0.3*getProduct(ProducerId).getPrice();
@ ensures getProducer(advertiserId).getMoney() ==
@ \old(getProducer(advertiserId).getMoney()) + 0.7 * getProduct(ProducerId).getPrice();
@ ensures (\exists int i; 0 <= i && i < productIdList.length && productIdList[i] ==
@ productId; productSalesList[i] == \old(productSalesList[i]) + 1);
@ also
@ public exceptional_behavior
@ signals (PersonIdNotFoundException e) !customerContains(customerId);
@ signals (PersonIdNotFoundException e) customerContains(customerId) &&
@ !advertiserContains(advertiserId);
@ signals (PersonIdNotFoundException e) customerContains(customerId) &&
@ advertiserContains(advertiserId) && !ProducerContains(ProducerId);
@ signals (ProductIdNotFoundException e) customerContains(customerId) &&
@ advertiserContains(advertiserId) && ProducerContains(ProducerId) &&
@ !ProductContains(productId);
@*/
public void buyProduct(int personId, int advertiserId, int ProducerId, int productId)
throws PersonIdNotFoundException, ProductIdNotFoundException;

  核心业务功能3号:int storeProduct(int id),上市新产品。

/*@ public normal_behavior
@ requires !(\exists int i; 0 <= i && i < productIdList.length; productIdList[i] ==
@ id);
@ assignable productIdList, productSalesList;
@ ensures (\exists int i; 0 <= i && i < productIdList.length; productIdList[i] == id &&
@ productSalesList[i] == 0);
@ ensures productIdList.length == \old(productIdList.length) + 1 &&
@         productSalesList.length == \old(productSalesList.length) + 1;
@ ensures (\forall int i; 0 <= i && i < \old(productIdList.length);
@         (\exists int j; 0 <= j && j < productIdList.length; productIdList[j] ==
@ \old(productIdList[i]) && productSalesList[j] == \old(productSalesList[i])));
@ also
@ public exceptional_behavior
@ signals (EqualProductIdException e)(\exists int i;0 <= i && i < productIdList.length;
@                                     productIdList[i] == id);
@*/
public void storeEmojiId(int id) throws EqualproductIdException;

 

  1. 本单元学习体会

  本单元是最容易对拍的一个单元,和小伙伴们一起对拍,最棒!

  JML的确很好,将规则描述的很清楚。不过读起来并不是特别容易,为了实现准确性而失去了一些简洁性,或许还可以进行改进。但是借助于JML方法进行单元化测试时,的确可以很好地覆盖所有可能性。

  维护或许没有看起来的那么容易,还是要进行综合的考量。

posted @ 2022-06-01 14:53  早点明安  阅读(41)  评论(0编辑  收藏  举报