面向正确性和健壮性的软件构造学习概要(上)

2022/6/8 23:20:07

本文主要是介绍面向正确性和健壮性的软件构造学习概要(上),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Outline

1.健壮性和正确性;

2.关于健壮性和正确性的测度;

3.Java中的Error和Exception;

4.处理异常;

5.断言;

6.防御式编程;

7.Debug的一些工具;

2022-06-08 20:56:50

 


 


 

1.健壮性和正确性

可靠性=健壮性+正确性

健壮性:系统在不正常输入或不正常外部环境下仍能够表现正常的程度。(尽可能保持软件运行而不是总是退出)

面向健壮性的编程:处理未期望的行为和错误终止;即使终止执行,也要准确的向用户展示全面的错误信息;错误信息有助于进行debug。

面向健壮性的编程的原则:封闭实现细节,限定 用户的恶意行为;考虑极端情况,没有“不可能”。

Postel’s Law:对别人宽容点,对自己狠一点。程序员应总是假定用户恶意、假定自己的代码可能失败;把用户想象成“白痴”,可能输入任何东西。

正确性:程序按照spec加以执行的能力,是最重要的质量指标。(永不给用户错误的结果)

正确性和健壮性的对比:

  正确性倾向于直接报错(error),健壮性则倾向于容错(fault-tolerance)。

  正确性让开发者变得更容易:用户输入错误,直接结束(不满足precondition的调用)。

  健壮性让用户变得更容易:出错也可以容忍,程序内部已有容错机制。

  对外的接口,倾向于健壮;对内的实现,倾向于正确。(与后文中提到的Exception和Assertions的使用场景类似)

  以几个例子来说明一下二者的区别:

 

 

提升程序健壮性和正确性的一般步骤:

 

  1. 利用assertions、代码评审、防御性编程、exception等编写代码;
  2. 观察程序运行错误后的状态(例如Memory dump、stack traces、execution logs);
  3. 确认潜在错误(Debug、局部化bug);
  4. 修改bug。

 

用一张我认为很好的图概括一下:

 

 

 


 

 

 

 2.关于健壮性和正确性的测度

外部观察角度MTBF:平均故障间隔时间(平均无故障运行时间),是指相邻两次故障之间的平均工作时间。即总的运行时间/总的故障次数。

MTBF的计算依赖于对系统failure的定义:对于可修复系统,是指导致系统不可使用的failure,出现问题,但系统仍可运行,则不视为failure。

同理还有故障前平均时间MTTF,它是用来描述不可修复系统的故障前平均时间。

下图描述的是可修复系统的MTBF(注意可修复系统没有MTTF):两个红色状态的间隔时间即为MTBF。

内部观察角度残余缺陷率:每千行代码中遗留的bug的数量。

 

像一般企业的代码残余缺陷率为1 - 10 defects/kloc,而像NASA这样的用于科研航空机构的代码残余缺陷率低至0.01 - 0.1 defects/kloc。

 

另外还可以用Halstead Volume来描述,具体内容之前在面向可维护性的软件构造中提到过,这里不再赘述。

 

 

 

 


 

3.Java中的Error和Exception

Java中所有的Error和Exception都是继承自Throwable类,记住下面这张图即可:图中红色虚线圈起来的一部分包括所有的Error和RuntimeException都属于Unchecked Exception,其余异常都属于checked Exception(编译时异常),下一节中会具体讲到对二者的区别对待。

 

 

Error通常是指内部错误,程序员通常无能为力,一旦其发生,应该想办法让程序优雅的结束。所以我们不要抛出Error对象(即使抛出了也是毫无意义的,程序员不能让内部错误恢复);异常则是你自己程序导致的问题,可以捕获、可以处理。

Error的分类:用户输入错误、设备错误、物理限制。一些典型的Error有OutOfMemoryError、StackOverflowError、InternalError、NoClassDefFoundError。

 

 


 

 *.关于我在Lab2程序编写时碰到的一个RuntimeException的思考与回味:

当时在编写Lab2时,对一个Map进行了迭代器遍历,第一遍的时候程序没有任何报错,一切运行也很正常;但是在我第二次运行时,程序莫名其妙的报了一个NullPointerException。当时还没有学到这一章节的内容,我便去搜索引擎上查找相关原因,这个运行时异常是由程序对一个空的(null)对象解引用造成的,但是奇怪的是这个Map在我的程序中不可能有一个key指向一个空的对象啊(这里的Map存放的是当前单词在语料库生成图中的出边集(目标点和边权值),与这个单词没有连接的单词也不会出现在Map中,所以更不会有null出现)。在学习完这一章节的知识之后,我恍然大悟,首先这个异常属于Unchecked Exception,所以静态检查和编译都通过了,运行的时候才报错;至于后面为什么不会解引用null也会出现这个异常,我的推测是IDE并不知道我们的具体代码实现,它只清楚在当前这个Map的迭代遍历中可能会出现NullPointerException,所以负责任地提醒我们编写的代码存在着漏洞,这样的处理百利而无一害。但是我当时自然而然地将NullPointerException当做普通的编译时异常try catch了一下,运行时也没有报错了,说明源代码确实也不会触发NullPointerException,只是IDE细致入微的考虑。放现在来看,这并不是一个好的处理,Unchecked Exception最好的解决办法是重写代码,而不是try catch和throw。

下面是我当时的代码:现在看来,最好在对value解引用之前判断一下其是否为空,然后再去操作,这样就不会触发异常了。

感觉这个试错也是学习中的一大乐趣吧,学以致用才能更好地把握知识。

 



这篇关于面向正确性和健壮性的软件构造学习概要(上)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程