模块独立(高内聚,低耦合)---总体设计之设计原理

模块独立的概念是模块化、抽象、信息隐藏和局部化概念的直接结果。

开发具有独立功能而且和其他模块之间没有过多的相互作用的模块,就可以做到模块独立。换句话说,希望这样的软件结构,使得每个模块完成一个相对独立的特定子功能,并且和其他模块之间的关系很简单。

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

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


1:耦合

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

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

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

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

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

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

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

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

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

l一个模块往公共环境送数据,另一个模块从公共环境取数据。这是数据耦合的一种形式,是比较松散的耦合。

l两个模块都往公共环境送数据又从里面取数据,这种耦合比较紧密,介于数据耦合和控制耦合之间。


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

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


l一个模块访问另一个模块的内部数据;

l一个模块不通过正常入口而转到另一个模块的内部;

l两个模块有一部分程序代码重叠;

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

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

总之,耦合是影响软件复杂程度的一个重要因素。应该采取下述设计原则:

尽量使用数据耦合,少用控制耦合和特征耦合,限制公共环境的范围,完全不用内容耦合。

2内聚

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

内聚按模块内各个元素彼此结合的紧密程度通常分为高内聚、中内聚和低内聚3类。设计时应该力求做到高内聚,通常中内聚也是可以采用的,而且效果和高内聚相差不多。

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

低内聚有如下几类:如果一个模块完成一组任务,这些任务彼此间即使有关系,关系也是很松散的,就叫做偶然内聚。有时在写完一个程序之后,发现一组语句在两处或多处出现,于是把这些语句作为一个模块以节省内存,这样就出现了偶然内聚的模块。如果一个模块完成的任务在逻辑上属于相同或相似的一类(例如,一个模块产生各种类型的全部输出),称为逻辑内聚。如果一个模块包含的任务必须在同一段时间内执行(例如,模块完成各种初始化工作),就叫时间内聚

在偶然内聚的模块中,各种元素之间没有实质性联系,很可能在一种应用场合需要修改这个模块,而在另一种场合不允许这种修改,从而陷入困境。事实上,偶然内聚的模块出现修改错误的概率比其他类型的模块高的多。

在逻辑内聚的模块中,不同功能混在一起,合用部分程序代码,即使局部功能的修改也会影响全局。因此,这类模块的修改也比较困难。

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

中内聚主要有两类:如果一个模块内的处理元素是相关的,而且必须以特定次序执行,则称为过程内聚。使用程序流程图作为工具设计软件时,常常通过研究流程图确定模块的划分,这样得到的往往是过程内聚的模块。如果模块中所有元素都使用同一个输入或产生同一个输出数据,则称为通信内聚。

高内聚也有两类:如果一个模块内的处理元素和同一个功能密切相关,而且这些处理必须顺序执行(通常一个处理元素的输出数据作为下一个处理元素的输入数据),则成为顺序内聚。根据数据流图划分模块时,通常得到顺序内聚的模块,这种模块彼此间的连接往往比较简单。如果模块内所有处理元素属于一个整体,完成一个单一的功能,则称为功能内聚。功能内聚是最高程度的内聚。

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


功能内聚 10

时间内聚 3

顺序内聚 9

逻辑内聚 1

通信内聚 7

偶然内聚 0

过程内聚 5



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


摘自:《软件工程》张海藩编著