第四章-总体设计
- 了解总体设计的概念及在软件开发中的位置,了解总体设计的目标、步骤及基本任务。
- 掌握软件结构设计的基本概念及相应的方法。
- 掌握模块化、抽象、信息隐蔽、模块独立性、内聚性、耦合性等相关内容。
- 掌握软件结构形态中的相关特征、模块的影响范围、模块的控制范围及软件结构设计的优化准则等内容。
1、总体设计的任务
- 用比较抽象概括的方式确定系统如何完成预定的任务,确定系统的物理配置方案,并且进而确定组成系统的每个程序的结构。
- 设计软件的结构
2、总体设计的主要阶段
- 系统设计阶段
从数据流图出发构想完成系统功能的若干种合理的物理方案 - 结构设计阶段
确定程序由哪些模块组成以及这些模块之间的关系
3、总体设计的步骤
1、设想供选择的方案
需求分析阶段得到的数据流图是总体设计的极好的出发点。
2、选取合理的方案
- 系统流程图
- 组成系统的物理元素清单
- 成本/效益分析
- 实现这个系统的进度计划
3、推荐最佳方案
4、功能分解
功能分解导致数据流图的进一步细化,同时还应该用IPO图或其他适当的工具简要描述细化后每个处理的算法。
5、设计软件结构
- 顶层模块调用它的下层模块以实现程序的完整功能,每个下层模块再调用更下层的模块,从而完成程序的一个子功能,最下层模块完成最具体的功能。
- 软件结构(即由模块组成的层次系统)可以用层次图或结构图来描绘。
6、设计数据库
7、制定测试计划
8、书写文档
- 系统说明
用系统流程图描绘的系统构成方案
组成系统的物理元素清单
成本/效益分析
对最佳方案的概括描述
精化的数据流程图
用层次图或结构图描绘的软件结构
用IPO图或其他工具(PDL语言)简要描述的各个模块的算法
模块间的接口关系
需求、功能和模块三者之间的交叉参照关系 - 用户手册
- 测试计划
- 详细的实现计划
- 数据库设计结果
9、审查和复查
4、设计原理
(1)模块化
模块是构成程序的基本构件。
模块化就是把程序划分为独立命名而且可以独立访问的模块,每个模块完成一个子功能,把这些模块集成起来构成一个整体,可以完成指定的功能满足客户的需求。
总成本不仅与模块的数量有关,还与模块间接口的数量有关。
模块化的优点:
- 可以使软件结构清晰,不仅容易设计也容易阅读和理解
- 可以使软件容易测试和调试,因而有助于提高软件的可靠性
- 能够提高软件的可修改性
- 有助于软件开发工程的组织管理,一个复杂的大型程序可以由许多程序员分工编写不同的模块,并且可以进一步分配技术熟练的程序员编写困难的模块
(2)抽象
软件工程过程的每一步都是对软件解法的抽象层次的一次精化。
随着软件开发工程的进展,在软件结构每一层中的模块,表示了对抽象层次的一次精化。
抽象就是抽出事物的本质特性而暂时不考虑它们的细节。
软件结构顶层的模块,控制了系统的主要功能并且影响全局;
在软件结构底层的模块,完成对数据的一个具体处理。
用自顶向下由抽象到具体的方式分配控制的优点:

- 简化了软件的设计和实现
- 提高了软件的可理解性和可测试性
- 使软件更容易维护
(3)逐步求精
逐步求精与抽象是互补的概念,由抽象到具体地构造出软件的的层次结构。
定义:为了能集中精力解决主要问题而尽量推迟对问题细节的考虑。
- 抽象使得设计者能够说明过程和数据,同时却忽略了底层细节。
- 求精则帮助设计者在设计过程中逐步揭示出底层的细节。
求精实际上是细化过程。
求精是把一个时期内必须解决的种种问题按照优先级排序的技术。
(4)信息隐藏和局部化
信息隐藏:
应该这样设计和确定模块,使得一个模块内包含的信息(过程和数据)对于不需要这些信息的模块来说,是不能访问的。
局部化:
把一些关系密切的软件元素物理地彼此靠近。(局部化有助于信息隐藏)
细节隐藏:应该隐藏的不是有关模块的一切信息,而是模块的实现细节。
优点:
- 方便在以后的测试期间和维护期间修改软件
- 因为绝大多数数据和过程对于软件的其他部分而言是隐藏的,在修改期间由于疏忽而引入的错误就很少可能传播到软件的其他部分。
(5)模块独立
模块独立的概念是模块化、抽象、信息隐藏和局部化的直接结果。
开发具有独立功能而且和其他模块之间没有过多的相互作用的模块,就可以做到模块独立。
A.耦合
衡量不同模块彼此间相互依赖(连接)的紧密程度
影响耦合强弱的因素:
- 模块间接口的复杂程度
- 进入或访问一个模块的点
- 通过接口的数据
实现低耦合,采取下列措施:
- 耦合方式:采用非直接耦合,不采用直接耦合。
- 传递信息类型:尽量使用数据耦合,少使用控制耦合,外部耦合和公共耦合限制使用。
- 耦合数量:模块间相互调用时,传递参数最好只有一个。
耦合级别(好到差):
-
非直接耦合
两个模块之间没有联系,则它们之间为非直接耦合。
-
数据耦合
被调用模块的输入与输出是简单的参数或者是数据结构(该数据结构中的所有元素为被调用的模块使用),则它们之间为数据耦合。
-
标记耦合
如果两个模块都要使用同一数据结构的一部分,不是采用全局公共数据区共享,而是通过模块结构传递数据结构的一部分,则它们称为标记耦合。
-
控制耦合
如果两个模块中的一个模块给另一个模块传递控制信息,则它们具有控制耦合。
特点:接口单一,但仍然影响被控模块的内部逻辑。
-
外部耦合
当模块与软件的外部环境联结在一起,并受到约束时就出现较高程度的耦合,则它们之间为外部耦合。
-
公共耦合
如果两个模块都可以存取相同的全局数据,则它们之间是公共耦合。
存在的问题:
- 公共部分的改动将影响所有调用它的模块;
- 公共部分的数据存取无法控制;
- 复杂程度随耦合模块的个数增加而增加。
解决方法:通过使用信息隐藏来避免公共耦合。
-
内容耦合
产生的条件(满足其一即可):
- 一个模块访问另一个模块的内部数据
- 一个模块不通过正常入口而转移到另一个模块的内部
- 两个模块有一部分程序代码重叠(只可能出现在汇编程序中)
- 一个模块有多个入口(这意味着一个模块有几种功能)
B.内聚
衡量一个模块内部各个元素彼此结合的紧密程度
模块各元素执行相同的任务。
内聚级别(差到好;3低2中2高):
-
偶然内聚
为方便把不相干的元素组合在一起。
缺点:产品的可维护性退化;模块是不可复用的,增加软件成本。
解决途径:将模块分成更小的模块,每个小模块执行一个操作。
-
逻辑内聚
某段时间执行的若干功能任务放在同一模块中。
例如:系统的初始化
问题:不同的功能混在一个模块中,有时共用部分编码,使局部功能的修改牵动全局。
-
时间内聚
一个模块包含的任务必须在同一段时间内执行。
-
过程内聚
模块内的各功能元素是相关的,而且必须以特定次序执行。
例如:读写并更新数据记录
程序流程图
-
通信内聚
模块中所有元素都使用同一个输入数据和(或)产生同一个输出数据。
例如:某模块的各成分都利用一符号表进行操作;从同一磁带上读取不相干的数据。
问题:可能破坏独立性。
-
顺序内聚
模块的输出是下一个模块的输入。
数据流图
-
功能内聚
为完成一个任务把所需的全部功能组合在一起。
原则:力争高内聚,识别低内聚,可以使得设计的软件具有较高的功能独立性。
5、启发规则
-
争取低耦合、高内聚
增加内聚、减少耦合 -
模块规模适中
过大,分解不充分,不易理解;
太小,则开销过大、接口复杂。 -
适当控制模块结构参数
- 深度 = 分层的层数。过大表示分工过细。
- 宽度 = 同一层上模块的最大值。过大,表示系统复杂度大。
- 扇出 = 一个模块直接调用\控制的模块数。
- 扇入 = 直接调用该模块的模块数。
尽可能减少高扇出结构,随着深度增大扇入。
扇出太大一般是因为缺乏中间层次,应当适当增加中间层次的控制模块;
扇出太小时可以把下级模块进一步分解为若干个子功能模块,或合并到它的上级模块中去。
顶层扇出高,中间扇出少,底层高扇入。
-
模块的作用范围保持在该模块的控制范围内
- 控制域 = 该模块本身以及所有直接或间接从属于它的模块。
- 作用域 = 该模块中一个判断所影响的所有其他模块。
令作用域是控制域的子集:
- 把作判定的点往上移;
- 把那些在作用域但不在控制域内的模块移到控制域内。
-
降低接口的复杂程度
模块接口的复杂性是引起软件错误的一个主要原因。接口设计应该使得信息传递简单并且与模块的功能一致。 -
单出口单入口,避免内容耦合
易于理解和维护。 -
模块功能可预测
相同输入必产生相同输出。
6、面向数据流的设计方法
-
模块独立
面向数据流的设计方法吧数据流(信息流)映射成软件结构,数据流的类型决定了映射的方法。
-
变换流
信息沿输入通路进入系统,同时由外部形式变成内部形式,进入系统的信息通过变换中心,经加工处理后再沿输出通路变成外部形式离开软件系统。 -
事务流
数据沿输入通路到达一个处理,这个处理根据输入数据的类型在若干个动作序列中选出一个来执行。事务中心
-
接收输入数据(输入数据又称为事务)
-
分析每个事务以确定它的类型
-
根据事务类型选取一条活动通路
-
-
变换分析
经过若干步骤把具有变换流特点的数据流图按预先确定的模式映射成软件结构。 -
变换流分析
-
事务流分析
从事务流的DFD到程序结构图的转换。
与变换分析的主要差别仅在于由数据流图到软件结构的映射方法不同。- 接收分支
从事务中心的边界开始,把沿着接收流通路的处理映射成模块。 - 发送分支
包含一个调度模块,控制下层的所有活动模块。
- 接收分支
7、设计优化
应该在设计的早期阶段对软件结构进行精化,这种优化的可能,是把软件结构设计和过程设计分开的的真正优点之一。
设计优化应该力求做到在有效的模块化的前提下使用最少量的模块,以及在能满足信息要求的前提下使用最简单的数据结构。
对时间起决定作用的软件设计进行优化:
- 在不考虑时间因素的前提下开发并简化软件结构。
- 在详细设计阶段选出最耗费时间的那些模块,仔细地设计他们的处理过程(算法),以求提高效率。
- 使用高级程序设计语言编写程序。
- 在软件中孤立出那些大量占用处理机资源的模块。
- 必要时重新设计或用依赖于机器的语言重写上述大量占用资源的模块的代码,以求提高效率。
8、描绘软件结构的图形工具
- 层次图
用来描绘软件的层次结构
一个矩形框代表一个模块(与描绘数据结构的层次方框图不同)
连线表示调用关系(层次方框图表示组成关系)
适合在自顶向下设计软件的过程中使用 - HIPO图
层次图加输入/处理/输出图
在H图里除了最顶层的方框外,每个方框都加了编号→可追踪性 - 结构图
一个方框代表一个模块,方框之间的箭头(或直线)表示模块的调用关系
在结构图中还经常用带注释的箭头表示模块调用过程中来回传递的信息
- 数据(空心圆)
- 控制信息(实心圆)
选择调用、循环调用 - PS
层次图和结构图并不严格表示模块的调用次序。
层次图和结构图并不指明什么时候调用下层模块。
通常用层次图作为描绘软件结构的文档,结构图并不是很合适。
利用IPO图或数据字典中的信息得到模块调用时传递的信息,从而由层次图导出结构图的过程,可以作为检查设计正确性和评价模块独立性的好方法。