1
 软件工程
1.6.1.4 4.1.4 模块独立性

4.1.4 模块独立性

模块独立的概念是模块化、抽象、信息隐藏和局部化概念的直接结果。开发具有独立功能而且与其他模块之间没有过多的相互作用的模块,就可以做到模块独立。换句话说,这样设计的软件结构,使得每个模块完成一个相对独立的特定子功能,并且与其他模块之间的关系很简单。

为什么模块独立性很重要呢?主要有两条理由:第一,有效的模块化(具有独立的模块)软件比较容易开发出来,当许多人分工合作开发同一个软件时,这个优点尤其重要;第二,独立的模块比较容易测试和维护。相对来说,修改、设计程序需要的工作量比较小,错误传播范围小,需要扩充功能时能够“插入”模块。总之,模块独立是好的设计的关键,而设计又是决定软件质量的关键环节。

模块的独立程度有两个定性标准度量,即耦合和内聚。耦合衡量不同模块彼此间互相依赖(连接)的紧密程度;内聚衡量一个模块内部各个元素彼此结合的紧密程度。以下分别详细阐述。

1.耦合

耦合是对一个软件结构内不同模块之间互连程度的度量。耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点,以及通过接口的数据。

在软件设计中应该追求尽可能松散耦合的系统。在这样的系统中可以研究、测试或维护任何一个模块,而不需要对系统的其他模块有很多了解。此外,由于模块间联系简单,所以发生在一处的错误传播到整个系统的可能性就很小。因此,模块间耦合程度强烈影响着系统的可理解性、可测试性、可靠性和可维护性。

怎样具体区分模块间耦合程度的强弱呢?

如果两个模块中的每一个模块都能独立地工作而不需要另一个模块的存在,那么它们彼此完全独立,这意味着模块间无任何连接,耦合程度最低。但是,在一个软件系统中不可能所有模块之间都没有任何连接。

如果两个模块彼此间通过参数交换信息,而且交换的信息仅仅是数据,那么这种耦合称为数据耦合。如果传递的信息中有控制信息(尽管有时这种控制信息以数据的形式出现),那么这种耦合称为控制耦合。

数据耦合是低耦合。系统中至少必须存在这种耦合,因为只有当某些模块的输出数据作为另一些模块的输入数据时,系统才能完成有价值的功能。一般说来,一个系统内可以只包含数据耦合。控制耦合是中等程度的耦合,它增加了系统的复杂程度。控制耦合往往是多余的,在把模块适当分解之后通常可以用数据耦合代替它。

如果被调用的模块需要使用作为参数传递进来的数据结构中的所有元素,那么,把整个数据结构作为参数传递就是完全正确的。但是,当把整个数据结构作为参数传递而被调用的模块只需使用其中一部分数据元素时,就出现了特征耦合。在这种情况下,被调用的模块可以使用的数据多于它确实需要的数据,这将导致对数据的访问失去控制,从而给计算机犯罪提供了机会。

当两个或多个模块通过一个公共数据环境相互作用时,它们之间的耦合称为公共环境耦合。公共环境可以是全程变量、共享的通信区、内存的公共覆盖区、任何存储介质上的文件、物理设备等。

公共环境耦合的复杂程度随耦合的模块个数的变化而变化,当耦合的模块个数增加时,其复杂程度显著增加。如果只有两个模块有公共环境耦合,那么这种耦合有下面两种可能。

(1)一个模块向公共环境传送数据,另一个模块从公共环境读取数据。这是数据耦合的一种形式,是比较松散的耦合。

(2)两个模块都向公共环境传送数据和读取数据,这种耦合比较紧密,介于数据耦合和控制耦合之间。

如果两个模块共享的数据很多,都通过参数传递可能很不方便,这时可以利用公共环境耦合。

最高程度的耦合是内容耦合。如果出现下列情况之一,两个模块间就发生了内容耦合。

(1)一个模块访问另一个模块的内部数据。

(2)一个模块不通过正常入口而转到另一个模块的内部。

(3)两个模块有一部分程序代码重叠(只可能出现在汇编程序中)。

(4)一个模块有多个入口(这意味着一个模块有几种功能)。

应该坚决避免使用内容耦合。事实上,许多高级程序设计语言已经设计成不允许在程序中出现任何形式的内容耦合。

总之,耦合是影响软件复杂程度的一个重要因素。应该采取下述设计原则:尽量使用数据耦合,少用控制耦合和特征耦合,限制公共环境耦合的范围,完全不用内容耦合。

2.内聚

内聚标志着一个模块内各个元素彼此结合的紧密程度,它是信息隐藏和局部化概念的自然扩展。简单地说,理想内聚的模块只做一件事情。

设计时应该力求做到高内聚,通常中等程度的内聚也是可以采用的,而且效果和高内聚相差不多。

内聚和耦合是密切相关的,模块内的高内聚往往意味着模块间的松耦合。内聚和耦合都是进行模块化设计的有力工具,但是实践表明,应该把更多注意力集中到提高模块的内聚程度上。

1)低内聚的类别

(1)如果一个模块完成一组任务,这些任务彼此间即使有关系,其关系也是很松散的,这称为偶然内聚。有时在写完一个程序之后,发现一组语句在两处或多处出现,于是把这些语句作为一个模块以节省内存,这样就出现了偶然内聚的模块。在偶然内聚的模块中,各种元素之间没有实质性联系,很可能在一种应用场合需要修改这个模块,而在另一种应用场合又不允许这种修改,从而陷入困境。事实上,偶然内聚的模块出现修改错误的概率比其他类型模块的高得多。

(2)如果一个模块完成的任务在逻辑上属于相同或相似的一类(如一个模块产生各种类型的全部输出),则称为逻辑内聚。在逻辑内聚的模块中,不同功能的任务混在一起,合用部分程序代码,即使是局部功能的修改有时也会影响全局。因此,这类模块的修改也比较困难。

(3)如果一个模块包含的任务必须在同一段时间内执行(例如,模块完成各种初始化工作),就称为时间内聚。

时间关系在一定程度上反映了程序的某些实质,所以时间内聚比逻辑内聚好一些。

2)中内聚的类别

(1)如果一个模块内的处理元素是相关的,而且必须以特定次序执行,则称为过程内聚。使用程序流程图作为工具设计软件时,常常通过研究流程图确定模块的划分,这样得到的往往是过程内聚的模块。

(2)如果模块中所有元素都使用同一个输入数据和(或)产生同一个输出数据,则称为通信内聚。

3)高内聚的类别

高内聚包含顺序内聚和功能内聚。

(1)如果一个模块内的处理元素和同一个功能密切相关,而且这些处理必须顺序执行(通常一个处理元素的输出数据作为下一个处理元素的输入数据),则称为顺序内聚。根据数据流图划分模块时,通常得到顺序内聚的模块,这种模块彼此间的连接往往比较简单。

(2)如果模块内所有处理元素属于一个整体,完成一个单一的功能,则称为功能内聚。功能内聚是最高程度的内聚。

耦合和内聚的概念是由Constantine、Yourdon、Myers和Stevens等人提出来的。按照他们的观点,如果给上述7种内聚的优劣评分,将得到如下结果:

功能内聚10分    时间内聚3分

顺序内聚9分    逻辑内聚1分

通信内聚7分    偶然内聚0分

过程内聚5分

事实上,没有必要精确确定内聚的级别。重要的是设计时力争做到高内聚,并且能够辨认出低内聚的模块,有能力通过修改设计提高模块的内聚程度并且降低模块间的耦合程度,就可获得较高的模块独立性。