由错误处理引发的联想-防御式编程
前两天和一同学谈到程序出错应该如何处理的问题,他讲到错误处理的两个原则,
第一,应该在错误发生时立即将它抛出,而且得抛的很明显,有些人采用静默出错的原则,尝试修复错误并继续运行,这回导致代码调试起来很困难,所以他认为,当程序逻辑出错时,应该立刻崩溃,并生成一段有意义的错误消息,立即崩溃是为了不让事情变得更糟,错误消息应担被写入永久的错误日志,以便过后查明是哪里出错。
第二,就是抛错要快,也要文明,文明抛错,就是只有程序猿才能看到程序崩溃时产生的详细错误消息,程序的用户绝对不能看到这些消息,另一方面,用户也应该得到一些警告,让他们知道有错误发生这一情况,以及可以采取措施来补救。
以上这两点,我觉得还是蛮有道理的,重要的业务逻辑出错了,程序应该停止执行,避免错误蔓延扩散,产生一些列不可预知的问题,到最后都不知道哪出错了。
但是,我又在想,这些都是事后的处理,那么如何提高程序的健壮性呢?联想到了《代码大全》 里面的一章,防御式编程。还有《注重实效的程序员》 这本书里面也有相关防御式编程的介绍。主要的防御式编程手段有断言、错误处理技术、异常、隔离。
1 断言
断言是常用的软件设计技术,其实很简单,就是一个判断一个布尔表达式的语句,如果这个布尔表达式为真,不会有任何效果,但是如果为假,就会告诉程序员,这里有一个断言,去看一下。
需要注意一下几点:
(1) 用断言来处理绝不应该发生的状况,用断言去检查一些理论上不可能发生的情况,因为如果发生了就说明内部逻辑有问题,也就是有bug了。
(2) 避免把需要执行的代码放到断言中
(3) 用断言来注解并验证前条件和后条件,
(4) 断言是用来检测程序内部逻辑的,如果是和外部有数据交流,就不是断言的范畴。
2 错误处理
程序员在编写软件的时候,应该尽可能的预测到可能发生的错误,并对这些错误进行处理,另外,为使程序结构更加清晰,可以将错误的处理交给其它或专门的处理程序,而本身只是报告发生的错误,然后返回相应的错误码。
当错误发生时,我们应该如何处理:
a) 遇到错误数据的最佳做法就是继续执行操操作并简单地返回一个没有危害的数值,数值计算可以返回0,系统记录下这个错误。
b) 把警告信息记录到日志文件中。
c) 返回一个错误码,其实就是把错误向上抛出,由上游会有子程序处理该错误。
d) 当程序出错时,就立即在出错的地方处理。
e) 关闭程序,安全或性命攸关的程序,遇到错误,关闭是最好的选择。
还有一个重要的原则:对错误进行分类,
1.重大错误,程序崩溃,内存溢出等。这类错误一般不可恢复,通常的做法都是报告后直接退出,
2.无关用户的一般性错误,这类错误一般情况下不会导致程序退出,而且和用户没有直接的联系,这时可以写入日志,以便以后进行排查。
3.与用户相关的一般性错误,这类错误通常是由于用户输入错误数据引起这个时候,通常需要告诉用户,哪出错了,
理解错误处理最重要的就是分清楚项目需要处理错误的类型,并对不同的错误采取恰当的处理方式。
3 异常
异常是指程序无法预料到的情况引发的错误,异常不仅在开发中起到很好的防御式作用,它更是一个非常好的调试工具,通过不断地缩小捕获异常代码的范围,准确找出错误的准确位置。
1. 异常和断言一样,都是来处理那些不仅罕见甚至永远不该发生的情况,但是不可滥用,它弱化了类的“封装”性,可能增加复杂度。
2. 如果可以在局部处理异常,就不要抛出。
3. 在异常消息中加入关于导致异常发生的全部信息。
4. 创建一个集中的异常报告机制,这样做能确保异常处理的一致性。
5. 把项目中对异常的使用标准化,目的是保持异常处理便于管理。
4 隔离
隔离是在设计上简化错误处理的策略,事实上,如果所有的代码都做异常和错误处理,会使代码变得臃肿,可读性下降,我们需要在高层次上面避免这种情况的发生,
1. 把某些接口选定为“安全”区域的边界,在这些接口里对边界数据进行合法性校验
2. 将类的公用方法设计为特殊的安全方法,负责检查数据并进行清理,确保安全后,传递给私有方法进行正式操作
3. 在输入数据时将其转换为恰当的类型
5 总结
软件的质量与其健壮性有很大的联系,作为软件的开发人员,要有足够的重视,不要忽视任何的细节,不能依赖测试去发现bug,而是要不断地思考可能发生的问题并采取进行预防,这才是防御式编程的本质。
作者:章为忠
如有问题,可以微信:18618243664 联系我,非常感谢。
关注我的微信公众号,获取相关的 源代码及视频资料。