软件构造第7章-软件健壮性

本文详细探讨了软件的健壮性和正确性,解释了两者的区别。接着介绍了错误和异常处理,包括内部错误、运行时异常、检查异常和未检查异常的处理方式。此外,还讲解了防御性编程和断言的重要性,以及调试方法和测试优先编程,以增强软件的健壮性。

一、健壮性(Robustness)和正确性(Correctness)

健壮性

系统在不正常输入或不正常外部环境下仍能够表现正常的程度。

面向健壮性的编程:处理未期望的行为和错误终止,即使终止执行,也要准确且无歧义地向用户展示全面的错误信息。

程序员需要考虑到任何可能出现的错误操作,总是假定自己的代码可能失败,考虑的方面越多,程序的健壮性就越高。

“对别人宽容点,对自己狠一点。”

正确性

程序按照spec加以执行的能力,这是最重要的质量指标。

两者的区别

正确性是永远给出正确的答案;健壮性是尽可能保持软件正常运行而不是异常退出。

正确性倾向于直接报错(error);健壮性倾向于容错(fault-rolerance)。

正确性让开发者变得更容易,用户输入错误则直接结束;健壮性让用户变得更容易,出错也可以容忍。

对外的借口倾向于健壮;对内的借口倾向于正确。

可靠性 = 健壮性 + 正确性


二、错误和异常处理

内部错误

例如内存溢出、栈溢出等。

程序员通常无能为力,一旦出现内部错误,想办法让程序优雅地结束。

异常

程序执行中的非正常事件,使程序无法再按照预想的流程进行。

异常是自己程序导致的问题,可以捕获并处理。

异常的种类

运行时异常(RuntimeException):由程序员在代码里处理不当造成,在代码中提前验证就可以避免运行时异常。

其他异常:由程序员无法控制的外部原因造成,即使在代码中提前验证也无法完全避免。

检查异常(Checked Exception)和未检查异常(Unchecked Exception)

Checked异常:是Exception的子类型,必须捕获并指定错误处理器handler,否则编译无法通过,类似于静态类型检查。

        处理方法:声明“本方法可能会发生XXX异常”,抛出XXX异常,使用try-catch-finally捕获并处理XXX异常。

Unchecked异常:是RuntimeException的子类型,可以不处理并通过编译,但执行时出现就会导致程序失败,类似于动态类型检查。

        处理方法:可以抛出异常,也可以使用try-catch-finally捕获并处理,但不需要也不应该这么做。

应该采用哪种异常?

如果客户端可以通过其他的方法恢复异常,就采用checked异常;如果客户端对出现的这种异常无能为力,就采用unchecked异常。

异常出现的时候,要做一些试图恢复它的动作而不要仅仅打印它的消息。

用throws声明checked异常

class Person {
    public method() throws FileNotFoundException {
        ......
    }
}
抛出异常

throw new FileNotFoundException();

FileNotFoundException e - new FileNotFoundException();
throw e;
class Person {
    public method() throws FileNotFoundException {
        if(...) { throw new FileNotFoundExceotion(); }
    }
}
自定义异常

如果JDK提供的Exception类无法充分描述程序发生的错误,可以创建自己的异常类。

class PersonException extends Exception {
    public PersonException() { super(); }
    public PersonException(String message) { super(message); }
    public PersonException(String message, Throwable cause) { super(message, cause); }
    public PersonException(Throwable cause) { super(cause); }
}
捕捉异常

使用try-catch来捕捉异常(当需要捕捉异常时,Eclipse会提醒程序员用try-catch包围该段代码)

try {
    code
} catch (ExceptionType e) {
    handler for this type
}

当异常抛出时,前面正常执行的代码被终止,如果异常发生前曾申请过某些资源,那么异常发生后这些资源要被恰当地清理,这就需要用到finally块。

InputStream in = new FileInputStream(...);
try {
    code
} catch (ExceptionType e) {
    handler for this type
} finally {
    in.close();
}


三、断言与防御性编程

断言

在开发阶段的代码中嵌入,检验某些“假设”是否成立。若成立,表明运行正常,否则表明存在错误。

如果出现AssertionError,意味着内部某些假设被违反了。

何时使用断言

断言主要用来判断内部不变量、表示不变量、控制流不变量、方法的前置条件、方法的后置条件等是否满足。

使用断言的主要目的是为了在开发阶段调试程序,尽快避免错误,在运行阶段不再使用断言,因为断言非常影响运行时的性能。

使用异常来处理“预料到可以发生”的不正常情况,使用断言处理“绝不应该发生”的情况。

防御性编程

对来自外部的数据源要仔细检查,对每个函数的输入参数合法要做仔细检查,并决定如何处理非法输入。

隔离舱:从类的public方法接收到的外部数据都要处理过再传递到private方法。


四、调试

Bug种类

Math bugs:例如0做被除数。

Logic bugs:例如无限循环。

Syntax bugs:例如操作符错误。

Resource bugs:例如使用未初始化的变量。

Team working bugs:例如说明和产品不符合。

Debug

debug是测试的后续步骤,测试发现问题,debug消除问题。

“症状”和“原因”可能相隔很远,这是高耦合导致的结果。修改一个bug可能会同时改正其他bug。

步骤:复现bug → 诊断bug → 修复bug → 确定bug不会再次出现

Debug的方法

Memory Dump:在程序运行完成后,通过dump来分析出现的错误。

Stack Trace:stack trace是在执行程序的特定点上所有挂起的方法调用的列表。

Printf debugging:在程序内部各部分展示程序执行时的动态信息,比使用静态dump分析更有效。

Logging:日志。通过设定日志级别来确定要log哪些信息,log结果可被多种渠道加以处理,可通过设定条件进行过滤,并输出为多种格式。可使用层次化的多个日志记录器。

Complier Warning Messages:把编译器的warning level调到最高级别,消除所有warning。

Breakpoints:在代码中设置断点,来检查程序运行到这里的情况。


五、测试与测试优先编程

测试

白盒测试:对程序内部代码结构的测试。

黑盒测试:对程序外部表现出来的行为的测试。

测试思想:我的程序永远是“错”的,对自己的程序“狠”一点。

测试用例

test case = {test inputs + execution conditions + expected results} 输入+执行条件+期望结果

测试用例的书写规则:最可能发现错误,不重复、不冗余、最有效、既不简单也不复杂。

测试优先编程

先写spec,再写符合spec的测试用例。

先写测试会节省大量的调试时间。

单元测试

针对软件的最小单元模型开展测试,隔离各个模块,容易定位错误和调试。

可以使用JUnit来进行单元测试。

assertArrayEquals("failure - byte arrays not same", expected, actual);
assertEquals("failure - strings are not equal", "text", "text");
assertFalse("failure - should be false", false);
assertNotNull("should not be null", new Object());
assertNotSame("should not be same Object", new Object(), new Object());
assertNull("should be null", null);
assertSame("should be same", aNumber, aNumber);
assertTrue("failure - should be ture", true);
assertThat("good", allOf(equalTo("good"), startsWith("good")));
代码覆盖度

测试效果:路径覆盖>分支覆盖>语句覆盖

测试难度:路径覆盖>分支覆盖>语句覆盖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值