[软件构造]实验回顾:Lab4
实验回顾:Lab4
实验目标: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();
}
通过这些报告,可以给我一个提示,让我在以后的编程中少写出这种容易引起错误的风格的代码,对我的编程习惯还是有帮助的。
第六部分:给定程序的调试
这部分在我看来很无聊,也很累。第一,你需要读懂别人的代码,代码没有注释的时候这个就比较费时间。第二,代码的错误千篇一律,让人觉得很烦。
上一篇: 2020春软件构造lab1实验报告
下一篇: 2020春软件构造lab3实验报告