软件架构————防御式编程

防御式编程的主要思想:子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据。更一般地说,其核心想法是要承认程序都会有问题,都需要被修改。

保护程序免遭非法输入数据的破坏:

1.检查所有源于外部的数据的值

当从文件、用户、网络或其他外部接口中获取数据时,应检查所获得的数据值,以确保它在允许的范围内。对于数值,要确保它在可接受的取值范围内;对于字符串,要确保其不过长。如果字符串代表的是某个特定范围内的数据,那么要确认其取值合乎用途,否则就应该拒绝接受。如果在开发过程中需要有安全的应用程序,还要格外注意那些狡猾的可能是攻击你的系统的数据,包括企图令缓冲区溢出的数据、注入的SQL命令、整数溢出以及传递给系统调用的数据,等等。

2.检查子程序所有输入参数的值

检查子程序输入参数的值,事实上和检查来源于外部的数据值一样,只不过数据时来自于其他子程序而非外部接口。

3.决定如何处理错误的输入数据见下面的讲解


断言:

断言是指在开发期间使用的、让程序在运行时进行自检的代码(通常是一个子程序或宏),它对于大型的复杂程序或可靠性要求极高的程序来说尤为有用。通过使用断言,可以快速排查出因修改代码或者别的原因,而弄进程序里的不匹配的接口假设和错误等。

正常情况下,并不希望用户看到产品代码中的断言信息;断言主要是用于开发和维护阶段。通常,断言只是在开发阶段被编译到目标代码中,而在生成产品代码时并不编译进去。在开发阶段,断言可以帮助查清相互矛盾的假定、预料之外的情况以及传给子程序的错误数据等。

使用断言的建议:

1.用错误处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况

2.避免把需要执行的代码放入断言中:如果把代码写在断言里,那么当关闭断言功能时,编译器很可能就把这些代码排除在外不执行了。最好将函数的返回值传递到一个临时变量中,然后让断言判断这个临时变量。

3.用断言来注解并验证前条件和后条件:

前条件是子程序或类的调用方代码在调用子程序或实例化对象之前要确保为真的属性。

后条件是子程序或类在执行结束后要确保为真的属性。后置条件是子程序或类对调用方代码所承担的责任。

4.对于高健壮性的代码,应该先使用断言再处理错误


如果变量都来源于系统外部,那么就应该用错误处理代码来检查和处理非法的数值,而不是使用断言。而如果变量的值是源于可信的系统内部,并且这段程序是基于这些值不会超出合法范围的假定而设计,使用断言则是非常合适的。


错误处理技术:

断言可以处理代码中不应该发生的错误,那么该如何处理那些预料中可能要发生的错误呢?

1.返回中立值:返回一个对程序没有危害的数据。(数值可以返回0,指针可以返回空

2.换用下一个正确的数据

3.返回与前次相同的数据

4.换用最接近的合法值(如某个数超过了某个界限,则返回界限的边界值

5.把告警信息记录到日志文件中

6.返回一个错误码

7.调用错误处理子程序或对象:优点在于可以把错误处理的职责集中,从而让调试工作更加简单,但是代价是整个程序都要知道这个集中点并与之紧密耦合。

8.当错误发生时显示出错误消息:这种方法可以把错误处理的开销减到最小,但是也有可能让用户界面中出现的信息散布到整个应用程序中。而且攻击者可能会利用错误信息来发现如何攻击这个系统。

9.用最妥当的方式在局部处理错误:这种方法给每个程序员很大的灵活度,但也带来了显著的风险,即系统的整体性能将无法满足对其正确性或可靠性的需求。

10.关闭程序:有些系统需要一旦检测到错误发生就会关闭。


健壮性与正确性:

正确性:意味着永不返回不准确的结果,哪怕不返回结果也比返回不准确的结果好。

健壮性:意味着要不断尝试采取某些措施,以保证软件可以持续地运转下去,哪怕有时做出一些不够准确的结果。


高层次设计对错误处理方式的影响:

应该在整个程序里采用一致的方式处理非法的参数。确定一种通用的处理错误参数的方法,是架构层次的设计决策,需要在那里的某个层次上解决。

除非已确立了一套不对系统调用进行错误检查的架构性知道建议,否则请在每个系统调用后检查错误码。一旦检测到错误,就记下错误代号和它的描述信息。


异常:

异常就是把代码中的错误或异常事件传递给调用方代码的一种特殊手段。如果在一个子程序中遇到了预料之外的情况,但不知道该如何处理的话,这就可以抛出一个异常。对出错的前因后果不甚了解的代码,可以把对控制权装交给系统中其他能更好解释错误并采取措施的部分。

建议:

1.用异常通知程序的其他部分,发生了不可忽略的错误:相比错误处理机制有可能会导致错误在不知不觉中向外扩散,而异常则消除了这种可能。

2.只在真正例外的情况下才抛出异常:异常需要做出一个取舍,一方面它是一种强大的用来处理预料之外情况的途径,另一方面程序的复杂度会因此增加。由于调用子程序的代码需要了解被调用代码中可能会抛出异常,因此异常弱化了封装性。

3.不能用异常来推卸责任:如果某种的错误情况可以在局部处理,那就应该在局部处理掉它。不要把本来可以在局部处理的错误当成一个未被捕获的异常抛出去。

4.避免在构造函数和析构函数中抛出异常,除非在同一地方把他们捕获:这会让异常规则马上就变得非常复杂。

5.在恰当抽象层次抛出异常:子程序应在其接口中展现出一只的抽象,类也是如此。抛出的异常也是程序接口的一部分,和其他具体的数据类型一样。

6.在异常消息中加入关于导致异常发生的全部信息:确保信息中的内容容易让人理解异常的原因。

7.避免使用空的catch语句:因为要是做了就要么就是try里的代码不对,因为它无故抛出了一个异常;要么就是catch中的代码不对,因为它没能处理一个有效的异常。

8.了解所用函数库可能抛出的异常

9.考虑创建一个集中的异常报告机制

10.把项目中对异常的使用标准化

11.考虑异常的替换方案:应该自始至终考虑各种各样的错误处理机制:在局部处理错误、使用错误码来传递错误、在日志文件中记录调试信息、关闭系统或其他的一些方式等。仅仅因为编程语言提供了异常处理机制而使用异常,是典型的”为用而用“;这也是典型的”在一种语言上编程“而非”深入一种语言去编程“的例子。


隔离程序,使之包容由错误造成的损害:

以防御式编程为目的而进行隔离的一种方法,是把某些接口选定为”安全“区域的边界。对穿越安全区域边界的数据进行合法性校验,并当数据非法时做出敏锐的反应。在类的层次上也可以做同样的处理方式。

在输入数据时将其转换为恰当的类型:因为程序在长时间传递类型不明的数据会增加程序的复杂度和崩溃的可能性。因此,应该在输入数据后立即将其转换到恰当的类型。


确定在产品中该保留多少防御式代码:

1.保留那些检查重错误的代码

2.去掉检查细微错误的代码

3.去掉可以导致程序硬性崩溃的代码

4.保留可以让程序稳妥地崩溃的代码

5.为你的技术支持人员记录错误信息

6.确认留在代码中的错误消息是友好的


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值