欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[软件构造]实验回顾:Lab4

程序员文章站 2024-02-08 18:09:52
...

实验目标:Debugging, Exception Handling, and Defensive Programming

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

第一部分:异常处理

异常的定义

对于异常处理,我的感受是,如果想做的很复杂,那是真的可以做的很复杂;要是稍微做的简单些,那是真的能做的非常简单。我的Lab4做得比较仓促,就没有把异常种类分的很细,因为我觉得,能让人理解的清晰的异常抛出机制就很优秀了,不需要分的过于精细:毕竟异常里边还有prompt和gripe,通过这两个就可以看出异常是怎么触发的了。定义的异常类型:InputInvalidException, ParameterNumberException, ConstructFaliureException

public class InputInvalidException extends Exception {
	
	public final String prompt = "Input invalid Exception: ";
	public String gripe = "Unknown exception";
	
	public InputInvalidException() {}
	
	public InputInvalidException(String gripe) {
		this.gripe = gripe;
	}
}

异常处理的具体代码

这个实验里边的异常也就那么几种,参数数量不对,类型不对,读文件失败,这些异常会触发ConstructFaliureException,使整个构建失败。
下面是StellarSystem里边的一段代码,作为例子:

    	try {
    		if(strs.length != 3) {
    			log.severe("Parameters number not right: Stellar");
    			throw new ParameterNumberException("Parameters number not right: Stellar", 3, strs.length);
    		}
    		else{
    			double R, m;
    			try {
    				R = Double.valueOf(strs[1].toString());
    			}catch(NumberFormatException e) {
    				log.severe("Radius is not a number: Stellar");
    				throw new InputInvalidException("Radius is not a number: Stellar");
    			}
    			if(R > 10000 && !strs[1].contains("e") || R < 10000 && strs[1].contains("e")) {
    				log.severe("Radius number format invalid: Stellar");
    				throw new InputInvalidException("Radius number format invalid: Stellar");
    			}
    			
    			try {
    				m = Double.valueOf(strs[2].toString());
    			}catch(NumberFormatException e) {
    				log.severe("Mass is not a number: Stellar");
    				throw new InputInvalidException("Mass is not a number: Stellar");
    			}
    			if(m > 10000 && !strs[2].contains("e") || m < 10000 && strs[2].contains("e")) {
    				log.severe("Mass number format invalid: Stellar");
    				throw new InputInvalidException("Mass number format invalid: Stellar");
    			}
    			
    			this.addCenter(new Stellar(strs[0].trim(), R, m));
    		}
    	}catch(InputInvalidException e) {
    		System.out.println(e.prompt + e.gripe);
    		ex = true;
    	}

其中ex是一个布尔型变量,用于记录构建过程中是否出现了异常。如果一直都没有出现异常,那么构建成功。如果出现了异常,也可以汇总之后再使构建失败。

    	if(ex) {
    		log.severe("Construction failed.");
    		throw new ConstructFaliureException();
    	}

第二部分:断言,checkRep

在这部分中,使用断言技术,保证我们的程序执行正确。由于在实验3中,我已经编写了较全面的checkRep,包括每个方法执行完之后的检测,所以做的工作不是很多。

第三部分:日志

在Lab3中,我在多数方法中都编写了向控制台的输出,现在只需要把他们输出到日志里边即可。
日志记录以下信息:
⚫ 所有的异常/错误:发生的时间、异常/错误类型、异常/错误发生的类名和方法名,异常/错误的具体信息、异常/错误处理的结果;
⚫ 对多轨道系统的所有操作,包括:读取文件、增加/删除轨道、增加中心点物体、增加/删除轨道上的物体、增加/删除物体之间的关系、跃迁等。
日志记录信息,按照严重程度,分为不同的等级,我这里只用了info,warning,severe三种。关于日志的操作,比较基本,就是调用Java自带的logger写日志就行了,网上有很多参考资料,大多数都可以用。
为日志添加FileHandler并初始化的过程:

	public static FileHandler fh = null;
	static {
		try {
			fh = new FileHandler("src\\logging\\AtomStructure.txt");
		} catch (IOException e) {
			e.printStackTrace();
		}
		fh.setFormatter(new SimpleFormatter());
	}

在日志查询的过程中,用字符串是否存在子串的方法就可以有效过滤了。

第四部分:测试

根据课件上的等价类和边界值的测试思想,为各ADT添加了测试用例。这次的测试用例都是针对异常抛出设计的,所以有的时候checkRep在程序中会报错,这个也是没有办法,只能在测试的时候将其注释掉。这个部分没什么难度。

第五部分:用工具检查潜在Bug

这部分使用SpotBugs对代码进行静态检查,检查代码中潜在的危险与漏洞。SpotBugs检查我的代码,报告说明了我的程序中两个不好的地方:
1.在main中,调用三个APP函数参数传null值
解决方法:将没有用的String[] args重新传给APP的主函数

		switch(choice) {
		case 1:
			StellarSystemAPP.main(args);
			break;
		case 2:
			AtomStructureAPP.main(args);
			break;
		case 3:
			SocialNetworkCircleAPP.main(args);
			break;

2.对RuntimeException和checked exception混淆处理
解决方案:分开处理unchecked和checked异常

			try {
				Thread.sleep(10);
			} catch (RuntimeException e) {
				e.printStackTrace();
			}
			catch (Exception e) {
				e.printStackTrace();
			}

通过这些报告,可以给我一个提示,让我在以后的编程中少写出这种容易引起错误的风格的代码,对我的编程习惯还是有帮助的。

第六部分:给定程序的调试

这部分在我看来很无聊,也很累。第一,你需要读懂别人的代码,代码没有注释的时候这个就比较费时间。第二,代码的错误千篇一律,让人觉得很烦。