现代软件工程
上QQ阅读APP看书,第一时间看更新

4.6 模块的划分

通常,把设计阶段的工作分成两步,即概要设计(也称为总体设计或结构设计)及详细设计(也称为程序设计)。在概要设计阶段应着重解决实现需求的程序模块设计问题。这包括考虑如何把被开发的软件系统划分成若干个模块,并决定各模块的接口,即模块间的相互关系,以及模块之间传递的信息。详细设计则要决定每个模块内部的具体算法。在概要设计和详细设计完成以后,都要进行必要的阶段评审。其目的在于使设计中发生的问题能够及时发现并得到及时解决,而不致将其带到开发的后期,造成更大的危害。

常见的软件概要设计方法有以数据流图为基础构造模块结构的结构化设计(SD)方法,以数据结构为基础构造模块结构的Jackson方法,以信息隐蔽为原则构造模块结构的Parans方法,此外还有以数据结构为基础的LCP(Wanier)方法等。

4.6.1 软件结构

软件结构表示程序的系统结构,它以层次结构表示一种控制层次体系,它并不表示软件的具体过程(例如各个进程的顺序、判定和操作重复等)。

软件结构表示了软件元素(模块)之间的关系,例如调用关系、包含关系、从属关系和嵌套关系等。图4-6表示了一个软件结构。

978-7-111-52634-6-Chapter04-6.jpg

图4-6 软件的层次表示

1)深度:表示软件结构中的控制层数。例如图4-6是一个4层软件结构。深度往往能粗略地表示一个系统的大小和复杂程度,软件结构的深度和程序长度之间存在着某种对应关系。

2)宽度:是软件内同一层次上的模块总数。一般来说,结构的宽度越大,系统就越复杂。

3)扇出:是由一个模块直接控制的其他模块数的一种度量。例如图4-6中的模块A,B,C,D的扇出分别为3,2,1,2。扇出过大,表示模块过分复杂,需要控制和协调的下级模块太多。扇出的上限一般为5~9,平均一般为3或4。

4)扇入:表示有多少个上级模块直接控制一个给定的模块。例如图4-6中模块R和S的扇入分别为3和1。扇入过大,意味着共享该模块的上级模块数目多,这有一定的益处,但是决不能违背模块的独立性原则而片面追求高扇入。

4.6.2 模块划分的基本原则

“模块”实际上是一个已经得到广泛应用的概念,许多程序设计语言中的所谓子程序、分程序、过程或函数等实质上都具有模块的特性。这些特性包括模块的名称、输入和输出信息、功能、内部逻辑构造、可被调用,以及可调用其他模块等。

通常一个系统是由若干个子系统组成的;每个子系统又可分解成更小的子系统。在实际工作中,要求改变整个系统的结构的情况是极为少见的。所谓系统的修改,往往只是对某些子系统的某些细节做一些变动,也就是说,这些修改通常都是局部的。然而,由于各子系统之间是相互关联的,对其中某个子系统的修改,可能会通过这些相互关系而影响到其他子系统,乃至波及整个系统。这样,就可能会造成“牵一发而动全身”,越改越乱,甚至会出现使修改工作无法进行下去的局面。由此可见,要使系统易于修改,必须使每一个修改都尽可能地局部化。这就要求在进行系统的模块划分时,要使模块的内部联系尽可能地强,而模块间的外部联系尽可能地弱,即尽可能地提高模块的相对独立性。这样,在对其中某一个模块进行修改时,它所造成的影响只局限在本模块范围内,而不至于波及整个系统。

用来评价模块结构质量的具体标准是模块的耦合度和内聚度。

4.6.3 内聚度

内聚度又称模块强度,是指一个模块内部各成分之间的联系,内聚度高,则模块的相对独立性高。按内聚度由低到高的顺序,可划分为以下7个等级。

1)偶然内聚(偶发强度)。即模块内各成分间无实质性的联系,只是偶然地被凑合到一起。这种模块的内部联系毫无疑问是最弱的。

2)逻辑内聚(相似强度)。将几个逻辑上相似(实际上并无必然联系)的功能放入一个模块而形成块内联系。这种模块比偶然内聚要好一些,但由于只是形式上的相似,实质上彼此并无必然联系,因此其内部的联系仍然是较弱的。例如,将各种错误信息的处理集中起来,定义为一个模块,但这些错误信息的处理彼此间没有丝毫关系。这样的模块就是具有逻辑内聚的模块。显然,由于它把所有形式上相似(都是错误信息处理)而实质上毫无关系的处理放在一起,它和其他模块之间的联结关系就必然相当复杂,因此修改维护会相当困难。

3)时间内聚(时间强度)。将若干在同一个时间带内进行的工作集中在一起所形成的模块,但这些工作彼此间毫无关系。这种模块具有的强度比逻辑内聚模块略好些,但由于模块的各组成元素之间仍然是彼此无关的,因此,其内部联系也还是较弱的,可变更性能也很差。例如,初始化处理模块就是这样一种模块。由于它将彼此间并无关系的初始化处理工作集中到一起,模块本身的强度较弱,而且它和其他模块之间的联结范围也较宽,因此,修改维护也很困难。

4)过程内聚(过程强度)。模块内的各成分是相关的,且必须以特定的次序执行,中间不能穿插其他工作。例如,先输入x,再决定y,……和时间内聚模块相比,过程内聚强调严格的执行次序,而时间内聚强调在同一个时间带内执行,对执行次序则并无要求。显然,过程内聚比时间内聚模块的内部紧密程度要高些。

5)通信内聚(通信强度)。指模块中的各成分引用相同输入数据和(或)产生相同的输出数据,但各成分的执行次序可以是任意的。这样组成的模块就是具有通信内聚。例如模块中的若干个处理功能读取同一个文件的数据就是这样的一种模块。它比过程内聚模块具有更紧密的内部联系。

6)顺序内聚(顺序强度)。如果模块内各成分间具有这样的特征:前一成分所产生的输出是另一成分的输入,那么这种模块就是具有顺序内聚的模块。这种模块的内部紧密程度比以上各种模块都要高。

顺序内聚与过程内聚的区别在于:前者强调的是数据的顺序,而后者强调的是加工处理的先后。

7)功能内聚(功能强度)。模块内所有的成分属于一个整体,为完成同一个功能而存在,那么这种模块就称为具有功能内聚的模块。由于这种模块只执行一个功能,因而其内部紧密程度最高,具有“黑盒”的作用。

4.6.4 耦合度

耦合度又称模块结合度,是模块结构中各模块之间相互联系的一种度量,是模块独立性的直接尺度。耦合度越弱,就意味着模块的独立性越高,则模块间的相互影响就越小。耦合度的强弱可以从以下4个方面来衡量。

1)数据结合度。两个模块之间的通信信息是若干个数据,这种联结关系就是具有数据结合度的联结关系。由于两者之间没有控制信号的交换,因此相互间的影响最小。例如报表生成与报表打印两模块间的关系。

2)控制结合度。两个模块之间传递的信息中含有控制信号,使得一个模块控制了另一个模块的内部逻辑,这种联结就是具有控制结合度的联结关系。这种结合度下的模块间的相互影响显然要比数据结合度下的模块间相互影响要大。

3)公共结合度。两个模块通过对公共数据的访问建立联系,这样的联结关系就是具有公共结合度的联结关系。这是一种不太好的联结关系,因为若干个模块共用某几个数据,就使联结关系复杂起来,增加了模块修改的难度。

4)内容结合度。一个模块不经过调用关系直接使用另一个模块的内容,这种联结关系称为具有内容结合度的联结关系。例如模块A用绝对地址直接使用模块B的数据就是一例。这是最坏的一种联结形式,给模块维护带来很大困难。比如,假设模块B进行了某种修改,使模块A要使用的数据发生了改变,那么当模块A运行时势必发生错误,但从模块A本身是查不出这种错误发生的原因的,只能通过模块B才能检查出来。

4.6.5 高内聚和低耦合

内聚和耦合是密切相关的,模块内的高内聚往往意味着模块间的低耦合。这也就是说,内聚度(模块强度)和耦合度(模块结合度)要说明的是同一内容,即模块的独立性,它们是衡量这种独立性的两个不同方面。

在软件设计中,应该追求高内聚、低耦合(即块内要紧,块间要松)的系统,在这样的系统中,可以研究、测试或维护任何一个模块,而不需要对系统的其他模块有很多了解。此外,由于模块间联系简单,发生在某处的错误传播到整个系统的可能性很小。因此,模块间的耦合程度强烈地影响着系统的可理解性、可测试性、可靠性和可维护性等性能。当然,在实际情况中,要做到所划分的模块都具有功能内聚,模块间联结都具有数据结合度是很困难的,只是要求尽可能地提高模块的内聚,减小模块的耦合。