防御式编程——一种提高软件质量技术的辅助手段

一、概念

主要思想:子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据。直白些,就是要对传入的数据做有效性校验。
核心思想:要承认程序都会有问题,都要被修改。

二、预防非法输入的数据

什么是好的程序?

  1. 不管输入的数据是什么都不会产生并输出垃圾数据
  2. 要做到“垃圾进,什么都不输出”、“垃圾进来,出去是错误提示”或“不许垃圾进来”

处理进来垃圾的方法

  1. 检查所有来源于外部的数据的值
  2. 检查子程序所有输入参数的值
  3. 决定如何处理错误的输入数据

三、断言

什么是断言
 
使用场景
【前提】用于处理代码中不应发生的错误。

  1. 用于开发和维护阶段
  2. 只在开发阶段被编译到目标文件中,不编进生成的产品代码中

建立自己的断言机制

#ifdef DEBUG
    #define assert(expr, message) \
    do { \
        if (!expr) { \
            ERR_LOG("Assertion failed: %s, %s", #expr, message); \
            exit(EXIT_FAILURE);
        }\
    }
#else
    #define assert(expr, message)  (static_cast<void> (0))
#endif

C++标准库支持的断言

  1. C++11之前
    函数签名:
#include <assert.h>   
void assert( int expression );

作用: 计算表达式 expression,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用abort来终止程序运行。
2. C++11编译期断言static_assert
static_assert是C++11标准引入的一个新关键字,用于在编译期做静态断言。它需要两个参数,第一个是一个可以在编译期返回bool值的常量表达式,第二个是一个字符串常量,用于当断言失败时编译器输出用。

使用断言的建议
1. 用错误处理代码处理预期会发生的状况,用断言来处理绝不应该发生的状况;
2. 避免把需要执行的代码放到断言中;
3. 用断言来注解并验证前条件和后条件;
4. 对于高健壮性的代码,应该先使用断言再处理错误。

四、错误处理技术

用于处理预料中可能要发生的错误
可用的技术手段

  1. 返回中立值 出现错误数据,可以继续执行,并简单返回一个没有危害的值
  2. 换用下一个正确的数据
  3. 返回于前次相同的数据
  4. 换用最接近的合法值
  5. 把警告信息记录到日志文件中
  6. 返回错误码
  7. 调用错误处理子程序或对象
  8. 当错误发生时显示错误信息
  9. 用最妥当的方式在局部处理错误
  10. 关闭程序
    健壮性和正确性
    【正确性】子程序永远不返回不准确的结果。
    【健壮性】子程序要不断尝试采取某些措施,以保证软件可以持续地运转下去,哪怕有时会做出一些不够准确的结果。
    这两种特性不同软件应用场景,追求不一样。比如,人身安全相关的场景更多的追求正确性。
    错误处理需要高层设计
    整个程序应该采用一致的方式处理非法参数,确定一种通用的处理错误的方法。

五、异常

概念
 
谨慎使用异常
 
【建议】

  1. 用异常通知程序的其他部分,发生了不可忽略的错误
  2. 只有正在例外的情况下才抛出异常
  3. 不能用异常来推卸责任,能局部处理的错误,就该局部处理掉
  4. 避免在构造函数和析构函数中抛出异常,除非你在同一地方把它们捕获
  5. 在恰当的抽象层次抛出异常
  6. 在异常消息中加入关于导致异常发生的全部信息
  7. 避免使用空的catch语句
  8. 了解所有函数库可能抛出的异常
  9. 考虑创建一个集中的异常报告机制
  10. 把项目中对异常的使用标准化
  11. 考虑异常的替代方案
    【注】异常会弱化封装性,增加代码复杂度。

六、容损策略之划边界

  1. 建立防火窗进行隔离,对穿越安全区域的数据进行合法校验,并对数据非法时做出敏锐的反应。
  2. 在输入数据时将其转换为恰当的类型

七、辅助调试的代码

不要自动地把产品版的限制强加于开发板之上
应该在开发期间牺牲一些速度和对资源的使用,来换取一些可以更顺畅的内置工具。
尽早引入辅助调试代码
采用进攻式编程

  • 何谓进攻式编程?
    在开发阶段让异常显现出来,而在产品代码运行时异常能够自动恢复。
  • 进攻式编程的方法
     
    计划移除调试辅助的代码
    商用软件,不应遗留调试代码,以免软件体积变大,运行效率底下。
  1. 使用类似ant和make这样的版本控制工具和make工具
  2. 使用内置的预处理器
  3. 编写你自己的预处理器
  4. 采用mock来模拟外部对象,并把这些保留起来,以便开发和发布来回切换

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

应该保留的代码的建议

  1. 保留那些检查重要错误的代码
  2. 去掉检查细微错误的代码(手段:版本控制、预编译器开关等)
  3. 去掉可以导致程序硬性崩溃的代码
  4. 保留可以让程序稳妥地崩溃的代码
  5. 为你的技术支持人员记录错误信息
  6. 确认留在代码中的错误信息是友好的

九、你的态度

不要过度防御,防御性代码会带来复杂度,也有可能引入新的bug。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值