高内聚低耦合

软件工程学习笔记之细说高内聚低耦合

Posted by LuJiangBo on September 10, 2017

是什么

按照百度百科的解释,高内聚低耦合的定义是:

高内聚低耦合,是软件工程中的概念,是判断设计好坏的标准,主要是面向对象的设计,主要是看类的内聚性是否高,耦合度是否低。

当然,单纯看定义,我们并不能理解什么叫高内聚低耦合,下面我会分别解释下什么叫内聚,什么叫耦合,以及为什么要这样做。

耦合

定义

耦合是指不同的模块之间相互依赖程度的度量。高耦合(紧密耦合)是指两个模块之间存在着很强的依赖;反之低耦合(松散耦合)是指两个模块之间存在一定依赖;无耦合就是指模块之间根本没有任何联系。 高内聚低耦合 上图1-1从左至右分别展示了3种耦合关系。

原因

产生耦合的主要因素有一下几点:

  • 一个模块对另一个模块的引用,例如:模块A调用了模块B,模块B调用了模块C,那么ABC3者就构成了依赖关系。
  • 一个模块向另一个模块传递数据,例如:模块A为了完成其功能需要模块B向其传递一组数据。
  • 一个模块对另一个模块施加控制,例如:模块A给模块B传递一个控制信号,模块B在执行操作时依赖于控制信号的值。

分类

基于上面3种原因, 存在下面几种模块间耦合类型(按从强到弱依次列出):

  • 内容耦合:当一个模块直接修改或操作另一个模块的数据,或者一个模块不通过正常入口转入到另一个模块时,这样的耦合被称为内容耦合。
  • 公共耦合:两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。
  • 控制耦合: 一个模块通过接口向另一个模块传递一个控制信号,接收信号的模块根据信号值进行适当的动作,这种耦合称为控制耦合。
  • 标记耦合:若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块BC之间存在一个标记耦合。
  • 数据耦合:模块之间通过参数来传递数据,则称为数据耦合。

内聚

定义

内聚是指一个模块内部各成分之间相互关联程度的一个度量。 高内聚指一个模块中各个部分之间存在着很强的依赖;低内聚是指一个模块中各个部分之间存在较少的依赖。

分类

下面是几种常见的内聚类型(按从低到高依次列出):

  • 偶然内聚:一个模块的各个成分之间基本不存在任何联系,则称为偶然内聚。 例如程序中因为多处代码重复而提取出来的公共逻辑,这组逻辑彼此间没有任何的联系,这时就出现了偶然内聚。
    • 这种提取模块一般没有确定的语义或者很难了解它的语义,那么当一个应用场合需要对之进行理解或者修改的时候,就会产生相当大的困难。事实上,系统中如果存在偶然内聚的模块,那么对系统进行修改所发生的错误概率就比其它类型的模块高得多。
  • 逻辑内聚:几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。例如,一个模块读取各种类型外设的输入(如磁盘、键盘等),而不管这些输入从哪里来、做什么用,因为这个模块的各种成分都是执行输入,所以,该模块是逻辑内聚。
  • 时间内聚:一个模块完成功能必须在同一时间内执行(例如,初始化系统或者一组变量),但这些功能只是因为时间因素关联在一起,称为时间内聚。
  • 过程内聚:一个模块内部的处理成分相关的,而且这些处理必须以特定的次序执行,称为过程内聚。
  • 通信内聚:一个模块的所有成分都操作同一个数据集或者生成同一数据集,则称为通信内聚。
  • 顺序内聚:一个模块的各个成分和同一功能密切相关,而且一个成分的输出作为另一成分的输入,则称为顺序内聚。
  • 功能内聚:模块的所有成分对于单一的功能都是基本的,功能内聚的模块对完成其功能而言是充分必要的。

关系

内聚和耦合两者是密切相关的,同其他模块存在高耦合的模块常意味着是低内聚的,而高内聚的模块常意味着该模块同其他模块之间是低耦合的。

怎么做

在需求分析之初,为了应对软件系统/产品的需求工作所存在的三大挑战(问题空间理解、人与人之间的通信、需求的变化性),前辈们提出了一系列的软件开发方法。包括但不限于:结构化方法、面向数据结构的软件开发方法以及近年流行的面向对象方法等。
而在结构化方法设计中,人们发现,无论待建系统等数据流图时如何的复杂,一般总可以把它们分为两种基本的类型,即变换型数据流图和事务型数据流图
而不论是变换设计还是事务设计, 都涉及到一个共同的问题,即“基于高内聚低耦合的原理,采用一些经验性的启发式规则,对初始模块结构图进行精化,形成最终的模块结构图”。
因此人们总结出了如下一些实现模块“高内聚低耦合”的启发式规则:

  • 改进软件结构,提高模块独立性。通过模块分解或合并,改进软件结构,力求降低耦合,提升内聚,提高模块独立性。
  • 力求模块规模适中。一般规模越大往往复杂度越高,从而模块的耦合度越高。经验表明,一个模块的语句最好能写在一页纸上(通常不超过60行)
  • 力求深度、宽度、扇入、扇出适中。
    • 深度:表示其控制的层数,如果深度过大,就应该考虑是否存在一些过分简单的管理性模块,是否可以做适当的合并。
    • 宽度:指同一层模块总数的最大值。一般来说,宽度越大系统越复杂。而对宽度影响最大的因素是模块的扇出。
    • 扇出:指一个模块直接控制(调用)的下级模块数目。如果一个模块的扇出过大,意味着它需要控制和协调过多的下级模块,因而该模块往往具有较为复杂的语义。反之扇出过小意味着该模块功能过于几种,导致该模块具有复杂的语义。经验表明,一个典型的系统,其平均扇出通常是3或4(上限通常是5~9)
    • 扇入:表明有多少个上级模块直接调用了它。扇入越大则共享该模块的上级模块数目越多,这是有好处的,但是,不能违背模块独立性原则而单纯的追求高扇入。
  • 尽力使模块的作用域在其控制域之内。
    • 作用域:指受该模块内一个判定所影响的所有模块的集合
    • 控制域:指这个模块本身以及所有直接或间接从属于它的模块的集合。
  • 尽力降低模块接口的复杂度。
  • 力求模块功能可以预测。

以上就是前辈们根据经验总结,得出的实现高内聚低耦合的启发式规则,在许多场合下可以有效地改善软件结构,对提高软件质量,往往具有重要的参考价值。