第三章软件设计 复习要求 1.了解软件概要设计的原则和过程。 2.掌握模块划分的评价准则一模块独立性的判别 3.掌握结构化设计方法 4.了解 Jackson系统开发方法和 Jackson程序设计方法。 5.了解数据设计和文件设计的原则。 6.掌握常用的详细设计的表达方法 6.了解软件设计规格说明和设计评审的主要内容 二、内容提要 1.软件设计的过程 旦软件需求确定之后,就进入开发阶段。开发阶段由三个互相关联的的步骤组成:设 计、实现(编码)和测试。每个步骤都按某种方式进行信息变换,最后得到有效的计算机软 件 (1)软件设计在开发阶段中的重要性 在软件需求分析阶段已经完全弄清楚了软件的各种需求,较好地解决了要让所开发的软 件“做什么”的问题,并已在软件需求规格说明和数据要求规格说明中详尽和充分地阐明了 这些需求。下一步就要着手实现软件的需求,即要着手解决“怎么做”的问题。 分析模型中的每一个成份都提供了建立设计模型所需的信息。软件设计的信息流如图4.1 所示。根据用数据、功能和行为模型表示的软件需求,采用某种设计方法进行数据设计、体 系结构设计、接口设计和过程设计。 据 实 象|关 据\格 过程设计 描系(数据词典)流说 接口设计 状态转换图 体系结构设计 控 说 数据设计 图41将分析模型转换为软件设计 数据设计将实体一关系图中描述的对象和关系,以及数据词典中描述的详细数据内容转
1 第三章 软件设计 一、复习要求 1. 了解软件概要设计的原则和过程。 2. 掌握模块划分的评价准则―模块独立性的判别。 3. 掌握结构化设计方法。 4. 了解 Jackson 系统开发方法和 Jackson 程序设计方法。 5. 了解数据设计和文件设计的原则。 6. 掌握常用的详细设计的表达方法。 6. 了解软件设计规格说明和设计评审的主要内容。 二、内容提要 1. 软件设计的过程 一旦软件需求确定之后,就进入开发阶段。开发阶段由三个互相关联的的步骤组成:设 计、实现(编码)和测试。每个步骤都按某种方式进行信息变换,最后得到有效的计算机软 件。 (1) 软件设计在开发阶段中的重要性 在软件需求分析阶段已经完全弄清楚了软件的各种需求,较好地解决了要让所开发的软 件“做什么”的问题,并已在软件需求规格说明和数据要求规格说明中详尽和充分地阐明了 这些需求。下一步就要着手实现软件的需求,即要着手解决“怎么做”的问题。 分析模型中的每一个成份都提供了建立设计模型所需的信息。软件设计的信息流如图4.1 所示。根据用数据、功能和行为模型表示的软件需求,采用某种设计方法进行数据设计、体 系结构设计、接口设计和过程设计。 图4.1 将分析模型转换为软件设计 数据设计将实体―关系图中描述的对象和关系,以及数据词典中描述的详细数据内容转 数据词典 数 据 流 图 实 体 关 系 图 状态转换图 加 工 控 制 规 规 格 格 说 说 明 明 数 据 对 象 描 述 过程设计 接口设计 体系结构设计 数据设计
化为数据结构的定义。体系结构设计定义软件系统各主要成份之间的关系。接口设计根据数 据流图定义软件内部各成份之间、软件与其它协同系统之间及软件与用户之间的交互机制。 过程设计则是把结构成份转换成软件的过程性描述。在编码步骤,根据这种过程性描述,生 成源程序代码,然后通过测试最终得到完整有效的软件。 软件设计是开发阶段中最重要的步骤,它是软件开发过程中质量得以保证的关键步骤。 设计提供了软件的表示,使得软件的质量评价成为可能。同时,软件设计又是将用户要求准 确地转化成为最终的软件产品的唯一途径。另一方面,软件设计是后续开发步骤及软件维护 工作的基础。如果没有设计,只能建立一个不稳定的系统,如图4.2所示。只要出现一些小小 的变动,就会使得软件垮掉,而且难于测试 维护 维护 测试 实现 设计 有软件设计 没有软件设计 图42软件设计的重要性 (2)软件设计的过程 软件设计是一个把软件需求变换成软件表示的过程。最初这种表示只是描绘出可直接反 映功能、数据、行为需求的软件的总的框架,然后进一步细化,在此框架中填入细节,把它 加工成在程序细节上非常接近于源程序的软件表示 从工程管理的角度来看,软件设计分两步完成。首先做概要设计,将软件需求转化为数 据结构和软件的系统结构,并建立接口。然后是详细设计,即过程设计。通过对结构表示进 行细化,得到软件的详细的数据结构和算法 McGlanghlin给出在将需求转换为设计时判断设计好坏的三条特征 设计必须实现分析模型中描述的所有显式需求,必须满足用户希望的所有隐式需求 设计必须是可读、可理解的,使得将来易于编程、易于测试、易于维护。 ■设计应从实现角度出发,给出与数据、功能、行为相关的软件全貌 以上三点就是软件设计过程的目标。为达到这些目标,必须建立衡量设计的技术标准。 ①设计出来的结构应是分层结构,从而建立软件成份之间的控制。 ②设计应当模块化,从逻辑上将软件划分为完成特定功能或子功能的构件。 ③设计应当既包含数据抽象,也包含过程抽象。 ④设计应当建立具有具有独立功能特征的模块。 ⑤设计应当建立能够降低模块与外部环境之间复杂连接的接口。 ⑥设计应能根据软件需求分析获取的信息,建立可驱动可重复的方法 软件设计过程根据基本的设计原则,使用系统化的方法和完全的的设计评审来建立良好 的设计 2.软件设计的原则 (1)抽象化
2 图 4.2 软件设计的重要性 化为数据结构的定义。体系结构设计定义软件系统各主要成份之间的关系。接口设计根据数 据流图定义软件内部各成份之间、软件与其它协同系统之间及软件与用户之间的交互机制。 过程设计则是把结构成份转换成软件的过程性描述。在编码步骤,根据这种过程性描述,生 成源程序代码,然后通过测试最终得到完整有效的软件。 软件设计是开发阶段中最重要的步骤,它是软件开发过程中质量得以保证的关键步骤。 设计提供了软件的表示,使得软件的质量评价成为可能。同时,软件设计又是将用户要求准 确地转化成为最终的软件产品的唯一途径。另一方面,软件设计是后续开发步骤及软件维护 工作的基础。如果没有设计,只能建立一个不稳定的系统,如图4.2所示。只要出现一些小小 的变动,就会使得软件垮掉,而且难于测试。 (2) 软件设计的过程 软件设计是一个把软件需求变换成软件表示的过程。最初这种表示只是描绘出可直接反 映功能、数据、行为需求的软件的总的框架,然后进一步细化,在此框架中填入细节,把它 加工成在程序细节上非常接近于源程序的软件表示。 从工程管理的角度来看,软件设计分两步完成。首先做概要设计,将软件需求转化为数 据结构和软件的系统结构,并建立接口。然后是详细设计,即过程设计。通过对结构表示进 行细化,得到软件的详细的数据结构和算法。 McGlanghlin 给出在将需求转换为设计时判断设计好坏的三条特征: ▪ 设计必须实现分析模型中描述的所有显式需求,必须满足用户希望的所有隐式需求。 ▪ 设计必须是可读、可理解的,使得将来易于编程、易于测试、易于维护。 ▪ 设计应从实现角度出发,给出与数据、功能、行为相关的软件全貌。 以上三点就是软件设计过程的目标。为达到这些目标,必须建立衡量设计的技术标准。 ① 设计出来的结构应是分层结构,从而建立软件成份之间的控制。 ② 设计应当模块化,从逻辑上将软件划分为完成特定功能或子功能的构件。 ③ 设计应当既包含数据抽象,也包含过程抽象。 ④ 设计应当建立具有具有独立功能特征的模块。 ⑤ 设计应当建立能够降低模块与外部环境之间复杂连接的接口。 ⑥ 设计应能根据软件需求分析获取的信息,建立可驱动可重复的方法。 软件设计过程根据基本的设计原则,使用系统化的方法和完全的的设计评审来建立良好 的设计。 2. 软件设计的原则 (1) 抽象化
对软件进行模块设计的时候,可以有不同的抽象层次。在最高的抽象层次上,可以使用 问题所处环境的语言描述问题的解法。而在较低的抽象层次上,则采用过程化的方法 过程的抽象:在软件工程过程中,从系统定义到实现,每进展一步都可以看做是对 软件解决方案的抽象化过程的一次细化。在软件计划阶段,软件被当做整个计算机系统中的 个元素来看待。在软件需求分析阶段,用“问题所处环境的为大家所熟悉的术语”来描述 软件的解决方法。而在从概要设计到详细设计的过程中,抽象化的层次逐次降低。当产生源 程序时到达最低的抽象层次 数据抽象:数据抽象与过程抽象一样,允许设计人员在不同层次上描述数据对象的 细节。例如,可以定义一个draw数据对象,并将它规定为一个抽象数据类型,用它的构成元 素来定义它的内部细节。此时,数据抽象draw本身是由另外一些数据抽象构成。而且在定义 draw的抽象数据类型之后,就可以引用它来定义其它数据对象,而不必涉及draw的内部细 节 ■控制抽象:与过程抽象和数据抽象一样,控制抽象可以包含一个程序控制机制而无 须规定其内部细节。控制抽象的例子就是在操作系统中用以协调某些活动的同步信号。 (2)自顶向下,逐步细化 Niklaus wirth提出的设计策略。将软件的体系结构按自顶向下方式,对各个层次的过程 细节和数据细节逐层细化,直到用程序设计语言的语句能够实现为止,从而最后确立整个的 体系结构。最初的说明只是概念性地描述了系统的功能或信息,但并未提供有关功能的内部 实现机制或有关信息的内部结构的任何信息。设计人员对初始说明仔细推敲,进行功能细化 或信息细化,给出实现的细节,划分出若干成份。然后再对这些成份,施行同样的细化工作。 随着细化工作的逐步展开,设计人员就能得到越来越多的细节 (3)模块化 软件系统的层次结构正是模块化的具体体现。就是说,整个软件被划分成若干单独命名 和可编址的部分,称之为模块。这些模块可以被组装起来以满足整个问题的需求 个大软件,由于其控制路径多、涉及范围广、变量多及其总体复杂性,使其相对于 个较小的软件不容易被人们理解。在解决问题的实践中,如果把两个问题结合起来作为一个 问题来处理,其理解复杂性大于这两个问题被分开考虑时的理解复杂性之和。因此,把一个 大而复杂的问题分解成一些独立的易于处理的小问题,解决起来就容易得多。 基于上述考虑,把问题/子问题(功能/子功能)的分解与软件开发中的系统/子系统 或者系统/模块对应起来,就能够把一个大而复杂的软件系统划分成易于理解的比较单纯的 模块结构。所谓“比较单纯”,是指模块和其它模块之间的接口应尽可能独立ε 实际上,如果模块是相互独立的,当模块变得越小,每个模块花费的工作量越低:但当 模块薮増加时,模块间的联系也随之増加,把这些模块联接起来的工作量也随之増加。如图 4.3所示。因此,存在一个模块个数M,它使得总的开发成本达到最小 成成本/模块 联接成本 总的软件成本 或工作量 最小成本范围 模块数目增加 模块大小减小 模块个数模块数/模块大小
3 对软件进行模块设计的时候,可以有不同的抽象层次。在最高的抽象层次上,可以使用 问题所处环境的语言描述问题的解法。而在较低的抽象层次上,则采用过程化的方法。 ▪ 过程的抽象 :在软件工程过程中,从系统定义到实现,每进展一步都可以看做是对 软件解决方案的抽象化过程的一次细化。在软件计划阶段,软件被当做整个计算机系统中的 一个元素来看待。在软件需求分析阶段,用“问题所处环境的为大家所熟悉的术语”来描述 软件的解决方法。而在从概要设计到详细设计的过程中,抽象化的层次逐次降低。当产生源 程序时到达最低的抽象层次。 ▪ 数据抽象 :数据抽象与过程抽象一样,允许设计人员在不同层次上描述数据对象的 细节。例如,可以定义一个 draw 数据对象,并将它规定为一个抽象数据类型,用它的构成元 素来定义它的内部细节。此时,数据抽象 draw 本身是由另外一些数据抽象构成。而且在定义 draw 的抽象数据类型之后,就可以引用它来定义其它数据对象,而不必涉及 draw 的内部细 节。 ▪ 控制抽象 :与过程抽象和数据抽象一样,控制抽象可以包含一个程序控制机制而无 须规定其内部细节。控制抽象的例子就是在操作系统中用以协调某些活动的同步信号。 (2) 自顶向下,逐步细化 Niklaus Wirth 提出的设计策略。将软件的体系结构按自顶向下方式,对各个层次的过程 细节和数据细节逐层细化,直到用程序设计语言的语句能够实现为止,从而最后确立整个的 体系结构。最初的说明只是概念性地描述了系统的功能或信息,但并未提供有关功能的内部 实现机制或有关信息的内部结构的任何信息。设计人员对初始说明仔细推敲,进行功能细化 或信息细化,给出实现的细节,划分出若干成份。然后再对这些成份,施行同样的细化工作。 随着细化工作的逐步展开,设计人员就能得到越来越多的细节。 (3) 模块化 软件系统的层次结构正是模块化的具体体现。就是说,整个软件被划分成若干单独命名 和可编址的部分,称之为模块。这些模块可以被组装起来以满足整个问题的需求。 一个大软件,由于其控制路径多、涉及范围广、变量多及其总体复杂性,使其相对于一 个较小的软件不容易被人们理解。在解决问题的实践中,如果把两个问题结合起来作为一个 问题来处理,其理解复杂性大于这两个问题被分开考虑时的理解复杂性之和。因此,把一个 大而复杂的问题分解成一些独立的易于处理的小问题,解决起来就容易得多。 基于上述考虑,把问题/子问题(功能/子功能)的分解与软件开发中的系统/子系统 或者系统/模块对应起来,就能够把一个大而复杂的软件系统划分成易于理解的比较单纯的 模块结构。所谓“比较单纯”,是指模块和其它模块之间的接口应尽可能独立。 实际上,如果模块是相互独立的,当模块变得越小,每个模块花费的工作量越低;但当 模块数增加时,模块间的联系也随之增加,把这些模块联接起来的工作量也随之增加。如图 4.3 所示。因此,存在一个模块个数 M, 它使得总的开发成本达到最小
图43模块大小、模块数目与费用的关系 (4)控制层次 控制层次也叫做程序结构,它表明了程序构件(模块)的组织情况。控制层次往往用程 序的层次(树形或网状)结构来表示。如图44所示。位于最上层根部是顶层模块,它是程 序的主模块。与其联系的有若干下属模块,各下属模块还可以进一步引出更下一层的下属模 块。模块M是顶层模块,如果算做第0层,则其下属模块A、B和C为第1层,模块D、E K、L和N是第2层,…,等等。 扇出 王 深 度 白面的面面∥ 扇入 宽度 图44程序的层次结构图示例 ·程序结构的深度:程序结构的层次数称为结构的深度。结构的深度在一定意义上反映 了程序结构的规模和复杂程度 程序结构的宽度:层次结构中同一层模块的最大模块个数称为结构的宽度 模块的扇入和扇出:扇出表示一个模块直接调用(或控制)的其它模块数目。扇入则 定义为调用(或控制)一个给定模块的模块个数。多扇出意味着需要控制和协调许多下属模 块。而多扇入的模块通常是公用模块 要注意的是,程序结构是软件的过程表示,但并未表明软件的某些过程性特征。比如, 进程序列、事件/决策的顺序或其它的软件动态特性 (5)结构划分 程序结构可以按水平方向或垂直方向进行划分。水平划分按主要的程序功能来定义模块 结构的各个分支。顶层模块是控制模块,用来协调程序各个功能之间的通信和运行。其下级 模块的最简单的水平划分方法是建立三个分支:输入、处理(数据变换)和输出。这种划分 的优点是:由于主要的功能相互分离,易于修改、易于扩充,且没有副作用。缺点是:需要 通过模块接口传递更多的数据,使程序流的整体控制复杂化 垂直划分也叫做因子划分。主要用在程序的体系结构中,且工作自顶向下逐层分布:顶 层模块执行控制功能,少做实际处理工作,而低层模块是实际输入、计算和输出的具体执行 者。这种划分的优点是:对低层模块的修改不太可能引起副作用的传播,而恰恰对计算机程 序的修改常常发生在低层的输入、计算或输出模块中。因此,程序的整体控制结构不太可能 被修改,便于将来的维护 (6)数据结构 数据结构是数据的各个元素之间的逻辑关系的一种表示。数据结构设计应确定数据的组 织、存取方式、相关程度、以及信息的不同处理方法。数据结构的组织方法和复杂程度可以
4 图 4.3 模块大小、模块数目与费用的关系 (4) 控制层次 控制层次也叫做程序结构,它表明了程序构件(模块)的组织情况。控制层次往往用程 序的层次(树形或网状)结构来表示。如图 4.4 所示。位于最上层根部是顶层模块,它是程 序的主模块。与其联系的有若干下属模块,各下属模块还可以进一步引出更下一层的下属模 块。模块 M 是顶层模块,如果算做第 0 层,则其下属模块 A、B 和 C 为第 1 层,模块 D、E、 K、L 和 N 是第 2 层,…,等等。 图 4.4 程序的层次结构图示例 ▪ 程序结构的深度:程序结构的层次数称为结构的深度。结构的深度在一定意义上反映 了程序结构的规模和复杂程度。 ▪ 程序结构的宽度:层次结构中同一层模块的最大模块个数称为结构的宽度。 ▪ 模块的扇入和扇出:扇出表示一个模块直接调用(或控制)的其它模块数目。扇入则 定义为调用(或控制)一个给定模块的模块个数。多扇出意味着需要控制和协调许多下属模 块。而多扇入的模块通常是公用模块。 要注意的是,程序结构是软件的过程表示,但并未表明软件的某些过程性特征。比如, 进程序列、事件∕决策的顺序或其它的软件动态特性。 (5) 结构划分 程序结构可以按水平方向或垂直方向进行划分。水平划分按主要的程序功能来定义模块 结构的各个分支。顶层模块是控制模块,用来协调程序各个功能之间的通信和运行。其下级 模块的最简单的水平划分方法是建立三个分支:输入、处理(数据变换)和输出。这种划分 的优点是:由于主要的功能相互分离,易于修改、易于扩充,且没有副作用。缺点是:需要 通过模块接口传递更多的数据,使程序流的整体控制复杂化。 垂直划分也叫做因子划分。主要用在程序的体系结构中,且工作自顶向下逐层分布:顶 层模块执行控制功能,少做实际处理工作,而低层模块是实际输入、计算和输出的具体执行 者。这种划分的优点是:对低层模块的修改不太可能引起副作用的传播,而恰恰对计算机程 序的修改常常发生在低层的输入、计算或输出模块中。因此,程序的整体控制结构不太可能 被修改,便于将来的维护。 (6) 数据结构 数据结构是数据的各个元素之间的逻辑关系的一种表示。数据结构设计应确定数据的组 织、存取方式、相关程度、以及信息的不同处理方法。数据结构的组织方法和复杂程度可以
灵活多样,但典型的数据结构种类是有限的,它们是构成一些更复杂结构的基本构件块。图 45表示了这些典型的数据结构。 标量项 链表 顺序向量 分层树 n维空间 图45典型的数据结构 标量是最简单的一种数据结构。所谓标量项就是单个的数据元素,例如一个布尔量、整 数、实数或一个字符串。可以通过名字对它们进行存取。 若把多个标量项组织成一个表或者顺序邻接为一组时,就形成了顺序向量。顺序向量又 称为一维数组。通常可以通过下标及数组名来访问数组中的某一元素。把顺序向量扩展到 维、三维,直至任意维,就形成了n维向量空间。最常见的n维向量空间是二维矩阵。 链表是一种更灵活的数据结构,它把不相邻的标量项、向量或空间结构用拉链指针链接 起来,使得它们可以像表一样得到处理。 组合上述基本数据结构可以构成其它数据结构。例如,可以用包含标量项、向量或n 维空间的多重链表来建立分层结构和网络结构。而利用它们又可以实现多种集合的存储 必须注意,数据结构和程序结构一样,可以在不同的抽象层次上表示。例如,一个栈是 种线性结构的逻辑模型,其特点是只允许在结构的一端进行插入或删除运算。它可以用向 量实现,也可以用链表实现。 (7)软件过程 模块A 模块A 图46一个模块内的软件过程 程序结构描述了整个程序的控制层次关系和各个部分的接口情况,而图46所示的软件
5 灵活多样,但典型的数据结构种类是有限的,它们是构成一些更复杂结构的基本构件块。图 4.5 表示了这些典型的数据结构。 图 4.5 典型的数据结构 标量是最简单的一种数据结构。所谓标量项就是单个的数据元素,例如一个布尔量、整 数、实数或一个字符串。可以通过名字对它们进行存取。 若把多个标量项组织成一个表或者顺序邻接为一组时,就形成了顺序向量。顺序向量又 称为一维数组。通常可以通过下标及数组名来访问数组中的某一元素。把顺序向量扩展到二 维、三维,直至任意维,就形成了 n 维向量空间。最常见的 n 维向量空间是二维矩阵。 链表是一种更灵活的数据结构,它把不相邻的标量项、向量或空间结构用拉链指针链接 起来,使得它们可以像表一样得到处理。 组合上述基本数据结构可以构成其它数据结构。例如,可以用包含标量项、向量或 n 维空间的多重链表来建立分层结构和网络结构。而利用它们又可以实现多种集合的存储。 必须注意,数据结构和程序结构一样,可以在不同的抽象层次上表示。例如,一个栈是 一种线性结构的逻辑模型,其特点是只允许在结构的一端进行插入或删除运算。它可以用向 量实现,也可以用链表实现。 (7) 软件过程 图 4.6 一个模块内的软件过程 程序结构描述了整个程序的控制层次关系和各个部分的接口情况,而图 4.6 所示的软件
过程则着重描述各个模块的处理细节。 软件过程必须提供精确的处理说明,包括事件的顺序、正确的判定点、重复的操作直至 数据的组织和结构等等。程序结构与软件过程是有关系的。对每个模块的处理必须指明该模 块所在的上下级环境。软件过程遵从程序结构的主从关系,因此它也是层次化的。 (8)信息隐蔽 如何分解一个软件才能得到最佳的模块组合?为了明确怎样去做,需要了解什么是“信 息隐蔽”。由 parnas提倡的信息隐蔽是指,每个模块的实现细节对于其它模块来说是隐蔽的 就是说,模块中所包含的信息(包括数据和过程)不允许其它不需要这些信息的模块使用。 通常有效的模块化可以通过定义一组独立的模块来实现,这些模块相互间的通信仅使用 对于实现软件功能来说是必要的信息。通过抽象,帮助我们确定组成软件的过程(或信息) 实体,通过信息隐蔽,则可定义和实施对模块的过程细节和局部数据结构的存取限制 由于一个软件系统在整个软件生存期内要经过多次修改,所以在划分模块时要采取措 施,使得大多数过程和数据对软件的其它部分是隐蔽的。这样,在将来修改软件时偶然引入 错误所造成的影响就可以局限在一个或几个模块内部,不致波及到软件的其它部分 3.软件体系结构 软件体系结构的三要素是程序构件(模块)的层次结构、构件之间交互的方式,以及数 据的结构。软件设计的一个目标是建立软件的体系结构表示。将这个表示当作一个框架,从 事更详细的设计活动。Shaw和 Garlan提出了在软件体系结构设计中应保持的几个性质: 结构:体系结构设计应当定义系统的构件,以及这些构件打包的方式和相互交互的 方式。如将对象打包以封装数据和操纵数据的处理,并通过相关操作的调用来进行交互 附属的功能:体系结构设计应当描述设计出来的体系结构如何实现对功能、性能 可靠性、安全性、适应性,以及其它的系统需求 可复用:体系结构设计应当描述为一种可复用的模式,以便在以后类似的系统族的 设计中使用它们。此外,设计应能复用体系结构中的构造块。 表41列出可能的软件构件,表42列出可能的构件间的连接方式。 表41软件构件分类 构件 寺点和示例 纯计算构件具有简单的输入/输出关系,没有运行状态的变化。例如,数值计算、过滤器( Filters) 转换器(Tra 存储枃件存放共享的、永久性的、结枃化的数据。例如,数据库、文件、符号表、超文本等。 管理构件执行的操作与运行状态紧密耦合。例如,抽象数据类型(ADT)、面向对象系统中的对 象、许多服务器( Servers)等。 控制构件管理其它构件运行的时间、时机及次序。例如,调度器、同步器等 链接构件在实体之间传递信息。例如,通信机制、用户界面等 表42构件之间的连接方式 寺点与示例 过程调用在某一个执行路径中传递执行指针。例如,普通过程调用(同一个命名空间)、远程过 程调用(不同的命名空间) 数据流 相互独立的处理通过数据流进行交互,在得到数据的同时被赋予控制权限。例如,UNIX 系统中的管道( pIpes)。 间接激活处理是因事件的发生而激活的,在处理之间没有直接的交互。例如,事件驱动系统、自
6 过程则着重描述各个模块的处理细节。 软件过程必须提供精确的处理说明,包括事件的顺序、正确的判定点、重复的操作直至 数据的组织和结构等等。程序结构与软件过程是有关系的。对每个模块的处理必须指明该模 块所在的上下级环境。软件过程遵从程序结构的主从关系,因此它也是层次化的。 (8) 信息隐蔽 如何分解一个软件才能得到最佳的模块组合?为了明确怎样去做,需要了解什么是“信 息隐蔽”。由 parnas 提倡的信息隐蔽是指,每个模块的实现细节对于其它模块来说是隐蔽的。 就是说,模块中所包含的信息(包括数据和过程)不允许其它不需要这些信息的模块使用。 通常有效的模块化可以通过定义一组独立的模块来实现,这些模块相互间的通信仅使用 对于实现软件功能来说是必要的信息。通过抽象,帮助我们确定组成软件的过程(或信息) 实体,通过信息隐蔽,则可定义和实施对模块的过程细节和局部数据结构的存取限制。 由于一个软件系统在整个软件生存期内要经过多次修改,所以在划分模块时要采取措 施,使得大多数过程和数据对软件的其它部分是隐蔽的。这样,在将来修改软件时偶然引入 错误所造成的影响就可以局限在一个或几个模块内部,不致波及到软件的其它部分。 3. 软件体系结构 软件体系结构的三要素是程序构件(模块)的层次结构、构件之间交互的方式,以及数 据的结构。软件设计的一个目标是建立软件的体系结构表示。将这个表示当作一个框架,从 事更详细的设计活动。Shaw 和 Garlan 提出了在软件体系结构设计中应保持的几个性质: ▪ 结构 :体系结构设计应当定义系统的构件,以及这些构件打包的方式和相互交互的 方式。如将对象打包以封装数据和操纵数据的处理,并通过相关操作的调用来进行交互。 ▪ 附属的功能 :体系结构设计应当描述设计出来的体系结构如何实现对功能、性能、 可靠性、安全性、适应性,以及其它的系统需求。 ▪ 可复用 :体系结构设计应当描述为一种可复用的模式,以便在以后类似的系统族的 设计中使用它们。此外,设计应能复用体系结构中的构造块。 表 4.1 列出可能的软件构件,表 4.2 列出可能的构件间的连接方式。 表 4.1 软件构件分类 构 件 特 点 和 示 例 纯计算构件 具有简单的输入∕输出关系,没有运行状态的变化。例如,数值计算、过滤器(Filters)、 转换器(Transformers)等。 存储构件 存放共享的、永久性的、结构化的数据。例如,数据库、文件、符号表、超文本等。 管理构件 执行的操作与运行状态紧密耦合。例如,抽象数据类型(ADT)、面向对象系统中的对 象、许多服务器(Servers)等。 控制构件 管理其它构件运行的时间、时机及次序。例如,调度器、同步器等。 链接构件 在实体之间传递信息。例如,通信机制、用户界面等。 表 4.2 构件之间的连接方式 连 接 特 点 与 示 例 过程调用 在某一个执行路径中传递执行指针。例如,普通过程调用(同一个命名空间)、远程过 程调用(不同的命名空间)。 数据流 相互独立的处理通过数据流进行交互,在得到数据的同时被赋予控制权限。例如,UNIX 系统中的管道(pipes)。 间接激活 处理是因事件的发生而激活的,在处理之间没有直接的交互。例如,事件驱动系统、自
动垃圾回收等 消息传递相互独立的处理之间有明确的交互,通过显式的离散方式的数据传递。这种传递可以是 同步的,也可以是异步的。例如,TCP/IP。 共享数据构件们通过同一个数据空间进行并发的操作。例如,多用户数据库、数据黑板系统 软件系统的体系结构经历了一个由低级到高级的发展过程。其间出现过下列体系结构 (1)数据流系统 批处理 管道及过滤器 这种结构中的每一个组成成份都有一套输入和输出数据,都依输入数据一处理一输出结 果的方式工作。进行数据变换的构件叫做过滤器,把数据从一个过滤器的输出导入到另一个 过滤器的输入,就叫做管道。在这种系统中,各个过滤器必须是相互独立的,每一个过滤器 对它的上游或下游的过滤器的情况是不知道的,也不能做任何假设。如果要求最终的输出结 果与各个过滤器的执行次序相关,就是一个数据流方式的体系结构。这种结构的优点是:数 据流程设计明确,直接支持复用,系统容易维护和升级,可以进行某些性能分析(如流量、 死锁等),容易支持并行计算。缺点是:容易导致把系统变成为简单的批处理作业。每一个过 滤器都要考虑相似的数据检验和处理,不能很好地支持交互式操作和反馈。对数据的格式不 能做过多的约定,使得每个过滤器的实现会更复杂。 (2)调用一返回系统 主程序/子程序 层次结构 在层次结构中,每一层都只与上下相邻的两层通信。每一层在利用下层基础服务的条件 下,为上层提供服务。最典型的例子就是各种虚拟机、X- window以及 OSL-ISO的7层网络 协议。这种结构的优点是:提供逐步抽象的编程支持,支持复用及系统升级。缺点是:不是 所有的系统都适合于建成层次结构,不能提供最佳性能。 面向对象的系统 在这种系统中,数据和其相关的基本操作被封装在一起。系统的构件是对象。对象具有 诸如封装、隐蔽、继承等良好的特性。对象必须自己维护其数据的一致性。这种结构的优点 是:将具体的实现部分隐蔽在对象中,使得代码之间的独立性很好,有利于将复杂的系统分 解为相互操纵的子任务。缺点是:对象间进行一般的调用时必须知道对方的标识。如果一个 对象的标识发生变化,所有显式调用这个对象操作的地方都要修改。对象之间的同步等还缺 乏现成的机制。 (3)独立构件系统 进程间通信 事件驱动 这种结构的特点是事件的发出者不必知道,也不应假设对该事件的具体处理过程。它的 优点是:提供了强大的可复用性支持。系统的重配置也很容易。缺点是:软件系统中的构件 在很大程度上依靠操作系统的调用。如果一个事件的处理需要多个进程处理,活动进程的激 活次序是不能保证的。经过事件传递数据也较受限制。事件处理中的逻辑处理实际上带有时 态性质,与一般的逻辑处理不一样。 (4)虚拟机 解释器 解释器的目的是实现一个虚拟机。这是一类比较复杂的体系结构,但是,如果进行结构 分析的话,就会发现看似不同的系统有着十分相似的结构
7 动垃圾回收等。 消息传递 相互独立的处理之间有明确的交互,通过显式的离散方式的数据传递。这种传递可以是 同步的,也可以是异步的。例如,TCP∕IP。 共享数据 构件们通过同一个数据空间进行并发的操作。例如,多用户数据库、数据黑板系统。 软件系统的体系结构经历了一个由低级到高级的发展过程。其间出现过下列体系结构。 (1) 数据流系统 ▪ 批处理 ▪ 管道及过滤器 这种结构中的每一个组成成份都有一套输入和输出数据,都依输入数据―处理―输出结 果的方式工作。进行数据变换的构件叫做过滤器,把数据从一个过滤器的输出导入到另一个 过滤器的输入,就叫做管道。在这种系统中,各个过滤器必须是相互独立的,每一个过滤器 对它的上游或下游的过滤器的情况是不知道的,也不能做任何假设。如果要求最终的输出结 果与各个过滤器的执行次序相关,就是一个数据流方式的体系结构。这种结构的优点是:数 据流程设计明确,直接支持复用,系统容易维护和升级,可以进行某些性能分析(如流量、 死锁等),容易支持并行计算。缺点是:容易导致把系统变成为简单的批处理作业。每一个过 滤器都要考虑相似的数据检验和处理,不能很好地支持交互式操作和反馈。对数据的格式不 能做过多的约定,使得每个过滤器的实现会更复杂。 (2) 调用―返回系统 ▪ 主程序∕子程序 ▪ 层次结构 在层次结构中,每一层都只与上下相邻的两层通信。每一层在利用下层基础服务的条件 下,为上层提供服务。最典型的例子就是各种虚拟机、X-window 以及 OSI-ISO 的 7 层网络 协议。这种结构的优点是:提供逐步抽象的编程支持,支持复用及系统升级。缺点是:不是 所有的系统都适合于建成层次结构,不能提供最佳性能。 ▪ 面向对象的系统 在这种系统中,数据和其相关的基本操作被封装在一起。系统的构件是对象。对象具有 诸如封装、隐蔽、继承等良好的特性。对象必须自己维护其数据的一致性。这种结构的优点 是:将具体的实现部分隐蔽在对象中,使得代码之间的独立性很好,有利于将复杂的系统分 解为相互操纵的子任务。缺点是:对象间进行一般的调用时必须知道对方的标识。如果一个 对象的标识发生变化,所有显式调用这个对象操作的地方都要修改。对象之间的同步等还缺 乏现成的机制。 (3) 独立构件系统 ▪ 进程间通信 ▪ 事件驱动 这种结构的特点是事件的发出者不必知道,也不应假设对该事件的具体处理过程。它的 优点是:提供了强大的可复用性支持。系统的重配置也很容易。缺点是:软件系统中的构件 在很大程度上依靠操作系统的调用。如果一个事件的处理需要多个进程处理,活动进程的激 活次序是不能保证的。经过事件传递数据也较受限制。事件处理中的逻辑处理实际上带有时 态性质,与一般的逻辑处理不一样。 (4) 虚拟机 ▪ 解释器 解释器的目的是实现一个虚拟机。这是一类比较复杂的体系结构,但是,如果进行结构 分析的话,就会发现看似不同的系统有着十分相似的结构
·推理系统 过程控制 这种结构要比较外界变量与目标常数的差异,经过控制策略的计算,反馈信号,控制需 要监测和控制的过程 (5)数据为中心的系统 数据库 超文本系统 数据黑板系统 这种结构的特点是:有两种构件:一是被共享的结构化数据,保存了所有的运行状态; 二是所有访问这些数据的独立的进程。如果是因为输入的数据而引起对共享数据的操作,那 么这种控制策略下的体系结构就叫做数据库。如果是由共享数据的当前状态触发相应的处理 进程,那么这种体系结构就叫做数据黑板。许多表面上看起来是其它种类的体系结构,可以 同时归入这种体系结构。 (6)其它 分布式处理的系统 特定领域的软件体系结构 (7)演进与综合 一般来讲,一个新系统的原型最开始出现时,往往以批处理方式进行组合;在进一步的 应用分析过程中,将逐步对交互控制方式、实时的反馈、集成化等方面提出需求,因而系统 将逐步向以数据为中心的系统过渡,中间还可能会经过一种或几种演化形态。 4.有效的模块设计 模块化方法带来了许多好处。一方面,模块化设计降低了系统的复杂性,使得系统容易 修改;另一方面,推动了系统各个部分的并行开发,从而提高了软件的生产效率 (1)模块 模块又称构件,在传统的方法中指用一个名字就可调用的一段程序。类似于高级语言中 的过程、函数等。它一般具有如下三个基本属性: 功能:即指该模块实现什么功能,做什么事情。 逻辑:即描述模块内部怎么做。 状态:即该模块使用时的环境和条件 在描述一个模块时,还必须按模块的外部特性与内部特性分别描述。模块的外部特性是 指模块的模块名、参数表、以及给程序以至整个系统造成的影响。而模块的内部特性则是指 完成其功能的程序代码和仅供该模块内部使用的数据 对于模块的外部环境(例如需要调用这个模块的上级模块)来说,只需要了解这个模块 的外部特性足够了,不必了解它的内部特性。而软件设计阶段,通常是先确定模块的外部特 性,然后再确定它的内部特性。 (2)模块独立性 所谓模块的独立性,是指软件系统中每个模块只涉及软件要求的具体的子功能,而和软 件系统中其它的模块的接口是简单的。例如,若一个模块只具有单一的功能且与其它模块没 有太多的联系,那么,我们则称此模块具有模块独立性 般采用两个准则度量模块独立性。即模块间的耦合和模块的内聚。 (3)内聚性 内聚是模块功能强度(一个模块内部各个元素彼此结合的紧密程度)的度量。一个内聚 程度高的模块(在理想情况下)应当只做一件事。一般模块的内聚性分为七种类型
8 ▪ 推理系统 ▪ 过程控制 这种结构要比较外界变量与目标常数的差异,经过控制策略的计算,反馈信号,控制需 要监测和控制的过程。 (5) 数据为中心的系统 ▪ 数据库 ▪ 超文本系统 ▪ 数据黑板系统 这种结构的特点是:有两种构件:一是被共享的结构化数据,保存了所有的运行状态; 二是所有访问这些数据的独立的进程。如果是因为输入的数据而引起对共享数据的操作,那 么这种控制策略下的体系结构就叫做数据库。如果是由共享数据的当前状态触发相应的处理 进程,那么这种体系结构就叫做数据黑板。许多表面上看起来是其它种类的体系结构,可以 同时归入这种体系结构。 (6) 其它 ▪ 分布式处理的系统 ▪ 特定领域的软件体系结构 (7) 演进与综合 一般来讲,一个新系统的原型最开始出现时,往往以批处理方式进行组合;在进一步的 应用分析过程中,将逐步对交互控制方式、实时的反馈、集成化等方面提出需求,因而系统 将逐步向以数据为中心的系统过渡,中间还可能会经过一种或几种演化形态。 4. 有效的模块设计 模块化方法带来了许多好处。一方面,模块化设计降低了系统的复杂性,使得系统容易 修改; 另一方面,推动了系统各个部分的并行开发,从而提高了软件的生产效率。 (1) 模块 模块又称构件,在传统的方法中指用一个名字就可调用的一段程序。类似于高级语言中 的过程、函数等。它一般具有如下三个基本属性: ▪ 功能:即指该模块实现什么功能,做什么事情。 ▪ 逻辑:即描述模块内部怎么做。 ▪ 状态:即该模块使用时的环境和条件。 在描述一个模块时,还必须按模块的外部特性与内部特性分别描述。模块的外部特性是 指模块的模块名、参数表、以及给程序以至整个系统造成的影响。而模块的内部特性则是指 完成其功能的程序代码和仅供该模块内部使用的数据。 对于模块的外部环境(例如需要调用这个模块的上级模块)来说,只需要了解这个模块 的外部特性足够了,不必了解它的内部特性。而软件设计阶段,通常是先确定模块的外部特 性,然后再确定它的内部特性。 (2) 模块独立性 所谓模块的独立性,是指软件系统中每个模块只涉及软件要求的具体的子功能,而和软 件系统中其它的模块的接口是简单的。例如,若一个模块只具有单一的功能且与其它模块没 有太多的联系,那么,我们则称此模块具有模块独立性。 一般采用两个准则度量模块独立性。即模块间的耦合和模块的内聚。 (3) 内聚性 内聚是模块功能强度(一个模块内部各个元素彼此结合的紧密程度)的度量。一个内聚 程度高的模块(在理想情况下)应当只做一件事。一般模块的内聚性分为七种类型
高 内聚性 低 功能内聚信息内聚|通信内聚|过程内聚时间内聚逻辑内聚|巧合内聚 强 模块独立性 功能单 功能分散 在上面的关系中可以看到,位于高端的几种内聚类型最好,位于中段的几种内聚类型是 可以接受的,但位于低端的内聚类型很不好,一般不能使用。因此,人们总是希望一个模块 的内聚类型向高的方向靠。模块的内聚在系统的模块化设计中是一个关键的因素。 巧合内聚(偶然内聚):当几个模块内凑巧有一些程序段代码相同,又没有明确表现 出独立的功能,把这些代码独立出来建立的模块即为巧合内聚模块。它是内聚程度最低的模 块。缺点是模块的内容不易理解,不易修改和维护 ■逻辑内聚:这种模块把几种相关的功能组合在一起,每次被调用时,由传送给模块 的控制型参数来确定该模块应执行哪一种功能。逻辑内聚模块比巧合内聚模块的内聚程度要 高。因为它表明了各部分之间在功能上的相关关系 时间内聚(经典内聚):这种模块大多为多功能模块,但要求模块的各个功能必须在 同一时间段内执行。例如初始化模块和终止模块。时间内聚模块比逻辑内聚模块的内聚程度 又稍高一些。在一般情形下,各部分可以以任意的顺序执行,所以它的内部逻辑更简单。 过程内聚:使用流程图做 为工具设计程序的时侯,常常通过 加工记录 流程图来确定模块划分。把流程图 打印 中的某一部分划出组成模块,就得 检验结 到过程内聚模块。这类模块的内聚 获得AB数据 程度比时间内聚模块的内聚程度文件(读文件) 计算A结果 更强一些 合并)你 通信内聚:如果一个模块 文件 计算 内各功能部分都使用了相同的输 B 入数据,或产生了相同的输出数 AB数据 据,则称之为通信内聚模块。通常, A/B卡片/读卡 片文件 通信内聚模块是通过数据流图来 定义的。如图47所示。 图47通信内聚模块 信息内聚(顺序内聚):这 种模块完成多个功能,各个功能都在同一数据 查找登录删除修改 结构上操作,每一项功能有一个唯一的入口点。 例如,图48所示的模块具有4个功能,由于模块 的所有功能都是基于同一个数据结构(符号 表),因此,它是一个信息内聚的模块。 信息内聚模块可以看成是多个功能内聚模 块的组合,并且达到信息的隐蔽。即把某个数 据结构、资源或设备隐蔽在一个模块内,不为 别的模块所知晓。当把程序某些方面细节隐藏 图48信息内聚模块 在一个模块中时,就增加了模块的独立性。 功能内聚:一个模块中各个部分都是为完成一项具体功能而协同工作,紧密联系, 不可分割的。则称该模块为功能内聚模块。功能内聚模块时内聚性最强的模块
9 图 4.8 信息内聚模块 在上面的关系中可以看到,位于高端的几种内聚类型最好,位于中段的几种内聚类型是 可以接受的,但位于低端的内聚类型很不好,一般不能使用。因此,人们总是希望一个模块 的内聚类型向高的方向靠。模块的内聚在系统的模块化设计中是一个关键的因素。 ▪ 巧合内聚(偶然内聚):当几个模块内凑巧有一些程序段代码相同,又没有明确表现 出独立的功能,把这些代码独立出来建立的模块即为巧合内聚模块。它是内聚程度最低的模 块。缺点是模块的内容不易理解,不易修改和维护。 ▪ 逻辑内聚 :这种模块把几种相关的功能组合在一起,每次被调用时,由传送给模块 的控制型参数来确定该模块应执行哪一种功能。逻辑内聚模块比巧合内聚模块的内聚程度要 高。因为它表明了各部分之间在功能上的相关关系。 ▪ 时间内聚(经典内聚):这种模块大多为多功能模块,但要求模块的各个功能必须在 同一时间段内执行。例如初始化模块和终止模块。时间内聚模块比逻辑内聚模块的内聚程度 又稍高一些。在一般情形下,各部分可以以任意的顺序执行,所以它的内部逻辑更简单。 ▪ 过程内聚 :使用流程图做 为工具设计程序的时侯,常常通过 流程图来确定模块划分。把流程图 中的某一部分划出组成模块,就得 到过程内聚模块。这类模块的内聚 程度比时间内聚模块的内聚程度 更强一些。 ▪ 通信内聚 :如果一个模块 内各功能部分都使用了相同的输 入数据,或产生了相同的输出数 据,则称之为通信内聚模块。通常, 通信内聚模块是通过数据流图来 定义的。如图4.7所示。 ▪ 信息内聚(顺序内聚):这 种模块完成多个功能,各个功能都在同一数据 结构上操作,每一项功能有一个唯一的入口点。 例如,图4.8所示的模块具有4个功能,由于模块 的所有功能都是基于同一个数据结构(符号 表),因此,它是一个信息内聚的模块。 信息内聚模块可以看成是多个功能内聚模 块的组合,并且达到信息的隐蔽。即把某个数 据结构、资源或设备隐蔽在一个模块内,不为 别的模块所知晓。当把程序某些方面细节隐藏 在一个模块中时,就增加了模块的独立性。 ▪ 功能内聚 :一个模块中各个部分都是为完成一项具体功能而协同工作,紧密联系, 不可分割的。则称该模块为功能内聚模块。功能内聚模块时内聚性最强的模块。 图 4.7 通信内聚模块
(4)耦合性 耦合是模块之间的相对独立性(互相连接的紧密程度)的度量。它取决于各个模块之间 接口的复杂程度、调用模块的方式以及哪些信息通过接口。 般模块之间可能的连接方式有七种,构成耦合性的七种类型 低 耦合性 非直接耦合数据耦合标记耦合|控制耦合外部耦合公共耦合|内容耦合 强 模块独立性 弱 ■内容耦合:如果一个模块直接访问另一个模块的内部数据:或者一个模块不通过正 常入口转到另一模块内部;或者两个模块有一部分程序代码重迭:或者一个模块有多个入口, 则两个模块之间就发生了内容耦合。在内容耦合的情形,被访问模块的任何变更,或者用不 同的编译器对它再编译,都会造成程序出错。这种耦合是模块独立性最弱的耦合。 公共耦合:若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共 耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等 公共耦合的复杂程度随耦合模块的个数增加而显著增加。如图49所示,若只是两个模 块之间有公共数据环境,则公共耦合有两种情况:松散公共耦合和紧密公共耦合。只有在模 块之间共享的数据很多,且通过参数表传递不方便时,才使用公共耦合。 外部耦合:一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是 通过参数表传递该全局变量的信息,则称之为外部耦合。外部耦合引起的问题类似于公共耦 合,区别在于在外部耦合中不存在依赖于一个数据结构内部各项的物理安排。 B B ommon公共数据区 common公共数据区 (a)松散的公共耦合 (b)紧密的公共耦合 图49公共耦合 控制耦合:如果一个模块通过传送开关、标志、 A 名字等控制信息,明显地控制选择另一模块的功能,就是 Flag 控制耦合。如图4.10所示。这种耦合的实质是在单一接 口上选择多功能模块中的某项功能。因此,对被控制模块 Flag 的任何修改,都会影响控制模块。另外,控制耦合也意味 着控制模块必须知道被控制模块内部的一些逻辑关系,这 些都会降低模块的独立性 标记耦合:如果一组模块通过参数表传递记录信 息,就是标记耦合。事实上,这组模块共享了某一数据结 构的子结构,而不是简单变量。这要求这些模块都必须清 楚该记录的结构,并按结构要求对记录进行操作 图4.10控制耦合 ■数据耦合:如果一个模块访问另一个模块时,彼此之间是通过数据参数(不是控制 参数、公共数据结构或外部变量)来交换输入、输出信息的,则称这种耦合为数据耦合。数 据耦合是松散的耦合,模块之间的独立性比较强。 非直接耦合:如果两个模块之间没有直接关系,它们之间的联系完全是通过主模块
10 图 4.10 控制耦合 (4) 耦合性 耦合是模块之间的相对独立性(互相连接的紧密程度)的度量。它取决于各个模块之间 接口的复杂程度、调用模块的方式以及哪些信息通过接口。 一般模块之间可能的连接方式有七种,构成耦合性的七种类型。 ▪ 内容耦合 :如果一个模块直接访问另一个模块的内部数据;或者一个模块不通过正 常入口转到另一模块内部;或者两个模块有一部分程序代码重迭;或者一个模块有多个入口, 则两个模块之间就发生了内容耦合。在内容耦合的情形,被访问模块的任何变更,或者用不 同的编译器对它再编译,都会造成程序出错。这种耦合是模块独立性最弱的耦合。 ▪ 公共耦合 :若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共 耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等。 公共耦合的复杂程度随耦合模块的个数增加而显著增加。如图 4.9 所示,若只是两个模 块之间有公共数据环境,则公共耦合有两种情况:松散公共耦合和紧密公共耦合。只有在模 块之间共享的数据很多,且通过参数表传递不方便时,才使用公共耦合。 ▪ 外部耦合 :一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是 通过参数表传递该全局变量的信息,则称之为外部耦合。外部耦合引起的问题类似于公共耦 合,区别在于在外部耦合中不存在依赖于一个数据结构内部各项的物理安排。 图 4.9 公共耦合 ▪ 控制耦合 :如果一个模块通过传送开关、标志、 名字等控制信息,明显地控制选择另一模块的功能,就是 控制耦合。如图 4.10 所示。这种耦合的实质是在单一接 口上选择多功能模块中的某项功能。因此,对被控制模块 的任何修改,都会影响控制模块。另外,控制耦合也意味 着控制模块必须知道被控制模块内部的一些逻辑关系,这 些都会降低模块的独立性。 ▪ 标记耦合 :如果一组模块通过参数表传递记录信 息,就是标记耦合。事实上,这组模块共享了某一数据结 构的子结构,而不是简单变量。这要求这些模块都必须清 楚该记录的结构,并按结构要求对记录进行操作。 ▪ 数据耦合 :如果一个模块访问另一个模块时,彼此之间是通过数据参数(不是控制 参数、公共数据结构或外部变量)来交换输入、输出信息的,则称这种耦合为数据耦合。数 据耦合是松散的耦合,模块之间的独立性比较强。 ▪ 非直接耦合 :如果两个模块之间没有直接关系,它们之间的联系完全是通过主模块