哈工大2020春软件构造Lab4实验报告

实验报告只是为了提供给学弟学妹们参考,所以很多代码都没有完整给出,希望学弟学妹们只用来参考,请勿直接抄袭!!!
有问题请联系QQ:1187987704

文章目录

1 实验目标概述

本次实验重点训练学生面向健壮性和正确性的编程技能,利用错误和异常处
理、断言与防御式编程技术、日志/断点等调试技术、黑盒测试编程技术,使程序
可在不同的健壮性/正确性需求下能恰当的处理各种例外与错误情况,在出错后
可优雅的退出或继续执行,发现错误之后可有效的定位错误并做出修改。
实验针对 Lab 3 中写好的 ADT 代码和基于该 ADT 的三个应用的代码,使用
以下技术进行改造,提高其健壮性和正确性:

  • 错误处理
  • 异常处理
  • Assertion 和防御式编程
  • 日志
  • 调试技术
  • 黑盒测试及代码覆盖度

2 实验环境配置

实验环境设置请参见 Lab-0 实验指南。
除此之外,本次实验需要你在 Eclipse IDE 中安装配置 SpotBugs(用于 Java
代码静态分析的工具)。请访问 https://spotbugs.github.io,了解它并学习其安装、
配置和使用
配置时,没有困难

https://github.com/ComputerScienceHIT/Lab4-1180300120.git

3 实验过程

请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。

3.1 Error and Exception Handling

3.1.1 处理输入文本中的三类错误

3.1.1.1 SameAirportException(飞机起落机场相同)

对比起落机场的字符串,相同则抛出此异常

3.1.1.2 PlaneTypeException(飞机型号异常)

如果飞机型号不是只由大写字母和数字组成,则抛出此异常

3.1.1.3 PlaneSeatsException(飞机座位数异常)

如果飞机座位数不在[50,600]范围内,则抛出此异常

3.1.1.4 PlaneNumberException(飞机编号异常)

如果飞机编号不是以N或B开头后面接4位数字的,则抛出此异常

3.1.1.5 PlaneAgeException(机龄异常)

如果机龄不是在[0,30]范围内,并且最多有一位小数的,并且不允许出现02.1这种情况,如果违反则抛出此异常

3.1.1.6 SameEntryException(两个计划项相同的异常)

如果两个计划项完全相同,则抛出此异常

3.1.1.7 SameFlightNumDifferentAirportsOrTimeException(同一个航班号,虽然日期不同,但其出发或到达机场、出发或到达时间有差异的异常)

如果同一个航班号,但是出发到达机场或时间不同则抛出此异常

3.1.1.8 EntryNumberException(计划项编号异常)

如果计划项编号不是两位大写字母开头接2到4位数字的,则抛出此异常

3.1.1.9 DepatureTimeException(起飞时间与第一行日期不一致的异常)

如果起飞时间与计划项第一场日期不一致,则抛出此异常

3.1.1.10 SameFlightNumSameDayException(两个航班号相同且在同一天的异常)

如果检测到两个航班号相同,并且在同一天,则抛出此异常

3.1.1.11 TimeOrderException(起飞降落时间顺序的异常)

如果起飞时间晚于降落时间,则抛出此异常

3.1.1.12 SamePlaneNumDifferentTypeOrSeatsOrAgeException(飞机编号相同,但是信息不同的异常)

如果飞机编号相同,但是座位数或机龄或型号不同,则抛出此异常

3.1.2处理客户端操作时产生的异常
3.1.2.1 CannotCancelException(计划项不可被取消的异常)

如果此计划项当前状态不可被取消,则抛出此异常

3.1.2.2 LocationConflictException(位置冲突异常)

如果新的计划项与之前的计划项存在位置冲突(相同不可共用的位置,使用时间有重叠),则抛出此异常

3.1.2.3 ResourceOccupiedException(资源被占用的异常)

如果删除某资源时,此资源正在被占用,则抛出此异常

3.1.2.4 LocationOccupiedException(位置被占用的异常)

如果删除某位置时,此位置正在被占用,则抛出此异常

3.1.2.5 ResourceConflictException(资源冲突异常)

如果新的计划项与之前的计划项存在资源冲突(相同的资源,使用时间有重叠),则抛出此异常

3.2 Assertion and Defensive Programming

3.2.1 checkRep()检查rep invariants

只对一些特殊的checkRep作出解释,其他普遍的检查变量不为空的checkRep不做说明
学习日程的board:

private void checkRep() {
		assert location != null;
		Pattern pattern = 	Pattern.compile("\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d\\s\\d\\d\\:\\d\\d");
		Matcher matcher = pattern.matcher(currentTime.getTime());
		assert matcher.find();
	}

时间要符合标准格式

学习资料资源:

private void checkRep() {
		assert name != null;
		assert department != null;
		assert date != null;
		Pattern pattern = Pattern.compile("\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d\\s\\d\\d\\:\\d\\d");
		Matcher matcher = pattern.matcher(date);
		assert matcher.find();
	}

时间要符合标准格式

航班的board:


private void checkRep() {
		assert location != null;
		Pattern pattern = Pattern.compile("\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d\\s\\d\\d\\:\\d\\d");
		Matcher matcher = pattern.matcher(currentTime.getTime());
		assert matcher.find();
	}

时间要符合标准格式

航班计划项:

private void checkRep() {		//起飞时间晚于降落时间的情况按照异常在APP里处理
		assert name != null;
		assert location != null;
		assert source != null;
		assert time != null;
		assert !location.getSrcLocation().getLocation().equals(location.getDstLocation().getLocation());
	}

起飞降落地点不可以相同

航班资源:

private void checkRep() {
		assert flightNum != null;
		assert mode != null;
		assert sitNum >= 0;
		assert year >= 0;
	}

座位数和机龄要大于等于0, 至于具体范围,利用异常在APP中进行处理

Time:

private void checkRep() {
		assert time != null;
		assert Year >= 0;
		assert Month >= 0;
		assert Day >= 0;
		assert Hour >= 0;
		assert Min >= 0;
		Pattern pattern = Pattern.compile("\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d\\s\\d\\d\\:\\d\\d");
		Matcher matcher = pattern.matcher(time);
		assert matcher.find();
	}

所有的时间信息都是不小于0的,并且时间格式要符合标准

TimeSlot:

private void checkRep() throws ParseException {
		assert srcTime != null;
		assert dstTime != null;
		assert srcYear >= 0;
		assert dstYear >= 0;
		assert srcMonth >= 0;
		assert dstMonth >= 0;
		assert srcDay >= 0;
		assert dstDay >= 0;
		assert srcHour >= 0;
		assert dstHour >= 0;
		assert srcMin >= 0;
		assert dstMin >= 0;
//		assert srcTime.getTimeNum() <= dstTime.getTimeNum();	//这步会在异常处理中进行
	}

同样,所有数字大于等于0, 其中时间顺序的问题,在APP中利用异常进行处理

高铁的board:

private void checkRep() {
		assert location != null;
		Pattern pattern = Pattern.compile("\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d\\s\\d\\d\\:\\d\\d");
		Matcher matcher = pattern.matcher(currentTime.getTime());
		assert matcher.find();
	}

时间要符合标准格式

火车的计划项:

private void checkRep() {
		assert name != null;
		assert location != null;
		assert source != null;
		assert time != null;
		for(int i = 0; i < location.getLocation().size(); i ++) {
			for(int j = i + 1; j < location.getLocation().size(); j ++) {
				assert !location.getLocation().get(i).getLocation().equals(location.getLocation().get(j).getLocation());
			}
		}
		
		for(int i = 0; i < source.getSource().size(); i ++) {
			for(int j = i + 1; j < source.getSource().size(); j ++) {
				assert !source.getSource().get(i).getName().equals(source.getSource().get(j).getName());
			}
		}
		
		for(int i = 0; i < time.size(); i ++) {
			for(int j = i + 1; j < time.size(); j ++) {
				try {
					assert time.get(i).getDstTimeNum() <= time.get(j).getSrcTimeNum();
				} catch (ParseException e) {
					e.printStackTrace();
				}
			}
		}
	}

各个站的地点不能相同,并且时间之间都要有序,并且车厢资源不能相同

高铁资源:

private void checkRep() {
		assert trainNum != null;
		assert mode != null;
		assert sitNum >= 0;
		assert year >= 0;
	}

座位数和生产年份要不小于0

3.2.2 Assertion/异常机制来保障pre-/post-condition

所有的状态转换,都要在转换前利用assert检验之前状态不为空(前置条件),转换后同样要检验状态不为空(后置条件)

3.2.3 你的代码的防御式策略概述

类能做成不可变的就不要做成可变的,方法如果外部不需要采用,就用private修饰,所有的类都有checkRep,可以有效的防御一些bug,再加上异常处理机制,可以做到较好的代码防御

3.3 Logging

3.3.1 异常处理的日志功能

为异常处理后都加上日志信息,并且加日志都记录到对应的文件中,有些细节需要处理:包括,规范日志语言为英文(这样方便后面匹配),日志的等级设置,我采用的是默认,还有就是日志的信息尽量要既易于理解又要简略

3.3.2 应用层操作的日志功能

在APP中,只要遇到异常,就需要有一条对应日志来记录他,这个不难处理,只是一个异常对应一个日志即可。

3.3.3 日志查询功能

首先在之前的处理中,已经将日志信息放入到了对应的文件中,那么只需要读取对应的文件,即可得到对应APP中所有发生的异常信息,首先日志的格式是:日期+类+方法+信息,所以需要一个正则表达式去匹配每条日志,正则表达式为:

"(.*?) P1\\.(.*?)App (.*?)\\nINFO: (.*?)\\n"

其中的INFO是因为我都是以log.info输出的异常信息,每读取两行做一个合并,合并后的信息就是一条日志,还要注意的是日志的时间格式为:MMM dd, yyyy hh:mm:ss a,还要注意的是,要将此设为英文格式,否则可能类似May和PM就会无法识别,总体为:private SimpleDateFormat df2 = new SimpleDateFormat(“MMM dd, yyyy hh:mm:ss a”, Locale.ENGLISH);
查询前,要询问查询时间段,包括起始时间和终止时间,然后一边匹配日志,一边检测时间是否在时间段内,如果在就以一个比较好的格式输出即可。

3.4 Testing for Robustness and Correctness

3.4.1 Testing strategy

按照等价类划分,尽量考虑全特殊情况,每个类都是如此,还要考虑到代码要尽可能的去覆盖完全

3.4.2 测试用例设计

为每个异常类都设计一个对应异常文件,如下:
在这里插入图片描述

3.4.3 测试运行结果与EclEmma覆盖度报告

在这里插入图片描述

由于有checkRep中的assert的原因,所以很多代码覆盖度都会将至50%以下。

3.5 SpotBugs tool

发现的错误:可能会有递归循环的问题

在这里插入图片描述

解决后:

在这里插入图片描述

3.6 Debugging

3.6.1 EventManager程序

理解待调试程序的代码思想
把时间放到数轴上,开始时间的位置置为1,结束时间置为-1,如果有多个开始时间或结束时间相同,则累加,这样从前向后遍历,对数字做和,这个数字就是当前有多少个事件同时发生,找到其中最大值即可。

发现并定位错误的过程
1:首先没有前置条件检验:要检验day,start,end的范围以及关系
2:没有对day进行处理,数轴上的位置应该是day24+start/end,而不是仅仅用start/end来记录
3:没有检验空指针,如果temp.get(day
24+start)不为空,则对得到的值加一,如果为空则将值设为1,再放入到temp中,end的处理类似

你如何修正错误
与定位(上一条)放在一起解决了

修复之后的测试结果

在这里插入图片描述

3.6.2 LowestPrice程序

理解待调试程序的代码思想
计算都按单价买的价格dot,遍历组合套餐,依次对所需个数判断,如果全都少于或等于所需个数,那么选定此套餐,并比较dot与使用此套餐的价格后再使用其他可用套餐(递归过程)比较,选取小的那个记录,最后即可选出最低价格

发现并定位错误的过程
1.list越界,有基础list的遍历范围是<=size,应该改为<size
2.没有判断套餐可行就对所需物品进行减少,应该在检测套餐可行后,逐个对所需物品数量进行变更
3.套餐价格:原代码中选取的是第j-1个,应该为第j个

你如何修正错误
与定位(上一条)放在一起解决了

修复之后的测试结果

在这里插入图片描述

3.6.3 FlightClient/Flight/Plane程序

理解待调试程序的代码思想
遍历选取飞机,检测是否与其他计划项有冲突,如果其中有一个没有冲突,则此计划项跳过,继续检验下一计划项,如果此计划项与其他计划项都有冲突,则返回false

发现并定位错误的过程
1.Collections.sort没有重写compare方法,应该重写此方法
2.飞机随机遍历的方法不对,运气不好的话,可能导致一直在一个飞机上做判断,应该随机到一个后,将此飞机与list中的最后一个飞机交换,然后下一次在list的size-1的范围内随机,依次类推,直到所有飞机都检测后,应该跳出循环
3.不能只有飞机不相等的时候continue,这样可能导致空指针异常,应该在指针为空或者(指针不空,飞机不同)或者航班相同的时候都continue
4.时间的比较应该转为long型比较
5.没有返回false的情况,应该在循环跳出后仍然没有分配时就将返回值置为false,并break

你如何修正错误
与定位(上一条)放在一起解决了

修复之后的测试结果

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值