OO_Lab2总结博客
OO_Lab2
一、单元内容
本单元内容为规格化设计,即通过参考已经完成的JML描述实现一个社交网络相关功能。
本单元整体来说难度不大,但是却是我最惨的一次作业,所以本博客可能会主要谈一谈测试中的一些策略吧。
二、自测策略
1、白盒测试
即从JML出发针对各种情况构造测试数据,可以选用课程组推荐的JUnit单元测试框架来对每个函数进行测试。我也借此机会学习了一下JUnit的用法。
在构造测试数据的时候,可以充分利用JML规格来构造一些针对性的数据,比如看哪个方法JML较为复杂,可以针对这样的方法构造一些边界情况的数据,比如作业中的queryBlockSum
,getValueSum
,queryLeastConnection
,sendIndirectMessage
等操作,尤其是对于那些不能直接照着JML写,而是需要自己进行优化的方法进行着重测试,构造极端数据。
2、黑盒测试
也就是随机对拍,我个人还是觉得在本课程中随机对拍仍然是最好的调试方式,尤其是在本单元中。本人在本单元第三次作业的时候因为一些其他事情占用了一些时间,而且第二次作业未编写对拍,第三次作业编写对拍相对来说难度不低,因此未进行随机测试,只是进行了一些功能测试和单元测试,各项功能都正常,最后因为在编写异常时直接复制相同类型的类,结果忘了改前面的字符串,然后测试的时候没考虑到这部分,强测喜提30分。
三、架构设计
本单元的目标是要实现一个社交网络,主要包括Network
、Message
、Group
、Person
等类,本单元大部分代码都可以直接按照JML的逻辑完成,因此这些代码不涉及架构方面的问题。
在这里主要是列举一些特殊函数,这些函数要么JML并没有给出具体的实现逻辑,要么按照JML的逻辑可能会导致超时(CTLE)的情况。
1、queryBlockSum
JML提供了一个 \(O(N^2)\) 的做法,但是这无法满足复杂度的要求,观察代码可以看出它计算了所有 \(1\sim i-1\) 中和 \(i\) 不相连的 \(i\) 的个数,本质上也就是连通块个数,因此可以使用并查集来维护。
处于优化代码结构的目的,我将并查集单独分为一类,在其中实现了add(x)
、merge(x1,x2)
、union(x1,x2)
、getCountBlock
等操作,降低了代码的耦合性。
2、queryLeastConnection
本函数的JML只描述了一个边的集合,即我们的答案具有什么样的特征,而对于如何求解则没有给出具体做法,观察这个边的集合会发现他是用最少的边权和把所有点连起来的方案,也就是最小生成树,考虑到复杂度的问题,我们可以使用堆优化的Prim算法来求解。
Prim我同样单独封装为一个类,实现了add(a,b,val)
、query(st)
操作,在外层只需要直接调用即可。
3、queryGroupValueSum
这个从JML出发可以直接得到做法,但是这样的做法却会超时,因此正确的做法是在addRelation
、addPerson
、delPerson
时维护一个Group的答案,这样就将询问原本 \(O(N^2)\) 的复杂度转化为了修改时单次 \(O(N)\) 的复杂度。
4、sendIndirectMessage
与queryLeastConnection操作类似,它给出了一个点的集合,观察可以发现相邻两个点相连,也就是描述了路径的性质,而最终也就是求的最短路径,可以用堆优化Dijkstra解决。具体实现与Prim的实现类似。
四、性能问题与修复情况
性能问题上面已经描述了几个可能出现性能问题的操作最终是如何解决的。
本人自己的错误除了上述写错字符串错误外,在第二单元的Dijkstra实现也出现了错误,一是很简单的Dijkstra写错,二是没有考虑到一个连通块中只有一个点的情况。
这些错误归根到底还是没有进行充分的测试,本次出现的错误都很明显,基本都是由于粗心导致写错,也很容易改正,以后还是应该编写足够的随机测试,只能说吃一堑长一智了。
本人在房内hack出qgvs超时等情况,前面已经提过,再次不再赘述。
五、功能扩展
功能要求
假设出现了几种不同的Person
- Advertiser:持续向外发送产品广告
- Producer:产品生产商,通过Advertiser来销售产品
- Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
- Person:吃瓜群众,不发广告,不买东西,不卖东西
如此Network可以支持市场营销,并能查询某种商品的销售额和销售路径等 请讨论如何对Network扩展,给出相关接口方法,并选择3个核心业务功能的接口方法撰写JML规格(借鉴所总结的JML规格模式)
扩展实现
Advertiser
、Producer
、Customer
实现 Person
接口,新增 AdvertisementMessage
类、BuyMessage
类,他们实现 Message
接口。AdvertisementMessage
类应保存 Producer
、Product
、Price
等信息。BuyMessage
类应保存 Product
等信息。
求产品销售量
/*@ public instance model non_null Product[] products;
@ public instance model non_null Product[] productSales;
@*/
/*@ public normal_behavior
@ requires containsProduct(product);
@ ensures (\exists int i; 0 <= i && i < products.length;
@ products[i].getId() == product.getId() && \result == productSales[i]);
@ also
@ public exceptional_behavior
@ signals (ProductNotFoundException e) !containsProduct(product);
@*/
public int queryProductSales(Product product) throws ProductNotFoundException;
发布广告
/*@ public instance model non_null AdvertisementMessage[] advertisementMessages;
@*/
/*@ public normal_behavior
@ requires !(\exists int i; 0 <= i && i < advertisementMessages.length; advertisementMessages[i].equals(advertisement)) &&
@ containsProductId(advertisement.getProductId()) &&
@ (advertisement.getType() == 0) ==> (advertisement.getPerson1() != advertisement.getPerson2());
@ assignable advertisementMessages;
@ ensures advertisementMessages.length == \old(advertisementMessages.length) + 1;
@ ensures (\forall int i; 0 <= i && i < \old(advertisementMessages.length);
@ (\exists int j; 0 <= j && j < advertisementMessages.length; advertisementMessages[j].equals(\old(advertisementMessages[i]))));
@ ensures (\exists int i; 0 <= i && i < advertisementMessages.length; advertisementMessages[i].equals(advertisement));
@ also
@ exceptional_behavior
@ signals (EqualAdvertisementIdException e) (\exists int i; 0 <= i && i < advertisementMessages.length; advertisementMessages[i].equals(advertisement));
@ signals (ProductIdNotFoundException e) !(\exists int i; 0 <= i && i < advertisementMessages.length; advertisementMessages[i].equals(advertisement)) &&
@ !containsProductId(advertisement.getProductId());
@ signals (EqualPersonIdException e) !(\exists int i; 0 <= i && i < advertisementMessages.length; advertisementMessages[i].equals(advertisement)) &&
@ containsProductId(advertisement.getProductId()) &&
@ advertisement.getType() == 0 && advertisement.getPerson1() == advertisement.getPerson2();
@*/
public void advertise(AdvertisementMessage advertisement)
throws EqualAdvertisementIdException, ProductIdNotFoundException, EqualPersonIdException;
这里只是生成发布广告的Message,之后需要进一步sendIndirectMessage。
生产产品、购买产品的操作与此类似
添加偏好
/*@ public normal_behavior
@ requires !(\exists int i; 0 <= i && i < referenceProductId.length; referenceProductId[i].equals(product.getId()));
@ assignable referenceProductId;
@ ensures referenceProductId.length == \old(referenceProductId.length) + 1;
@ ensures (\forall int i; 0 <= i && i < \old(referenceProductId.length);
@ (\exists int j; 0 <= j && j < referenceProductId.referenceProductId; people[j] == (\old(referenceProductId[i]))));
@ ensures (\exists int i; 0 <= i && i < referenceProductId.length; referenceProductId[i] == product.getId());
@ also
@ public exceptional_behavior
@ signals (EqualProductIdException e) (\exists int i; 0 <= i && i < referenceProductId.length; referenceProductId[i].equals(product.getId()));
@*/
public void AddReference(Product product) throws EqualProductIdException;
六、总结反思
首先是编程思想方面,本单元通过学习JML相关知识以及课外的了解和分享,我了解了契约式编程等相关思想,对于其可靠、易于测试的优势有了一定理解,鉴于目前写的代码都比较短,了解这些思想对于之后编写大规模的项目肯定还是很有用处的。
然后是测试方面,我学会了如何用JUnit进行单元测试。本单元相对前两个单元难度不高,但是却出了很大的错误,之后还是应该更加注重数据的测试,尽可能自己设计覆盖率高的强测。
OO_Lab2总结博客的更多相关文章
- Android请求网络共通类——Hi_博客 Android App 开发笔记
今天 ,来分享一下 ,一个博客App的开发过程,以前也没开发过这种类型App 的经验,求大神们轻点喷. 首先我们要创建一个Andriod 项目 因为要从网络请求数据所以我们先来一个请求网络的共通类. ...
- 一步步开发自己的博客 .NET版(11、Web.config文件的读取和修改)
Web.config的读取 对于Web.config的读取大家都很属性了.平时我们用得比较多的就是appSettings节点下配置.如: 我们对应的代码是: = ConfigurationManage ...
- 一步步开发自己的博客 .NET版(10、前端对话框和消息框的实现)
关于前端对话框.消息框的优秀插件多不胜数.造轮子是为了更好的使用轮子,并不是说自己造的轮子肯定好.所以,这个博客系统基本上都是自己实现的,包括日志记录.响应式布局.评论功能等等一些本可以使用插件的.好 ...
- 【原】Github+Hexo+NextT搭建个人博客
摘要 GitHub 是一个开源项目的托管网站,相信很多人都听过.在上面有很多高质量的项目代码,我们也可以把自己的项目代码托管到GitHub,与朋友们共享交流.GitHub Pages 是Github为 ...
- 我为什么要写LeetCode的博客?
# 增强学习成果 有一个研究成果,在学习中传授他人知识和讨论是最高效的做法,而看书则是最低效的做法(具体研究成果没找到地址).我写LeetCode博客主要目的是增强学习成果.当然,我也想出名,然而不知 ...
- 博客使用BOS上传图片
1.博客平台的选定 从大学开始做个人主页算起,最开始是使用html,CSSS写简单的页面,后面大学毕业之后接触到了WordPress,就开始用WordPress搭建网站.现在还维护着一个农村网站.ht ...
- 在jekyll模板博客中添加网易云模块
最近使用GitHub Pages + Jekyll 搭建了个人博客,作为一名重度音乐患者,博客里面可以不配图,但是不能不配音乐啊. 遂在博客里面引入了网易云模块,这里要感谢网易云的分享机制,对开发者非 ...
- iOS controller解耦探究实现——第一次写博客
大学时曾经做过android的开发,目前的工作是iOS的开发.之前自己记录东西都是通过自己比较喜欢的笔记类的应用记录下了.直到前段时一个哥们拉着我注册了一个博客.现在终于想明白了,博客这个东西受众会稍 ...
- 中文 iOS/Mac 开发博客列表
中文 iOS/Mac 开发博客列表 博客地址 RSS地址 OneV's Den http://onevcat.com/atom.xml 一只魔法师的工坊 http://blog.ibireme.com ...
- 企业shell面试题:获取51CTO博客列表倒序排序考试题
#!/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin HTMLFILE=/home/oldboy/ht ...
随机推荐
- (转) IIS隐藏响应头信息
先安装url-rewrite组件 http://www.iis.net/downloads/microsoft/url-rewrite 修改应用根目录下的Web.config配置文件 <conf ...
- centos7.9重启后,主机名会变为IP地址
现象: centos7.9重启后,主机名会变为IP地址 当关闭DHCP功能,则主机名修改生效,否则重启后,主机名依旧变为图示的IP地址 解决方法:经过排查,由 /etc/sysconfig/netwo ...
- 一些test短代码
#include <stdio.h> int main(){ char ray_tx_pwr[2][3]={{1,2,3},{4,5,6}}; int i = 0; printf(&quo ...
- go环境搭建及tjfoc-gm安装
go环境搭建及tjfoc-gm安装 一.环境配置 1 首先进入usr/local文件夹,下载go语言环境配置压缩包 wget https://dl.google.com/go/go1.19.3.lin ...
- SDCC 学习
单个led.c文件 #include <stdbool.h> __sfr __at(0xb0) P3; __sfr __at(0x88) TCON; __sfr __at(0x89) TM ...
- java第七周学习情况
这个星期 主要是在搞学校在暑期安排的实验报告b 怎么说来着 才知道这个消息几天 这是对学习不上心的体现啊 题目也有点多 慢慢做呗 而Java这边还是看些相关知识呗 说实话 已经学不太懂了 不过没关系 ...
- (面试题)面试官为啥总是让我们手撕call、apply、bind?
引言 上一篇关于<面试官为啥总是喜欢问前端路由实现方式>的文章发布后,发现还是挺受欢迎的.这就给我造成了一定的困惑 之前花了很长时间,实现了一个自认为创意还不错的关于前端如何利用node+ ...
- [2001年NOIP普及组] 求先序排列
给出一棵二叉树的中序与后序排列.求出它的先序排列.(约定树结点用不同的大写字母表示,长度<=8). 输入 第一行输入一个字符串表示二叉树的中序排列,第二行输入一个字符串表示二叉树的后序排列. 输 ...
- python菜鸟学习: 5.字符串的基本用法,进度条显示
# -*- coding: utf-8 -*-import sys,timestr1 = "my name is liyuzhoupan"# 首字母大写print(str1.cap ...
- Java基础学习:3、数组
1.三种声明方式: // 创建一个double数据类型的数组,长度为5 double array[] = new double[5]; // 创建int数据类型数组,长度为6 int age[] = ...