Lab-4 Report

1 实验目标概述

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

2 实验环境配置

在eclipse的marketplace安装了spotbugs,远程clone

3 实验过程

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

3.1 Error and Exception Handling

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

在Exception下定义了13种错误,包括DifferentDateException, DifferentFlightException, DifferentPlaneException, ElementFormatException, FlightFormatException, MoreThanOneDayException, OneDecimalException, PlaneAgeException, PlaneNumberException, PlaneSeatException, SameAirportException, SameFlightException来处理不同的错误,用这些exception足够处理很多异常了。测试文本保存在txt下。

public class DifferentDateException extends Exception {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public DifferentDateException() {super();}
	
	public DifferentDateException(String message) {super(message);}

}

在可能出现异常的地方直接throw,然后通过exception chain将其传递到App统一处理,处理时采用try-catch,catch到后记录上日志,并且提示用户重新读入文件。

3.1.2 处理客户端操作时产生的异常

用了自定义的LocalConflictException, LocalDeleteException, ResourceConflictException, ResourceDeleteException和系统自带的IllegalStateException对客户端操作的异常进行处理。catch到这些异常后,在控制台打印提示信息,并且恢复操作前的状态,即是忽略了用户的操作。这样可以保证即使用户做出一些违规操作系统仍然能够正常运行。

public class ResourceConflictException extends Exception{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public ResourceConflictException() {super();}
	
	public ResourceConflictException(String message) {super(message);}
}

3.2 Assertion and Defensive Programming

3.2.1 checkRep()检查rep invariants

rep详细请见源代码,此处讲几个例子:

  1. 航班的起飞时间必须要早于降落时间,起飞和降落的机场不相同;
  2. 高铁的起始站和各途经站不能重复,所分配的各个车厢不能有相同的编号
  3. 课程上课时间早于下课时间
	private void checkRep() {
		assert tle != null;
		assert sre != null;
		assert super.getTime().getStartTime().isBefore(super.getTime().getFinishTime());
		assert getArrival() == null || getDeparture() == null || !getArrival().equals(getDeparture());
	}

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

对于很多pre-condition,采用assertion保障

  1. 在set时需要原来的属性为null
  2. 在change是需要原来的属性不为null
    在状态改变时,对于非法的状态改变采用异常机制
@Override
	public EntryState finish() {
		throw new IllegalStateException("未启动");
	}

	@Override
	public EntryState set() {
		return instance;
	}

	@Override
	public EntryState block() {
		throw new IllegalStateException("未启动");
	}

	@Override
	public EntryState unblock() {
		throw new IllegalStateException("未启动");
	}

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

在Client和ADT中嵌入了不少的防御策略。对于client的输入,ADT会用assertion来保障前置条件的正确,而ADT本身又利用了异常处理机制来保障了程序的健壮性。不仅如此,对于Client端的一些输入我也采用了异常处理机制对齐进行处理,争取不需要某个模块的运行就已经发现并提示了错误。

3.3 Logging

3.3.1 异常处理的日志功能

利用log4j和commons-logging实现了日志功能。抛出异常时直接加上日志的log即可。采用WARN级别或者是ERROR级别进行记录。
FlightScheduleApp.log.log(Level.WARN, e.getMessage(), e);

3.3.2 应用层操作的日志功能

采用INFO级别进行记录。
FlightScheduleApp.log.info(“读取文件” + filename);

3.3.3 日志查询功能

根据配置文件,不同app的log都记录到了Log下的*.log文件当中。
配置文件:

log4j.rootLogger=INFO

log4j.logger.Flight=INFO,Flight
log4j.logger.Course=INFO,Course
log4j.logger.Train=INFO,Train

log4j.appender.Flight=org.apache.log4j.FileAppender
log4j.appender.Flight.File=src/Log/FlightLog.log
log4j.appender.Flight.Append=false
log4j.appender.Flight.layout=org.apache.log4j.PatternLayout
log4j.appender.Flight.layout.ConversionPattern=%d %p [%c] = %m%n

log4j.appender.Course=org.apache.log4j.FileAppender
log4j.appender.Course.File=src/Log/CourseLog.log
log4j.appender.Course.Append=false
log4j.appender.Course.layout=org.apache.log4j.PatternLayout
log4j.appender.Course.layout.ConversionPattern=%d %p [%c] = %m%n

log4j.appender.Train=org.apache.log4j.FileAppender
log4j.appender.Train.File=src/Log/TrainLog.log
log4j.appender.Train.Append=false
log4j.appender.Train.layout=org.apache.log4j.PatternLayout
log4j.appender.Train.layout.ConversionPattern=%d %p [%c] = %m%n

在日志查询时,采用正则表达式进行读入,用jtable将日志加以显示。查询时需要输入时间段。

3.4 Testing for Robustness and Correctness

3.4.1 Testing strategy

testing strategy主要是检测了一下null和普通的情况,将输入数据划分为了这两个等价类进行测试。

3.4.2 测试用例设计

对于App和Exception,我构造了一些数据和文件人为的进行了测试,主要是由于这部分我都是采用GUI实现的,Junit不太好处理这方面和Client端的操作。

而Junit中则使用了如下的测试

@Test
	public void setLocationTest() {
		fe.setLocation(loc);
		assertEquals(loc, fe.getLocation());
	}
	
	@Test
	public void changeLocationTest() {
		fe.setLocation(loc);
		fe.changeLocation(loc_1);
		assertEquals(loc_1, fe.getLocation());
	}
	
	@Test
	public void setTest() {
		fe.set(plane);
		assertEquals(plane, fe.get());
	}
	
	@Test
	public void getLocationssTest() {
		List<Location> s = new ArrayList<>();
		s.add(loc);
		fe.setLocation(loc);
		assertEquals(s, fe.getLocationss());
	}
	
	@Test
	public void getTimesTest() {
		List<Timeslot> t = new ArrayList<>();
		t.add(x);
		assertEquals(t, fe.getTimes());
	}
	
	@Test
	public void getResourceTest() {
		fe.set(plane);
		List<Teacher> pl = new ArrayList<>();
		pl.add(plane);
		assertEquals(pl, fe.getResource());
	}

	@Test
	public void toStringTest() {
		assertEquals("Course:1", fe.toString());
	}
	
	@Test
	public void cancelTest() {
		fe.set(plane);
		fe.cancel();
	}

3.5 SpotBugs tool

这部分没有发现什么错误,可能是防御策略比较得当的原因。

3.6 Debugging

3.6.1 EventManager程序

temp里保存的是该点的事件数的一个增量start+1,end-1,事件数为某点的start+end。所以在查询事件数时,直接将之前所有点的temp累加即可,从中找到最大值。
测试时发现不同天的时候会出问题,debug发现原来temp的键表示会出现重复,于是将其改为start += (day * 25); end += (day * 25);这样就避免了重复。

3.6.2 LowestPrice程序

LowestPrice实际上是采用了一个递归的方式实现。感觉有点贪心法的味道。大多数是读代码的时候就发现了有问题将其改了过来,因为代码的思想本身没有多大的错误,主要集中在语法和一些细节上。

3.6.3 FlightClient/Flight/Plane程序

程序本身根据一个Random对List中的计划项的飞机是否会有冲突进行了检测,但是可能会陷入一个没有可分配的飞机的死循环,为其加入一个flag条件,我用的是一个set保存对于该计划项已经查过的飞机,如果全都查过了就退出循环。还有错误是关于Calendar比较的错误,这个比较简单,eclipse都有提示了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值