概要设计之软件结构设计

简介

软件结构设计是对组成系统的各个子系统的进一步分解与规划。例如,将子系统按照其功 能要素分解成具有一定的功能边界的模块,然后以模块为单位来构造软件。显然,需求分析阶 段已经建立起的有关系统的功能模型、数据模型或状态机模型,可以作为软件结构设计的前提 依据。 具体说来,软件结构设计包括以下几方面的内容:

  1. 确定构造子系统的模块元素。
  2. 定义每个模块的功能。
  3. 定义模块接口,设计接口的数据结构。
  4. 确定模块之间的调用与返回关系。
  5. 评估软件结构质量,进行结构优化。

模块概念

模块化

模块概念产生于结构化程序设计思想,这时的模块被作为构造程序的基本单元,例如函数、 过程。 在结构化方法中,模块是一个功能单位,因此模块可大可小。它可以被理解为所建软件系 统中的一个子程序系统,也可以是子程序系统内一个涉及多项任务的功能程序块,并可以是功 能程序块内的一个程序单元,例如函数、过程。也就是说,模块实际上体现出了系统所具有的 功能层次结构。 模块可以使软件系统按照其功能组成进行分解,而通过对软件系统进行分解,则可以使一 些大的复杂的软件问题分解成诸多小的简单的软件问题。从软件开发的角度来看,这必然有利 于软件问题的有效解决。对于这个结论,可以进行以下论证: 现假设函数 C ( P )用于度量问题 P 的复杂程度,函数 E ( P )用于度量为解决问题 P 所需 要的工作量(用时间计算)。并假设有两个问题 P 1和 P 2。 如果有: C ( P 1)> C ( P 2) 则必有: E ( P 1)> E ( P 2) 这是一个显而易见的经验性结论。它表明:问题越复杂,解决这个问题所需要的工作量就 越大,需要花费时间就越多。 另一个来源于经验的结论是: C ( P 1 + P 2)> C ( P 1)+ C ( P 2) 它表明:如果能够把两个结合在一起的问题隔离开来单独解决,则可以使这原本结合在一 起的问题的复杂程度下降。 显然,从上面两个经验性结论中可以作出以下推论: E ( P 1 + P 2)> E ( P 1)+ E ( P 2) 它表明:如果能够把两个结合在一起的问题隔离开来单独解决,则可以使这原本难于解决 的问题变得容易解决起来。这个结论就是模块化的基本依据,其作用就是能够使大的复杂的问 题被“各个击破”。 上述结论表明:模块化可以使软件问题简化。但是,得出这个结论需要有下面的前提条件: 大模块被分解成诸多小模块之后,小模块基本上处于相互隔离的独立状态,它们之间没有太多 的通信。否则,小模块之间由于存在太多的通信,将会导致小模块接口复杂起来,其结果是, 虽然每个模块内部简化了,但整个软件问题反而复杂起来。 许多人都有一个良好的愿望,那就是依靠模块化使系统不断分解而使整个系统不断简化, 由此使软件开发成本不断下降。然而这却可能做不到,因为随着系统的分解,系统中模块数目 将会增加,模块接口也会增加,软件构造会由此变得复杂起来,模块连接的难度也会由此加大。实际上还有一个因素也在阻碍着这个愿望的实现,这就是软件系统的模块化分解本身也是一件 并不轻松的工作,假如分解系统所需要的工作量,已经超过因为模块简化而减少的工作量,那 就意味着,进一步分解系统已是一件得不偿失的事情了。 因此,软件系统模块化分解,从软件成本角度来看,有一个最小成本模块数范围。如下图 所示,若分解程度低于这个最小成本模块数范围,则可继续分解,以降低软件开发成本;但若 分解程度已经达到这个最小成本模块数范围,则就没有必要再进行分解了,否则将会反而增加 软件开发的成本。

抽象化

抽象是人所具有的一种高级思维活动,是以概括的方式抽取一系列事物的共同特征,由此 把握事物的本质属性。抽象也是人类解决复杂问题时强有力的手段,能够使人从事物复杂的外 部表象中发现事物的内在本质规律,由此找到解决问题的有效途径。 这种抽象的方法也被运用于软件工程之中。实际上,软件工程的每一个阶段,都能够看到 抽象思维方法的作用,从软件的定义到软件的设计,直到软件编码与最终实现。但是,在软件工程的不同阶段,抽象层次与对象却各不相同,并需要运用不同的语言进行描述。例如,在软件分析阶段,抽象对象是系统的外部特征,需要采用的描述语言是软件问题所处环境中的用户 术语;而在设计阶段,抽象对象则是系统的内部构造,需要采用的描述语言则是更加规范的适 合于系统构造的过程化语言。 概要设计中的功能模块往往被看成是一个抽象 化的功能黑盒子,其特征如图 5-13 所示。虽然它已 是一个与软件实现直接相关的实体单元,可以看到 它清晰的外观,但是却看不到它内部更加具体的实 现细节。 抽象的作用是对事物现象的高度概括,但抽象 的最终目的则是走到它的对立面,产生出具体的结 果。因此,一个完整的抽象化过程是既包含抽象又 包含具体的循环演变过程。应该说,正是由抽象到具体的不断演变,才使得人的抽象认识能够不断地产生出有意义的结果,并不断地推动着人类 思想的进步。 这种由抽象到具体的不断演变也一直贯穿于软件工程过程之中,这就是自顶向下、逐步细 化,其中,顶是抽象的,而细化则是这个抽象的顶具体化的结果。这意味着,软件工程的每一 个阶段的推进,都具有从抽象到具体的转化。 例如需求分析,它建立在可行性研究基础之上。可行性研究所获得的是有关整个计算机系 统的外部模型,这是一种对整个系统的高度抽象。但是,随着需求分析的进行,可行性研究中 高度抽象的系统模型被逐步地转变成需求分析模型中每一个功能局部的规格定义。显然,这是 软件从高层定义到低层定义的具体化演变。 又如概要设计,它是在需求分析基础上进行的,需要根据需求分析中的基本要求设计软件 系统的基本构架,需要按照需求分析所提出的功能规格,确定软件系统中模块的构成,定义模 块的接口,确定模块之间通信,设计对模块的控制。显然,这是软件从系统定义到系统设计的 具体化演变。 应该说,概要设计中的结论,对于软件内部构造而言则仍是抽象的。比如概要设计中的功 能模块,尽管它有惟一的名称标识,有明确的功能定义,并设置有数据的输入输出接口。但是, 模块的内部实现细节则处于待定状态,需要等到详细设计完成以后才能得出更加具体的算法结论。

信息隐藏

设计软件结构时一个不可回避的问题是:为了得到一种高质量的模块组合,应该如何分解 软件。对此,不得不了解什么是“信息隐蔽”。 信息隐蔽是指每个模块的内部实现细节对于其他模块来说是隐蔽的。也就是说,模块中所 包含的信息,例如,模块内部的数据、语句或过程等,不允许其他不需要这些信息的模块使用。 显然,信息隐蔽有利于模块相互之间的隔离,可以使每个模块更加具有独立性,并可以使模块 之间的通信受到限制。例如,模块之间只能传递那些对于其功能实现而言是必须的信息。 通过限制模块需要使用的数据的作用范围,例如,定义模块内部局部变量,可以产生出模块内部信息隐蔽的效果。 模块内部信息隐蔽的好处是可以使软件系统更加健壮,更加方便维护。 以模块中的错误为例,假如模块内部信息是隐蔽的,则模块中存在的这个错误将比较难于扩散到其他模块。否则,系统可能因为一个小错误的扩散,而使整个系统崩溃。实际上,信息 隐蔽还使软件错误定位更加方便。由于软件出错位置容易发现,因此,整个软件纠错工作的效 率、质量都会随之提高。 一些模块的功能有时需要根据用户需求的变更而适时地进行一些功能改造。当需要对模块 进行功能改造时,模块内部的信息隐蔽会使对模块改造所带来的影响限制在需要改造的模块之 内,而与其他模块无关。显然,这将使软件系统的局部修改变得更加便利。

模块的独立性

模块的独立性是指软件系统中每个模块都只涉及自己特定的子功能,并且模块接口简单, 与软件中其他模块没有过多的联系。

模块独立性是衡量软件中模块质量最重要的指标,是设计与优化软件结构时必须考虑的重要因素。当软件中的每个模块都具有很好的独立性时,软件系统不仅更加容易实现,并且会使今后的维护更加方便。 模块的独立性一般采用耦合和内聚这两个定性的技术指标进行度量。其中,耦合用来反映 模块之间互相连接的紧密程度,模块之间的连接越紧密,联系越多,耦合性就越高。内聚用来反映模块内部各个元素彼此结合的紧密程度,一个模块内部各个元素之间结合越紧密,则它的 内聚性就越高。显然,为了使模块具有较强的独立性,要求模块是高内聚、低耦合。

耦合

耦合是软件结构中各个模块之间相互关联程度的度量。耦合的强弱取决于各个模块之间接 口的复杂程度、接口数据对模块内部计算的影响程度和调用模块的方式。 模块之间的耦合形式主要有:非直接耦合、数据耦合、控制耦合、公共耦合和内容耦合。 其中,非直接耦合和数据耦合是较弱的耦合,控制耦合和公共耦合是中等程度的耦合,内容耦 合则是强耦合。 模块化设计的目标是尽量建立模块间耦合松散的系统。因此,在设计软件结构时一般也就 要求尽量采用非直接耦合和数据耦合,少用或限制使用控制耦合和公共耦合,绝对不能使用内 容耦合。 为了更好地认识模块之间的耦合,下面将对上述各种耦合形式给出必要说明。

a.非直接耦合

如果两个模块之间没有直接关系,它们之间的联系仅限于受到共同主模块的控制与调用, 则称这种耦合为非直接耦合。 由于非直接耦合的模块之间没有数据通信,因此模块的独立性最强。

b.数据耦合

当一个模块访问另一个模块时,如果彼此之间是通过模块接口处的参数实现通信,并且参 数所传递的数据仅用于计算,而不会影响传入参数模块的内部程序执行路径,则称这种耦合为 数据耦合。 数据耦合是一种松散的耦合。依靠这种类 型的耦合,模块之间既能实现通信,又有比较 强的独立性。因此,软件结构设计中提倡使用 这类耦合

c.控制耦合

当一个模块访问另一个模块时,如果彼此 之间是通过模块接口处的参数实现通信,并且 参数传递了开关、标志、名字等控制信息,由 此影响了传入参数模块的内部程序执行路径, 则称这种耦合为控制耦合如图 5-14 所示。 由于接口参数影响着内部程序执行路径, 因此控制耦合比数据耦合要强一些,会使模块 的独立性有所下降。当需要通过一个单一的接口传递模块内多项功能的选择信息时,往往需要用到控制耦合。显然,在控制耦合形式下,对 所控制模块的修改,需要受到控制参数的限制。

d.公共耦合 公共耦合是一种通过访问公共数据环境而实现通信的模块耦合形式,例如,独立于模块而 存在的数据文件、数据表集、公共变量等,都可以看作为公共数据环境。 公共耦合中的公共数据环境是提供给任何模块的,当模块之间的耦合是公共耦合时,那些 原本可以依靠接口提供的对数据的限制也就没有了。因此,相比依靠接口的耦合形式,公共耦 合必然会使模块的独立性下降。也正因为如此,实际应用中,只有在模块之间需要共享数据, 并且通过接口参数传递不方便时,才使用公共耦合。 需要注意的是:模块之间公共耦合的复杂程度,将会随着耦合模块个数的增加而显著增加。 为了降低公共耦合带来的复杂性和提高模块的独立性,实际应用中还会针对公共耦合专门设置 一些限制,例如不使用公共耦合数据传递控制信息。 下图中的模块采用了访问公共数据环境的公共耦合形式。其中图(a)是一个模块只往 公共数据环境里送进数据,另一个模块只从公共数据环境中取出数据,这是一种比较松散的公 共耦合;而图(b)则是两个模块都可以向公共数据环境里送进数据,又都可以从公共数据环境 中取出数据,这就是一种比较紧密的公共耦合。

另外,公共耦合还会带来以下一些方面的影响: •  由于所有公共耦合模块都会与某一个公共数据环境有关,因此修改公共数据环境中某个 数据的结构,将会影响到所有被耦合的模块。

•  由于没有办法控制各个模块对公共数据的存取,因此公共耦合会影响软件模块的可靠性 和适应性。

•  由于公共数据环境需要被许多模块使用,因此不得不使用具有公共意义的数据名称。显 然,这会使得程序的可读性有所下降。

e.内容耦合 如果发生下列情形,两个模块之间就发生了内容耦合。

•  一个模块直接访问另一个模块的内部数据。

•  一个模块不通过正常入口转到另一模块内部。 •  两个模块有一部分程序代码重叠。

•  一个模块有多个入口。 内容耦合是一种非常强的耦合形式,严重影响了模块独立性。当模块之间存在内容耦合时,模块的任何改动都将变得非常困难,一旦程序有错则很难修正。因此,设计软件结构时,也就 要求绝对不要出现内容耦合。所幸的是,大多数高级程序设计语言已经设计成不允许出现内容 耦合,它一般只会出现在汇编语言程序中。

内聚

内聚是对模块内部各个元素彼此结合的紧密程度的度量。模块内部各个元素之间的联系越紧密,则它的内聚程度就越高。模块的设计目标是尽量使模块的内聚程度高,以达到模块独立、 功能集中的目的。 模块内聚的主要类型有:功能内聚、信息内聚、通信内聚、过程内聚、时间内聚、逻辑内 聚和偶然内聚,如下图  所示。其中,功能内聚和信息内聚属于高内聚,通信内聚和过程内聚 属于中等程度的内聚,时间内聚、逻辑内聚和偶然内聚则属于低内聚;而且,模块内聚程度越 高,其功能越集中、独立性越强。

内聚所体现的是模块的内部功能构造,耦合所体现的是模块之间的联系。一般情况下,内 聚和耦合是相互关联的,模块的内聚程度越高,则模块间的耦合程度就会越低,但这也不是绝 对的。值得指出的是,比起模块之间的耦合来,模块的内聚更显重要。因此,实际设计中应当 把更多的注意力放在如何提高模块的内聚程度上。 模块内聚的提高依赖于对模块功能的正确认识,应该通过定义使每一个模块都具有明确的 功能。为了更好地认识模块内聚,下面将对上述几种内聚类型分别加以说明。

a.偶然内聚

当模块内各部分之间没有联系,或即使有联系,这种联系也很松散时,将会出现偶然内聚。偶然内聚往往产生于对程序的错误认识或没有进行软件结构设计就直接编程。例如,一些 编程人员可能会将一些没有实质联系,但在程序中重复多次出现的语句抽出来,组成一个新的 模块,这样的模块就是偶然内聚模块。 偶然内聚模块由于是随意拼凑而成,模块内聚程度最低、功能模糊,很难进行维护。

b.逻辑内聚

逻辑内聚是把几种相关的功能组合在一起形成为一个模块。在调用逻辑内聚模块时,可以 由传送给模块的判定参数来确定该模块应执行哪一种功能。例如下图 中的打印模块。 逻辑内聚模块比偶然内聚模块的内聚程度要高,因为它表明了各部分之间在功能上的相关 关系。但是它每次执行的不是一种功能,而是若干功能中的一种,因此它不易修改。另外,在 调用逻辑内聚模块时,需要进行控制参数的传递,由此增加了模块间的耦合。

c.时间内聚

时间内聚模块一般是多功能模块,其特点是模块中的各项功能的执行与时间有关,通常要 求所有功能必须在同一时间段内执行。例如初始化模块,其功能可能包括给变量赋初值、连接 数据源、打开数据表、打开文件等,这些操作要求在程序开始执行的最初一段时间内全部完成。
 时间内聚模块比逻辑内聚模块的内聚程度又稍高一些,其内部逻辑比较简单,一般不需要 进行判定转移。

d.过程内聚

如果一个模块内的处理是相关的,而且必须以特定次序执行,则称之为过程内聚模块。在 使用流程图设计程序的时侯,常常通过流程图来确定模块划分,由此得到的就往往是过程内聚 模块。例如,可以根据流程图中的循环部分、判定部分和计算部分将程序分成三个模块,这三 个模块就是过程内聚模块。 过程内聚模块的内聚程度比时间内聚模块的内聚程度更强一些,但过程内聚模块仅包括完 整功能的一部分,因此模块之间的耦合程度比较高。

e.通信内聚

如果一个模块内各功能部分都使用了相同的输入数据或产生了相同的输出数据,则称之为 通信内聚模块。例如下图 中的处理工资模块。 通信内聚模块由一些独立的功能组成,因此其内聚程度比过程内聚程度要高。

f.顺序内聚

如果一个模块内的诸多功能元素都和某一个功能元素密切相关,而且这些功能元素必须顺 序安排(前一项功能的数据输出作为后一项功能的数据输入),则称之为顺序内聚模块。当根据 数据流图划分模块时,由此得到的通常就是顺序内聚模块。例如下图 中的入库统计模块。 顺序内聚模块也包含着多项功能,但它有一个核心功能,其他功能则围绕着这个核心而安 排。因此,顺序内聚程度比通信内聚程度更高。

g.功能内聚

如果一个模块中各个部分都是完成某一具体功能必不可少的组成部分,各个部分协同工 作、紧密联系、不可分割,则称该模块为功能内聚模块。 功能内聚模块的特征是功能单一、接口简单,因此其容易实现、便于维护。与其他内聚类 型相比,功能内聚具有最高的内聚程度,软件结构设计时应以其作为追求目标。

结构化设计建模

软件结构设计涉及模块功能、模块接口与模块调用关系等问题,为了使这些问题能够集中 清晰地表达出来,软件结构设计需要借助于一定的图形工具来建立设计模型,例如软件结构图、 HIPO 图。

软件结构图

软件结构图由 Yourdon 于 20 世纪 70 年代提出,并被广泛应用于软件结构设计中,能够有 效说明软件中模块之间的调用与通信。 软件结构图使用矩形框表示模块(框内注明模块的名字或主要功能),使用带箭头的直线 段连接上下级模块,以表示上级模块对下级模块的调用。此外,软件结构图还可以在调用箭头 旁使用带注释的箭头,以表示上级模块在调用下级模块时参数的传递与结果的返回,其基本图 形符号如下表 所列。

上图图是一个自动阅卷系统的软件结构图。阅卷总控模块为顶层模块,其调用考卷数据 输入、阅卷处理和考卷成绩输出这三个模块,以进行考卷输入、成绩计算和成绩输出的控制。 在更下一级模块调用中,考卷数据输入模块通过调用读答卷卡模块将考卷原始数据输入,然后 调用检验考卷数据模块产生出适合评分计算的有效数据;考卷成绩输出模块则通过调用格式化 成绩数据模块对计算出的成绩数据进行输出前的格式化处理,然后调用写成绩记录模块实现成 绩的存档操作。
 

HIPO 图

HIPO 图是美国 IBM 公司推出的“H 图”与“IPO 图”的组合。

  a.H 图

H 图是软件层次图的简称,用于描述软件结构上的分层调用关系,作用类似于软件结构图, 但不涉及调用时的数据流、控制流等附加信息。 H 图的优点是清晰度高,能够用于正式文档中对软件结构的描述。图 5-21 是自动阅卷系统 的 H 图,可以看出,它比上面的软件结构图要清晰得多。 为了能使 H 图中的模块具有可追踪性,由此可以与它所对应的 IPO 图联系起来。图中模块 除了最顶层的之外,其他的模块都需要按照一定的规则编号,以方便检索。

  b.IPO图

IPO 图是“输入—处理—输出图”的简称,其中的“I”是指输入,“O”是指输出,“P” 是指处理。可以使用 IPO 图对模块进行外部特征描述,涉及输入、输出接口和基本加工步骤等。 在 HIPO 图中,需要针对 H 图中的每个模块给出 IPO 图描述。

从上面的 IPO 图举例可以看到,IPO 图提供了有关模块的更加完整的定义和说明。显然, 这将有利于由概要设计到详细设计的过渡。

软件结构优化

软件结构设计往往需要经历多次反复,其作用是可以不断地改进软件结构,提高软件质量。 为了改进软件结构,软件设计人员进行了长期的实践与总结,由此得到了一些优化设计的原则。 本节将介绍这些原则。

使模块功能完整

一个完整的功能模块,不仅能够完成指定的功能,而且还应当能够将完成任务的状态通知 使用者。 具体说来,一个完整的功能模块应当包含以下几个部分的内容:

  a.执行规定功能的部分。

  b.出错处理的部分。当模块不能完成规定的功能时,必须回送出错标志,并向它的调用 者报告出现这种例外情况的原因。

  c.如果需要返回一系列数据给它的调用者,在完成数据加工时,应当给它的调用者返回 一个该模块执行是否正确结束的“标志”。

使模块大小适中

许多情况下,可以用模块中所含语句的数量的多少来衡量模块的大小。有人认为限制模块 的大小也是减少模块复杂性的有效手段之一,因而要求把模块的大小限制在一定的范围之内, 例如将模块内的语句限制在 50~100 行左右。当然,这只能作为参考。 一般说来,模块过大的原因往往是模块的功能太多,因此有必要对大模块做进一步的分解。 大多数情况下,可以从大模块中分解出一些下级模块或同层模块。 软件结构设计时,也有可能出现模块过小的问题。如果模块太小,则要注意这个模块的功 能是否完整,是否将一个完整的功能分解到多个模块中去了。许多情况下,可以考虑将较小的 模块与调用它的上级模块合并。如果模块虽小但功能内聚性好,或者它为多个模块所共享,或 者调用它的上级模块很复杂,则不要贸然将小模块与其他模块合并,而是需要做更加细致的分析。

使模块功能可预测

一个功能可预测的模块可以被看成是一个“黑箱”,无论内部处理细节如何,只要输入数 据是相同的,就总能产生相同的输出结果。 例如,在模块中使用了静态变量。因为静态变量会在模块内部产生较难预见的存储,而如 果这个静态变量又被用为多项功能的选择标记,则可能会使得模块的功能难以被调用者预知。 由于这个原因,设计者要特别慎重地使用静态变量。 模块功能可预测还意味着,模块的功能必须明确清晰地定义,应该使模块具有很好的内   聚性。

(4)尽量降低模块接口的复杂程度

模块接口是模块与外界进行通信的通道,较复杂的接口往往会带来较高的耦合。因此,应 努力降低模块接口的复杂程度。对此,可以从以下两个方面作出考虑。

a.接口参数尽量采用简单数据类型,以使接口容易理解。例如,尽量使用基本字符、整 数类型,而不是数组、指针、结合体类型。

b.限制接口参数的个数。接口参数个数太多,往往是由于模块功能混杂,不得不依靠参 数进行调控。因此,过多的参数往往意味着模块还有进一步分解的必要。

使模块作用范围限制在其控制范围之内

模块的控制范围包括它本身及其所有从属于它的直接或间接下级模块。如图 5-24 所示, 模块 B 的控制范围是模块 E、F、G、K,模块 C 的控制范围是模块 H、I、L、K,模块 D 的控制范 围是模块 I、J、L。 模块的作用范围则是指模块内一个判定的影响范围,凡是受这个判定影响的所有模块都属 于这个判定的作用范围。其中,如果一个判定的作用范围包含在这个判定所在模块的控制范围 之内,则这种结构是简单的;否则,其结构就是不简单的,需要进行结构调整。 例如,图 5-24 中的模块 F。如果它做出一个判定之后,接着需要模块 G 工作,由于模块 G 不在模块 F 控制范围之内,因此模块 F 必须返回一个信号给模块 B,再由 B 把信号传送给模块 G。 显然这不是一个好的设计,它增加了模块之间数据的传送量,并使模块之间出现了控制耦合, 因此需要对其结构进行调整。
 

在设计过程中,如果发现模块的作用范围不在控制范围之内时,可以采用如下办法进行结 构调整,把模块的作用范围移到其控制范围之内。

(1)将判定所在的模块合并到它的父模块中去,或上移到层次较高的位置上去,由此可使 判定处于一个较高的位置,以达到有效的控制。例如,将模块 F 与模块 B 合并,如图 5-25 所示, 由此可使模块 G 受到控制。

(2)将受判定影响的模块下移到控制范围以内。例如,将模块 G 下移到模块 F 之下,如图 5-26 所示,由此可使模块 G 受到模块 F 控制。 需要注意的是:在改进模块的结构时,应当根据具体情况通盘考虑,既要尽量小范围地调 整结构,使改进后的软件结构能够最好地体现问题的原来结构,又要考虑改进后的结构在实现 上的可行性。

深度、宽度、扇出和扇入应当适当

软件的深度是指软件结构的层数。例如,图 5-24 中的软件结构,其深度为 4 层。

软件的宽度是指软件结构同一个层次上模块的总个数的最大值。例如,图 5-24 中的软件 结构,其第二层宽度是 3,第三层宽度是 6,第四层宽度是 2。其整个软件的宽度是 6。 软件结构上的深度、宽度,在一定程度上反映出了软件系统的规模和复杂程度。也就是说, 软件规模越大越复杂,其深度、宽度也会越大。一般说来,深度、宽度会受模块的扇出、扇入 影响,并应该有一个合适的大小。深度、宽度太小,往往表示模块分解得还不够细,需要进一 步分解。而深度、宽度太大,则可能表示模块分解得太细小了,或功能冗余模块太多了,以致 软件结构变得复杂起来。一般说来,软件结构越复杂,模块之间的耦合就会越大。因此,假如 软件结构过于复杂,就有必要将一些模块进行适当的合并,将那些功能相同或相似的模块合并 起来作为公共模块使用。 模块的扇出是指模块直接调用的下级模块的个数。比较适当的扇出数为 2~5,一般不要超 过 9。如果一个模块的扇出过大,就表明该模块具有过分复杂的控制功能,需要协调和控制过 多的下属模块。对此,应当适当增加中间层次的控制模块,将比较集中的控制分解开来,如图 5-27 所示。但扇出过小也不好,这样将使得软件结构的深度大大增加,会带来过多的调用和返 回的时间开销,由此降低软件的工作效率。

模块的扇入是指模块受到了多少个直接上级模块的调用。一个模块的扇入越大,则共享该 模块的上级模块的数目就越多。如果一个模块的扇入数太大,而它又不是公用模块,则说明该 模块可能具有多项功能。在这种情况下,应当对它做进一步的分析,并将其功能做进一步的分解。

一般说来,各个不同层次的模块具有以下特点:顶层模块起全局控制作用;中间层次的模 块起局部控制作用,并适当承担一些简单的操作提示功能;底层模块担任具体加工任务。因此, 一个设计得比较好的软件结构通常应具有这样的特征:顶层模块高扇出,中层模块低扇出,底 层模块高扇入。

posted on 2020-02-05 21:01  活着的虫子  阅读(5004)  评论(0编辑  收藏  举报

导航