【软件构造】——复习篇 面向正确性和健壮性编程

这一节主要是针对程序正确性和健壮性的编程。首先介绍了程序正确性和健壮性的相关概念,并引出JAVA中用于异常处理的Throwable对象,详细介绍了Error/Exception。并就可处理异常的处理机制进行了探讨。给出了开发时检测异常的断言机制,同时讲述了可用于检测程序正确性和健壮性的方法:测试以及其设计策略和写法。

一、程序的正确性和健壮性

健壮性

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

健壮性编程的原则:

  • 封闭实现细节,限定用户恶意行为
  • 考虑极端情况
  • 处理未期望的行为和错误终止,即使终止执行也要向用户展示全面的错误信息

正确性

定义:程序按照规约加以执行的能力,是最重要的质量指标

二者属于不同的侧重,但是正确性是第一侧重指标。对外的接口倾向于健壮,对内的接口倾向于正确。

从外部观察角度衡量程序的健壮性,通过观察平均故障间隔(MTBF)

从内部观察角度,通过残余缺陷率衡量

二、Throwable类

Throwable类概览

 

如果异常抛出客户端可以通过某种方式恢复,则为checked异常,否则为Unchecked异常

Unchecked异常

往往都是在程序运行的时候才会发生的错误。是动态阶段才会发现的

Error

是内部错误,一旦发生直接结束

常见的错误

  • 用户输入错误
  • 设备错误
  • 物理限制

RuntimeException

往往是程序源代码中引入故障所导致的,提前验证可以避免,一旦出现往往不处理

常见的RuntimeException:

  • 数组越界异常
  • 空指针异常
  • 数据参数类型异常
  • 类型转换异常

Checked异常

其他Exception

不是运行时异常,编译器可以帮助检查程序是否抛出或处理对应的异常。

必须捕获并指定错误处理器,否则无法通过编译

同时也需要在规约中声明捕获的变量

异常处理机制

  • try/catch
    • try代码中抛出异常并被捕获后,抛出异常代码后面的代码不会再执行,执行捕获异常的catch的代码
    • 如果没抛出异常不执行catch
    • 抛出异常但是没能捕获,方法结束
    • 可以在catch中继续抛出异常(链式异常),更方便客户端获取错误信息并处理
  • finally
    • ​​​​​​​为了使得异常抛出后仍可以进行某些代码的执行和资源的处理
    • finally部分的代码无论是否捕获异常都会被执行
    • 未抛出异常
    • 捕获了抛出的异常:执行完对应catch块代码再执行finally中代码
    • 未捕获抛出的异常:执行完finally中代码返回客户端
  • throws
    • ​​​​​​​在后置条件中刻画,在方法的规约中明确写清本方法会抛出的所有checked异常以便调用方法的用户进行处理
    • 如果子类型重写了父类型中的函数,那么子类型中方法抛出的异常不能比父类型抛出的异常类型更宽泛
    • 子类型方法可以抛出更具体的异常,也可以不抛出异常
    • 如果父类型的方法未抛出异常,那么子类型的方法也不能抛出异常
  • throw
    • ​​​​​​​用来抛出异常
    • 利用异常的构造函数将发生错误的现场信息充分传递给客户
    • 找到一个能表达错误的Exception类或构造一个新的异常类,构造其实例,将错误信息写入并抛出
    • 一旦抛出异常方法不会再将控制权返回给调用它的用户,因此也无需考虑返回错误代码

创建异常类

三、断言

用来在开发阶段尽早检查出程序的bug,限定在一个方法内部不扩散。在开发阶段的代码中嵌入,检验某些假设设否成立。若成立表示正常,否则表明存在错误。实际运行时不会使用。故不会影响实际运行时性能。断言一旦false

使用场景

  • 内部不变量
  • 表示不变量
  • 控制流不变量:如果条件覆盖不全面,用断言阻断非法用例
  • 方法的前置条件
  • 方法的后置条件

而对于断言和异常、PPT中给出了二者使用的准则:

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

如果参数来自外部,使用异常处理;来自自己写的其他代码,可以使用断言(比如对于private)

四、防御式编程的基本思路

  1. 保护程序免除外部无效输入的影响

  2. 设置路障(Barricade)

        类的Public方法接收到的外部数据都应被认为是dirty的,需要处理干净后再传递到private方法

五、测试与测试优先的编程

这一部分主要讲一下测试的设计、黑盒测试的撰写方法以及利用等价类划分和边界值检测方法实现对于测试高覆盖度的实现

测试用例 = 输入 + 执行条件 + 期望结果

测试优先的编程:

  1. 先写规约
  2. 再写符合规约的测试用例
  3. 写代码、执行测试、修改问题、再执行测试用例直至通过

JUnit单元测试的撰写

在每个方法前使用@Test进行标注指明

第一个参数为预期结果、第二个参数为实际结果

测试方法间互不影响

黑盒测试策略

黑盒测试:只关心是否实现规约中的功能,而不关心内部具体细节;白盒需要关心细节

等价类划分

将被测函数的输入域划分为等价类,从等价类中导出测试用例。针对每个输入数据需要满足的输入条件进行划分等价类。

对象等价:存在自反、传递和对称的关系

一些常见的等价类划分原则:

  • 输入数据限定了数值范围
  • 指明了特定的值
  • 确定了一组数值
  • Y/N

其实这里的等级性和我们之前学到的ADT间的等价性是相似的,都是根据规约来查看。这里的等价并非得到相同的输出,而是看这组对象是否遵循一样的规则。如果遵循的AF是相同的则可以划分为等价类。同时我们要对可能出现的输入数据的特殊情况一一进行考量,千万不要错过任何一个。否则测试就具有很低的覆盖度了。

边界值分析

实际上是对于等价类划分的补充,因为往往程序错误都出现在输入域的边界而非中间。

这里给出的思想是用笛卡尔积的思想,实现全覆盖。多个划分维度上的多个取值要组合起来,每个组合都要有一个用例。

但是!并非所有组合都可能!!!!!

比如下面这个例子:

 就不可能出现:a<0,b>0但是a>b的情况了,需要我们针对题目时时刻注意!

用注释写测试策略

这里就不多细说,大家参照例子进行撰写就好

测试策略撰写一例

 

测试覆盖度

定义:已有的测试用例有多大程度上覆盖了被测程序。覆盖度越低测试越不充分。

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

六、总思维导图

老规矩,上一个我自己做的这部分的导图

面向正确性和健壮性的编程导图

  

测试部分导图

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值