详细设计阶段设计出的模块质量如何呢?第5章中曾经讲述了软件设计的基本原理和概念,经过详细设计之后每个模块的内容都非常具体了,因此可以使用这些原理进一步仔细衡量它们的质量。但是,这种衡量毕竟只能是定性的,人们希望能进一步定量度量软件的性质。由于软件工程还是一门很年轻的学科,目前许多定量度量方法还处在研究过程中,本书将要介绍的程序复杂程度度量方法是其中比较熟悉的一种。
定量度量程序复杂程度的方法很有价值:把程序的复杂程度乘以适当常数即可估算出软件种错误的数量以及软件开发需要用的工作量,定量度量的结果可以用来比较两个不同的设计或两个不同算法的优劣;程序的定量的复杂程度可以作为模块规模的精确限度。
下面着重介绍使用得比较广泛的McCabe方法和Halstead方法。
1.流图
McCabe方法根据程序控制流的复杂程度定量度量程序的复杂程度,这样度量出的结果称为程序的环形复杂度。为了突出表示程序的控制流,人们通常使用流图(也称为程序图)。所谓流图实质上是“退化了的”程序流程图,它仅仅描绘程序的控制流程,完全不表现对数据的具体操作以及分支或循环的具体条件。
在流图中用圆表示结点,一个圆代表一条或多条语句。程序流程图中的一个顺序的处理框序列和一个菱形判定框,可以映射成流图中的一个结点。流图中的箭头线称为边,它和程序流程图中的箭头线类似,代表控制流。在流图中一条边必须终止于一个结点,即使这个结点并不代表任何语句(实际上相当于一个空语句)。由边和结点围成的面积称为区域,当计算区域数时应该包括图外部未被围起来的那个区域。
图5.11举例说明把程序流程图映射成流图的方法。
图 5.13把程序流程图映射成流图
(a)程序流程图; (b)流图
用任何方法表示的过程设计结果,都可以翻译成流图。图5.12是用PDL表示的处理过程及与之对应的流图。
当过程设计中包含复合条件时,生成流图的方法稍微复杂一些。所谓复合条件,就是在条件中包含了一个或多个布尔运算符(逻辑OR,AND,NAND,NOR)。在这种情况下,应该把复合条件分解为若干个简单条件,每个简单条件对应流图中一个结点。包含条件的节点称为判定节点,从每个判定结点引出两条或多条边。图5.13是由包含复合条件的PDL片断翻译成的流图。
图5.14 由PDL翻译成的流图
图5.15 由包含复合条件的PDL映射成的流图
2.计算环形复杂度的方法
环形复杂度定量度量程序的逻辑复杂度。由了描绘程序控制流的流图之后,可以用下述3种方法中的任何一种来计算环形复杂度。
(1)流图中的区域数等于环形复杂度。
(2)流图G的环形复杂度V(G)=E-N+2,其中,E是流图中边的条数,N是结点数。
(3)流图G的环形复杂度V(G)=P+1,其中,P是流图中判定结点的数目。
例如,使用上述任何一种方法,都可以计算出图5.16所示流图的环形复杂度为4。
3.环形复杂度的用途
程序的环形复杂度取决于程序控制流的复杂程度,即取决于程序结构的复杂程度。当程序内分支数或循环个数增加时,环形复杂度也随之增加,因此它是对测试难度的一种定量度量,也能对软件最终的可靠性给出某种预测。
McCabe研究大量程序后发现,环形复杂度高的程序往往是最困难、最容易出问题的程序。实践表明,模块规模以V(G)<=10为宜,也就是说,V(G)=10是模块的一个更科学更精确的上限。
Halstead方法是另一个著名的方法,它根据程序中运算符和操作数的总数来度量程序的复杂程度。
令N1为程序中运算符出现的总次数,N2为操作数出现的总次数,程序长度N定义为:
N=N1+N2
详细设计完成之后,可以知道程序中使用的不同运算符(包括关键字)的个数n1,以及不同操作数(变量和常数)的个数n2。Halstead给出预测程序长度的公式如下:
多次验证都表明,预测的长度H与实际长度N非常接近。 Halstead还给出了预测程序中包含错误的个数的公式如下:
有人曾对从300条到12000条语句范围内的程序核实了上述公式,发现预测的错误数与实际错误数相比误差在8%之内。