5.1 设计过程
1.设想供选择的方案
2.选择合理的方案
对每个合理的方案要提供:
- 系统流程图
- 组成系统的物理元素清单
- 成本/效益分析
- 实现这个系统的进度计划
3.推荐最佳方案
4.功能分解
5.设计软件结构
6.数据库设计
- 模式设计
- 子模式设计
- 完整性和安全性设计
- 优化
7.制定测试计划
8.书写文档
- 系统说明
- 用户手册
- 测试计划
- 详细的实现计划
- 数据库设计结果
9.审查和复审
5.2 设计原理
5.2.1 模块化
如果一个大型程序仅由一个模块组成,很难被人理解。
设函数C(x)定义问题x的复杂程度,函数E(x)定义解决问题x需要的工作量(时间)。
对于两个问题P1和P2,如果:C(P1)>C(P2),那么E(P1)>E(P2)。
根据解决问题的经验,有一个规律是:C(P1+P2)>C(P1)+C(P2),于是有E(P1+P2)>E(P1)+E(P2) 。
5.2.2 抽象
5.2.3 逐步求精
5.2.4 信息隐蔽和局部化
5.2.5 模块独立 ★
模块的独立性很重要,因为:
- 1)有效的模块化的软件比较容易开发出来;
- 2)独立的模块比较容易测试和维护。
模块独立程度可以由两个定性标准度量:耦合与内聚。
一、耦合
耦合:指软件结构内不同模块彼此之间相互依赖(连接)的紧密程度。
模块的偶合分四类:
1)数据耦合
两个模块之间只是通过参数交换信息,而且交换的信息仅仅是数据。
数据耦合是最低程度的耦合。
2)控制耦合
两个模块之间所交换的信息包含控制信息。
控制耦合是中等程度的耦合。
图中模块A的内部处理程序判断是执行C还是执行D,要取决于模块B传来的信息状态(Status)。
3)公用耦合
两个或多个模块通过一个公共区相互作用时的耦合。
公共区可以是:全程数据区、共享通信区、内存公共覆盖区、任何介质上的文件、物理设备等。
软件结构中存在大量的公用耦合时会给诊断错误带来困难。
图中存在公用耦合,假设模块A、C、E都存取全程数据区(如公用一个磁盘文件)中的一个数据项。如果A模块读取该项数据,然后调用C模块对该项重新计算,并进行数据更新。如果此时C模块错误地更新了该项数据,在往下的处理中模块E读该数据项时出现错误。表面上看,问题由模块E产生,实际上由模块C引起。
4)内容耦合
一个模块与另一个模块的内容直接发生联系。
内容耦合对维护会带来严重的困难。
程序中如果一个模块直接把程序转移到另一个模块中,或一个模块使用另一个模块的内部数据,都会产生内容耦合。内容耦合是最高程度的耦合,应该避免采用。
软件设计应追求尽可能松散耦合,避免强耦合,这样模块间的联系就越小,模块的独立性就越强,对模块的测试、维护就越容易。
因此建议:尽量使用数据耦合,少用控制耦合,限制公用耦合,完全不用内容偶合。
二、内聚
内聚:一个模块内部各个元素彼此结合的紧密程度。
它是衡量一个模块内部组成部分间整体统一性的度量。
常见的内聚有七类:
1)功能内聚(Functional Cohesion)
如果一个模块内所有处理元素完成一个,而且仅完成一个功能,则称为功能内聚。
功能内聚是最高程度的内聚。但在软件结构中,并不是每个模块都能设计成一个功能内聚模块。
2)顺序内聚(Sequential Cohesion)
如果一个模块内处理元素和同一个功能密切相关,而且这些处理元素必须顺序执行,则称为顺序内聚。
通常,顺序内聚中一个处理元素的输出是另一个处理元素的输入。
如图,一个求一元二次方程根的模块由三个处理元素组成,该模块中存在顺序内聚。
3)通信内聚(Communicational Cohesion)
如果一个模块中所有处理元素都使用同一个输入数据和(或)产生同一个输出数据,称为通信内聚。
通信内聚有时也称为数据内聚。
如图,模块A的处理单元将根据同一个数据文件FILE的数据产生不同的表格,因此它存在通信内聚。
4)过程内聚(Procedural Cohesion)
如果一个模块内的处理元素是相关的,而且必须以特定的次序执行,称为过程内聚。
过程内聚与顺序内聚的区别是:顺序内聚中是数据流从一个处理单元流到另一个处理单元,而过程内聚是控制流从一个动作流向另一个动作。
5)时间内聚(Temporal Cohesion)
如果一个模块包含的任务必须在同一段时间内执行,称为时间内聚。也称为瞬时内聚。
例如,完成各种初始化工作的模块,或者处理故障的模块都存在时间内聚。
如图,在“紧急故障处理模块”中,“关闭文件”、“报警”、“保留现场”等任务都必须无中断地同时处理。
6)逻辑内聚(Logical Cohesion)
如果模块完成的任务在逻辑上属于相同或相似的一类,称为逻辑内聚。
如图,A、B、C模块合并成ABC模块之后,ABC模块就是逻辑内聚模块。
对逻辑内聚模块的调用,常常需要有一个功能开关,由上层调用模块向它发出一个控制信号,在多个关联性功能中选择执行某一个功能。
这种内聚较差,增加了模块之间的联系,不易修改。
7)偶然内聚(Coincidental Cohesion)
如果一个模块由完成若干毫无关系的功能处理元素偶然组合在一起的,就叫偶然内聚。
偶然内聚是最差的一种内聚。
常犯这种错误的一种情况是:有时在写完程序后,发现一组语句在多处出现,于是为了节省空间而将这些语句作为一个模块设计,就出现偶然内聚。
如图,模块A、B、C出现公共代码段W,于是将W独立成一个模块,而W中这些语句并没有任何联系。
如果在测试中发现模块A不需要做“X=Y+Z”,而应该做“X=Y*Z”,此时对W的维护就很困难了。
软件设计中应该:力求做到高内聚,尽量少用中内聚,不用低内聚。
5.3 启发规则
1. 改进软件结构提高模块独立性
2. 模块规模应该适中
3. 深度、宽度、扇出和扇入都应适当 ★
深度:软件结构中控制的层数;
宽度:软件结构内同一个层次上的模块总数的最大值;
扇出:一个模块直接控制(调用)其它模块的数目;
扇入:一个模块被其它模块调用的数目。
对扇出、扇入过大的改进:
4. 模块的作用域应该在控制域之内 ★
作用域:受该模块内一个判定影响的所有模块的集合。
控制域:模块本身以及所有从属于它的模块的集合。
5. 降低模块接口的复杂度
如:QUAD-ROOT(TBL,X)
求一元二次方程的根的模块,其中TBL,X都为数组,分别代表方程的系数和方程的根。
应该使接口更简单,如:
QUAD-ROOT(A,B,C,ROOT1,ROOT2)
A、B、C是方程的系数,ROOT1,ROOT2是方程的根。
6.设计单入口、单出口的模块
7.模块功能应该可以预测
5.4 描绘软件结构的图形工具
5.4.1 层次图和HIPO图
层次图:
HIPO图是:“层次图+输入/处理/输出图”
5.4.2 结构图
小结
◇ 概要设计说明书
该说明书是概要实际阶段的工作成果,它应说明功能分配、模块划分、程序的总体结构、输入输出以及接口设计、运行设计、数据结构设计和出错处理设计等,为详细设计提供基础。
重点
模块划分原理、耦合和内聚
耦合度(松散性)、内聚度(高低)
作用域、控制域
软件层次结构的深度、宽度、扇入、扇出
5.5不用