读《代码大全2》笔记:防御式编程

来源:互联网 发布:淘宝描述相符怎么提高 编辑:程序博客网 时间:2024/04/29 14:06

防御式驾驶:你永远不能确定另外一位司机将要做什么。你要承担其保护自己的责任,哪怕是其他司机犯的错误。

防御式编程的主要思想:子程序应该不因传入错误数据而被破坏,哪怕是有其他子程序产生的错误数据。

 

8.1 保护数据免遭非法输入数据的破坏

1、检查所有来源于子程序外部的数据的值。

2、检查所有输入参数的值。

3、决定如何处理错误的输入数据库。

 

8.2 断言

断言是指在开发期间使用的、让程序在运行时进行自检的代码。断言只在开发和维护阶段使用。

建立自己的断言机制

使用断言的建议:

1、  用错误处理代码来处理预期会发生的状况,用断言来处理绝对不应该发生的状况。断言是用来检查用于不该发生的情况,而错误处理代码是用来检查不太可能经常发生的非正常情况,这些情况在写代码的时候就可以预料到的,且产品代码中也要处理这种情况。断言用来检查程序中的bug,如果断言触发,则肯定是程序中存在bug。错误处理代码对反常情况做成反映。可以把断言看成主动的可执行的注释。错误处理代码处理系统外部的异常数据。如果数据来自可信的内部,则可以使用断言来检查。

2、  避免把需要执行的代码放入断言中。

3、  用断言来注释并验证前条件和后条件。前条件调用方代码在调用子程序或类之前要保证的条件;后条件是指被调用方代码执行结束后要保证的条件。可以用断言来检查前条件和后条件。

4、  对于高健壮性的代码,应该先使用断言再处理错误。同时使用两者。

 

8.3 错误处理技术

断言用于处理代码中不应该出现的错误。错误处理技术对反常情况做出响应。

1、  返回中立值    

2、  换用下一个正确的数据。

3、  返回与前次相同的数据。

4、  换用最接近的合法值。

5、  把警告信息记录日志中。

6、  返回一个错误码。

7、  调用错误处理子程序或对象。

8、当错误发生时显示出错消息

9、用最妥当的方式在局部处理错误

10、关闭程序

 

健壮性与正确性:

正确性意味着永远不返回不准确的结果。健壮性意味着要不断尝试采取某些措施,以保证软件能够持续的运行下去。我们的软件更倾向于健壮性。

 

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

在软件架构的时候,就要设计一个统一的错误处理决策。一般情况下,函数返回错误,而调用者则要根据返回值进行处理。

防御式编程的全部的重点就在于防御那些你未曾预料到的错误。

 

8.4 异常

 

1、  用异常通知程序的其他部分,发送了不可忽略的错误。异常有一种无法被忽略的通知机制。

2、  只有真正例外的情况下才抛出异常。和断言类似,处理不可能发生的情况。

3、  不能用异常来推卸责任。应该在局部处理的,不要抛出来。

4、  避免在构造和析构函数中抛出异常。

5、  在恰当的抽象层次抛出异常。把抛出的异常认为是接口的一部分。要符合接口的抽象。抛出的异常不能破坏封装,不能破坏抽象,暴露细节。

6、  在异常消息中加入关于导致异常发生的全部信息。

7、  避免是空的catch语句。

8、  了解所有的函数库抛出的异常。

9、  考虑建立一个集中的异常报告机制。

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

11、考虑异常的替代方案。

 

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

将某些接口设定为安全边界,对穿越边界的数据进行安全的检查。

让软件的某些部分处理“不干净的”数据,让另外一部分处理干净的数据,即可让大部分的代码无须在承担检查数据的责任。

在类的层次采用此方法:类的公用方法假定数据是不安全的,私用方法假定数据是安全的。

将输入数据转化为适当的类型。

隔栏使用的是错误处理错误,处理错误的数据。隔栏内部则使用断言,检查程序错误,而非数据错误。

隔栏的使用体现了架构层次上如何处理错误。

 

8.6 辅助调试的代码

用辅助代码来辅助调试程序。

尽早引入辅助调试的代码,包括对通道检查,通道状态检查。资源使用情况等。

 

采用进攻式编程:在开发节点让他显现出来,而在产品运行时,让它能够自我恢复。

1、  确保断言能使程序终止运行。

2、  完全填充分配到的所有错误。

3、  完全填充分配到的所有的文件或流。

4、  确保每一个case语句中的default分支或else分支都能产生严重的错误。

5、  在删除一个对象前把它填满垃圾数据。

 

计划移除辅助调试代码:

1、  使用ant或make工具。

2、  使用内置的与处理器。

3、  编写自己的预处理器。

4、  使用调试存根。

 

8.7 确定在代码中保存多少防御式代码

1、  保留那些检查重要错误的代码。

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

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

4、  保留可以稳妥的崩溃的代码。

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

6、  确保代码中的错误信息是友好的。

 

 

CHECKLIST: Defensive Programming

核对表:防御式编程

一般事宜

1、  子程序是否保护自己免遭有害输入数据的破坏?

2、  你用断言来说明编程假定了吗?其中也包括前条件和后条件了吗?

3、  断言是否只是用来说明从不该发生的情况(如果一发生,肯定是程序的BUG)?

4、  你是否在架构或高层设计中规定了一组特定的错误处理技术?

5、  你是否在架构或高层设计中规定了是让错误处理更倾向于健壮性还是正确性?

6、  你是否建立隔栏来阻止错误可能造成的破坏?是否减少了其他需要关注错误处理的代码的数量?

7、  代码中用到了辅助调试的代码了吗?

8、  如果需要添加或禁止添加辅助手段的话,是否无须大动干戈?

9、  在防御式编程中引入的代码量是否适宜——既不过多,又不过少?

10、              你在开发阶段是否采用了进攻式编程来是错误难以忽视?

 

异常

1、  你在项目中定义了一套标准化的异常处理方案吗?

2、  是否考虑异常之外的其他替代方案?

3、  如果可能的话,是否在局部处理错误,而不是把异常抛出到外部?

4、  代码中是否避免在构造函数和异构函数中抛出异常?

5、  所有的异常是否都与抛出它们的子程序处于同一抽象层次?

6、  每个异常是否都包含了异常发生的背景信息?

7、  代码中是否没有使用空的catch语句?

 

安全事宜

1、  检查有害输入数据的代码是否也检查了故意的缓冲区溢出,SQL注入,HTML注入,整数溢出以及其他的恶意输入数据?

2、  是否检查了所有的错误返回码?

3、  是否捕获了所有的异常?

4、  出错消息中是否避免出现有助于攻击者攻入系统的消息?

 

本章要点

1、  最终产品代码对错误的处理比“垃圾进,垃圾出”要复杂的多。

2、  防御式编程技术可以让错误更容易发现,更容易修改,并减少错误对产品代码的破坏。

3、  断言可以帮助人尽早的发现错误,尤其是在大型系统和高可靠性的系统中,以及快速变化的代码中。

4、关于如何处理错误输入的决策是一项关键的错误处理决策,也是一项关键的高层设计决策。

5、异常提供了一种与代码正常流程角度不同的错误处理手段。如果留心异常,它可以成为程序员们的知识工具箱中的一项有益补充,同时也应该在异常和其他错误处理手段之间进行权衡比较。

6、针对产品代码的限制并不适用于开发中的软件。你可以利用这一优势在开发中添加有助于更快的排查错误的代码。