二、错误处理
在处理错误时发生的错误通常是最常见的缺陷。错误处理产生的错误包括:未预料到错误发生的可能性并防止其发生,没有注意错误状态,以及较严重的:程序可能与错误数据一起工作并最终产生错误结果的情况。
1、错误预防
程序应具备这种能力:它能保护自己不受到系统其他部分的影响(包括有害输入和有害处理)。如果程序可能与错误数据共同工作,确保其在发生严重可怕的影响之前(如程序崩溃,数据丢失与错误,系统崩溃等),检查并消除这些问题。
1)不充分的初始状态验证
如果内存的某个区域必须以其中所有位都是0开始,那么程序应该可以运行一个抽样检查,而不是假定已经存在0值。这会导致程序初始化时发生内存错,甚至不能启动。
2)不充分的用户输入检查
此类问题非常常见,开发人员可能会在编写程序时遗漏掉大量这方面的问题。告诉人们输入1位到3位数是不够的,有些人可能会输入5位甚至更多,也有人 会输入特殊字符或是运算符,还有些人会按下功能键一次或多次,如果程序允许输入,那么程序就应能顺利应付,而不是一打非专业人士不能明白的提示甚至更糟的 情况。
3)对受损数据不能充分预防
没有人能保证磁盘上的数据是好的。可能是有人已经编辑过或者根本是有硬件问题。即使开发人员认定在保存前的文件是有效的,那么他也应该检查(校验)下次打开的是否是同一个文件。
4)不充分的参数传递测试
一个子程序不应该假定得到了正确的调用(事实上,采用相反的想法可能会让测试进行得更加顺利一些)。它应该确保传递给它的数据在其可控制的范围之内。
5)针对操作系统的预防不充分
操作系统存在缺陷――不光是过去,现在甚至将来也是,应用程序可能会触发其中存在的问题。如:如果开发人员知道,他把数据送到磁盘后很快又把数据送到打印机,会引起一些操作系统的崩溃,那么他就应该确保程序在任何情况下都不会这样做。
6)不适当的版本控制
如果可执行代码不止存在于一个文件中,那么有人会尝试把某一文件的新版本和老版本一起使用,客户对其软件升级使得这类问题时常发生,但是又不能明白出了什么问题。新版本应确保所有的代码都是最新的,当然也要对老版本有完整的备份。
7)针对恶意使用的不充分预防
人们有时会有意无意的提供程序有害输入,或者尝试触发错误状况(特别是做测试的家伙们)。不要假定“任何有理智的人都不会这么做”,相信我:程序不完全是为了“有理智”的人开发的。
2、错误检测
程序通常有足够的可用信息来检测数据中或其操作中的错误。这部分内容将指导一部分常见的错误检测方式并对其进行归类。
1)忽视溢出
一个数值计算结果对于程序来说太大以至于无法处理时,就会产生溢出。溢出由较大数字相加和相乘或者被0除或是由于过小的分数除而引起。在有既定规则的情况下,溢出是很容易检测到的。
2)忽视不可能的值
在计算机当中,几乎没有什么不可能发生的事。程序应该检查其变量,以确保它们在合理的界限之内,它应可以捕获并拒绝如2月31日这样的日期值。如果 当变量为0时,程序完成某动作,变量为1时完成另一工作,并假设其他所有值都是不可能,那么就必须保证变量只能为0或者是1,对其他所有值进行额外的处 理。在一个项目中,经过数年维护编程后,旧的假定就不一定安全了。
3)忽视看上去不真实的值
有些人可能会从自己帐户里提取100,000,000,000的钱,那么就算他有这么多钱,也应该在通过交易之前向几个不同的人进行确认。这类看似荒唐的用例往往包藏着错误的祸心,应该小心应付。
4)忽视错误标志
程序调用了一个子程序,结果操作不成功,它在一个被称为错误标志的特殊变量中报告了该失败。与通常一样,程序能检查或忽略它,并把从例程返回的误用数据当作真实的进行处理。
5)忽视硬件缺陷或错误情况
程序应该假定它能连接的设备是失败的,许多设备能够发送警告某件事情出错的返回信息。如果有设备这样做了,停止尝试与其交互,并向某人或更高级别的控制程序报告该事件。不能忽视该类情况以避免造成不必要的困扰。
6) 数据比较
结算银行存折时,你有一个你自己认为的余额数值,银行也会提供一个余额数值。在考虑了服务费,银行利息,最近帐单等等数据之后,两个数据不吻合,那么就要好好查一查了。在互相检查两个数据集或计算结果集时,也会产生类似的情况。
3、错误恢复
程序中存在错误,程序已经检测到了错误,而且现在正设法对其进行处理。许多错误恢复代码只是稍微进行了测试,或者根本没有进行测试。错误恢复例程中的缺陷可能比原始问题更严重。
1)自动错误更正
通过检查其他数据或规则集,有时程序不仅能检测错误,而且还能纠正错误,而用不着麻烦任何人。这样的程序是令人满意的,但仅当这种“纠正”是正确的情况时才是如此。
2)未能报告一个错误
程序应该报告任何检测到的内部错误,即使它能自动纠正错误产生的后果也一样。在稍微不同的环境下,它可能检测不到相同的错误。程序可以向用户报告这 一错误,也可以向一个多用户系统的操作员,向磁盘上的日志文件,或者是这些对象的任一组合报告错误。总之,不管怎么样,只要发生了,它就必须报告。
3)未能设置一个错误标志
某子程序被调用,但是结果失败,假定它在失败时设置了一个错误标志,它把控制返回给调用程序,却没有对这个标志进行设置,那么调用程序就会把无用数据当作有效数据传回去,这是应坚决避免的状况。这可能会造成数据冗余,脏数据或是直接导致当前操作失效,严重的则会引起崩溃。
4)程序要走向何方?
一部分代码失效了。它记录了错误,并设置了一个错误标志,接下来干嘛呢?尤其是经过了几个跳转的情况下,它如何才能得知程序中的什么地方返回了控制?
5)中止错误
停止了程序,或者当它在检测到错误时自动停止了,那么它是否关闭了任何打开的输出文件呢?它是否在关闭时会记录退出的原因呢?在最普通的条件下,在即将结束之前它是否进行了整理或者它只是结束但可能留下一团混乱呢?这都是开发人员和测试人员需要考虑的问题之一。
6)从硬件问题中恢复
程序应该适度地处理硬件故障。如果磁盘或其目录已满,你应该能够放入一张新的磁盘,而不只是关闭了你所有的数据。如果一个设备很长时间还没有准备好接收输入,你就没有应该假定它已经断线或断开连接而进行处理。程序决不能够让我们永远在那里坐等。
7)不能从遗失磁盘中退出
假定你的程序要求你插入一张具有它需要的文件的磁盘,如果插入的磁盘不正确,它会再次提醒你,直到插入了正确的磁盘为止。然而,如果没有正确的磁盘,你就没有任何办法可以退出,除非你重启系统。不,这种做法是极端蛮横的,决不能允许出现这样的问题。