196.整体设计

第5章  整体设计程序员

 

整体设计任务算法

• 系统方案设计数据库

    划分出组成系统的物理元素——程序、文件、数据库、人工过程和文档等等,可是每一个物理元素仍然处于黑盒子级。数据结构

• 体系结构设计app

    设计软件的结构,肯定系统中每一个程序是由哪些模块组成的,以及这些模块相互间的关系。数据库设计

 

5.1  设计过程

两个主要阶段组成:ide

系统设计阶段:肯定系统的具体实现方案;模块化

结构设计阶段:肯定软件结构。函数

 

 

    典型的整体设计过程包括下述9个步骤:工具

1. 设想供选择的方案

    以数据流图为基础,寻找实现目标系统的各类不一样的方案。

 

2. 选取合理的方案

    至少选取低成本、中等成本和高成本的三种方案。

    对每一个合理的方案分析员都应该准备下列资料:

(1) 系统流程图;

(2) 组成系统的物理元素清单;

(3) 成本/效益分析;

(4) 实现这个系统的进度计划。

 

3. 推荐最佳方案

 

4. 功能分解

肯定软件结构,从实现角度把复杂的功能进一步分解。

一般分为两个阶段完成:

    结构设计:肯定程序由哪些模块组成,以及这些模块之间的关系。整体设计阶段的任务

    过程设计:肯定每一个模块的处理过程。

                        详细设计阶段的任务

 

    功能分解致使数据流图的进一步细化,同时用IPO图简要描述细化后每一个处理的算法。

 

5. 设计软件结构

     把模块组织成良好的层次系统,顶层模块调用它的下层模块以实现程序的完整功能,每一个下层模块再调用更下层的模块,从而完成程序的一个子功能,最下层的模块完成最具体的功能。

   软件结构(即由模块组成的层次系统)能够用层次图或结构图来描绘,第5.4节将介绍这些图形工具。

    若是数据流图已经细化到适当的层次,能够直接从数据流图映射出软件结构,这就是第5.5节中将要讲述的面向数据流的设计方法。

 

6. 设计数据库

 

7. 制定测试计划

    开发早期考虑测试问题,可提升软件的可测试性。

 

8. 书写文档

(1) 系统说明。主要内容包括:

    系统流程图描绘的系统构成方案,组成系统的物理元素清单,成本/效益分析;

    精化的数据流图,用层次图或结构图描绘的软件结构,用IPO图描述的各个模块的算法,模块间的接口关系等。

(2) 用户手册

(3) 测试计划

(4) 详细的实现计划

(5) 数据库设计结果

 

9. 审查和复审

 

 

5.2  设计原理  

好的设计准则

• 模块化

• 抽象

• 逐步求精

• 信息隐藏和局部化

• 模块独立

 

5.2.1  模块化

模块: 是由边界元素限定的相邻程序元素(例如,数听说明,可执行的语句)的序列,并且有一个整体标识符表明它。按照模块的定义,过程、函数、子程序和宏等,均可做为模块。

   

模块化:把程序划分红独立命名且可独立访问的模块,每一个模块完成一个子功能,把这些模块集成起来构成一个总体,能够完成指定的功能,知足用户的需求。

模块化的根据:把复杂的问题分解成许多容易解决的小问题,原来的问题也就容易解决了。

模块化的后果:随着模块数目增长,设计模块间接口所须要的工做量也将增长。

    根据这两个因素,得出了图中的总成本曲线。每一个程序都有一个最适当的模块数目M,使得系统的开发成本最小。

   

图5.1 模块化和软件成本

 

5.2.1  模块化优势

• 使软件结构清晰,容易设计也容易阅读和理解。

•提升软件的可靠性。

     程序错误局限在有限的模块中,使软件容易测试和调试。

•提升了软件的可修改性。

     即便有变更,每每只涉及少数几个模块,

• 有助于软件开发工程的组织管理。

      一个大型程序由许多程序员分工编写,分配技术熟练的程序员编写困难的模块。

5.2.2  抽象

抽象:把事务类似的方面集中和归纳起来,暂时忽略它们之间的差别。

    抽象就是抽出事物的本质特性而暂时不考虑它们的细节。

5.2.3  逐步求精

Miller法则:一我的在任什么时候候都只能把注意力集中在(7±2)个知识块上。

逐步求精是人类解决复杂问题时采用的基本方法。

 

逐步求精其实是自顶向下的细化过程。

    高抽象级别定义的功能,仅做概念性地描述,没有提供功能的内部工做状况。

    求精要求设计者细化原始陈述,随着每一个后续求精(即细化)步骤的完成,提供愈来愈多的细节。

 

    抽象与求精是一对互补的概念。

    抽象使得设计者可以说明过程和数据,同时却忽略低层细节。

    求精则帮助设计者在设计过程当中逐步揭示出低层细节。

    这两个概念都有助于设计者在设计演化过程当中创造出完整的设计模型。

5.2.4  信息隐藏和局部化

信息隐藏:一个模块内包含的信息(过程和数据)对于不须要这些信息的模块来讲,是不能访问的。

局部化:是指把一些关系密切的软件元素物理地放得彼此靠近。

在模块中使用局部数据元素是局部化的一个例子。有助于实现信息隐藏。

5.2.5  模块独立

    模块独立的概念是模块化、抽象、信息隐藏和局部化概念的直接结果。

模块独立:具备独立功能并且和其余模块之间没有过多的相互做用的模块。

模块独立的重要性:

第一,具备独立模块的软件比较容易开发。

第二,独立模块比较容易测试和维护。

    错误传播范围小,容易测试与修改,须要扩充功能时可以“插入”模块。

 

 模块独立程度的度量标准:内聚和耦合。

耦合:模块间互相依赖(链接)的紧密程度;

内聚:模块内部各个元素彼此结合的紧密程度。

 

1. 耦合

 影响耦合强度的因素

• 一个模块对另外一个模块的引用

• 一个模块向另外一个模块传递的数据量

• 一个模块施加到另外一个模块的控制的数量

• 模块之间接口的复杂程度

 

耦合的类型

•         内容耦合  强

•         公共耦合  ¦

•         控制耦合  ¦

•         标记耦合  ↓

•         数据耦合  弱

 

 

 在软件设计中应该追求尽量松散耦合。

 

    对模块的测试或维护时,不须要对系统的其余模块有不少了解。

 此外,因为模块间联系简单,发生在一处的错误传播到整个系统的可能性就很小。

 

    所以,模块间的耦合程度强烈影响系统的可理解性、可测试性、可靠性和可维护性。

 

 

数据耦合

数据耦合:模块彼此间经过参数交换信息,交换的信息仅仅是数据。

 数据耦合是低耦合。系统中至少必须存在这种耦合,由于只有当某些模块的输出数据做为另外一些模块的输入数据时,系统才能完成有价值的功能。

    通常说来,一个系统内能够只包含数据耦合。

 

 

 

标记耦合
( Stamp coupled )

• 若两个模块间传递的参数中至少有一个是数据结构,如字符串或记录,而且在模块中仅用到该数据结构中的部分元素,则称这两个模块之间存在标记耦合。

    在这种状况下,被调用的模块可使用的数据多于它确实须要的数据,将致使对数据的访问失去控制,从而给计算机犯罪提供了机会。

 

 

 

控制耦合
(Control coupled)

控制耦合:一个模块向另外一个模块传递控制信息,接收信息的模块的动做根据信息值进行调整。  

    控制耦合是中等程度的耦合,它增长了系统的复杂程度。在把模块适当分解以后一般能够用数据耦合代替它。

 

 

 

公共耦合
( Common coupled )

•两个模块共享全局的数据区域,称他们为公共耦合。

•不要使用全局变量

 

 

 

公共耦合

  耦合的复杂程度随耦合模块的个数而变化,随个数的增长显著增长。

     两个模块的公共耦合有两种可能:

(1) 一个模块往公共环境送数据,另外一个模块从公共环境取数据。这是数据耦合的一种形式,是比较松散的耦合。

(2) 两个模块都既往公共环境送数据又从里面取数据,这种耦合比较紧密,介于数据耦合和控制耦合之间。

 

 

内容耦合
( Content coupled )

•内容耦合的三种状况:

•一个模块修改另外一个模块的语句 (Lisp 具备此种能力)

•一个模块引用或者修改另外一个模块内部的数据

•一个模块不经过正常入口而跳转到另外一个模块的内部

 

 

模块独立性与耦合的关系

 

 

 

 总之,耦合是影响软件复杂程度的一个重要因素。应该采起下述设计原则:

 尽可能使用数据耦合,少用控制耦合和标记耦合,限制公共环境耦合的范围,彻底不用内容耦合。

 

 

 

2. 内聚

 内聚标志一个模块内各个元素彼此结合的紧密程度。理想内聚的模块只作一件事情。

 度量一个模块内部各成分之间相互关联的强度

 

•         偶然内聚  弱

•         逻辑内聚

•         时间内聚

•         过程内聚  ↓

•         通讯内聚

•         顺序内聚

•         功能内聚  强

 

 

偶然内聚
coincidental cohesion

    若是一个模块的各成分之间毫无关系,则称为偶然内聚。

 

 

逻辑内聚
logically cohesive

几个逻辑上相关的功能被放在同一模块中。

 

 

时间内聚
Temporal cohesion

若是一个模块完成的功能只是由于时间因素关联在一块儿。

 

 

 

时间内聚
Temporal cohesion

•Consider a module called "On_Really_Bad_Failure" that is invoked when a Really_Bad_Failure happens. The module performs several tasks that are not functionally similar or logically related, but all tasks need to happen at the moment when the failure occurs.

•The module might

cancel all outstanding requests for services

cut power to all assembly line machines

notify the operator console of the failure

make an entry in a database of failure records

 

 

过程内聚
procedurally cohesive

若是一个模块内部的各个处理成分必须以特定的次序执行,则称为过程内聚。

 

通讯内聚
communicationally cohesive

若是一个模块的全部成分都操做同一数据集或生成同一数据集,则称为通讯内聚。

 

 

 

顺序内聚
sequentially cohesive

若是一个模块的各个成分和同一个功能密切相关,并且一个成分的输出做为另外一个成分的输入,则称为顺序内聚。

 

 

 

 

功能内聚
functionally cohesive

模块的全部成分对于完成单一的功能都是基本的。

 

 

设计时尽可能使用高内聚,低耦合模块。

 

• 高内聚:尽可能使用内聚度高的模块;中内聚也可;低内聚很坏,不要采用。

低内聚:偶然内聚,逻辑内聚,时间内聚

中内聚:过程内聚,通讯内聚

高内聚:顺序内聚,功能内聚;

 

• 低耦合:尽可能使用数据耦合,少用控制耦合和标记耦合,限制公共耦合的范围,彻底不用内容耦合。

 

 

5.3  启发式规则

改进软件设计,提升软件质量的途径。

•改进软件结构提升模块独立性

•模块规模应该适中

•深度、宽度、扇出和扇入应适中

•模块的做用域应该在控制域以内

•力争下降模块接口的复杂性

•设计单入口和单出口的模块

•模块功能应该能够预测

 

1. 改进软件结构提升模块独立性

•下降耦合

•提升内聚

 

2. 模块规模应该适中

•模块规模:

不超过60行

超过30,可理解程度迅速降低

•模块数量:

适中

 

模块过大:每每是因为分解不充分。

模块太小:致使模块数目过多,使系统接口复杂。能够把它合并到上级模块中去。

 

3. 深度、宽度、扇出和扇入都应适当

• 深度:表示软件结构中控制的层数。

      能粗略地标志一个系统的大小和复杂程度。若是层数过多,应考虑管理模块是否过度简单,可否适当合并。

• 宽度:软件结构内同一个层次上的模块总数的最大值。

      宽度越大系统越复杂。对宽度影响最大的因素是模块的扇出。

 

扇出:是一个模块直接控制(调用)的模块数目。

     扇出过大意味着模块过度复杂,须要控制和协调的下级模块过多;扇出太小(例如老是1)也很差。

    一般是3或4(上限是5~9)。

扇出太大:缺少中间层次,应适当增长中间层次的控制模块。

扇出过小:把下级模块进一步分解成若干个子功能模块,或者合并到它的上级模块中去。

    分解或合并模块应符合问题结构,不能违背模块独立原理。

 

扇入:代表有多少个上级模块。扇入越大则共享该模块的上级模块数目越多,这是有好处的。

    好的软件结构一般顶层扇出比较高,中层扇出较少,底层模块有高扇入。

    系统的模块结构呈现为“葫芦形”。

 

 

 

4. 模块的做用域应该在控制域以内

• 模块的做用域:受该模块内一个断定影响的全部模块的集合。

• 模块的控制域:模块自己以及全部直接或间接从属于它的模块的集合。

    例如,在图5.2中模块A的控制域是A、B、C、D、E、F等模块的集合。

    受断定影响的模块应在作出断定的那个模块的控制域以内。

 

 

图5.2 模块的做用域和控制域

 

5. 力争下降模块接口的复杂程度

    应该仔细设计模块接口,使得信息传递简单而且和模块的功能一致。

 

6. 设计单入口单出口的模块

    使模块间避免出现内容耦合。当从顶部进入模块而且从底部退出来时,软件是比较容易理解的,所以也是比较容易维护的。

 

7. 模块功能应该能够预测

    只要输入的数据相同就产生一样的输出,这个模块的功能就是能够预测的。

    带有内部“存储器”的模块的功能多是不可预测的,不宜测试与维护。

 

 

5.4  描绘软件结构的图形工具  

5.4.1  层次图和HIPO图

层次图:用来描绘软件的层次结构。

    形式和描绘数据结构的层次方框图相同,但表现的内容却彻底不一样。

•层次图中的一个矩形框表明一个模块,方框间的连线表示调用关系

•层次方框图中的一个矩形框表明数据的子集,方框间的连线表示组成关系。

图5.3是层次图的一个例子。

  

图5.3 正文加工系统的层次图

 

 

HIPO: “层次图加输入/处理/输出图”的英文缩写。

    为了能使HIPO图具备可追踪性,在H图(层次图)里除了最顶层的方框以外,每一个方框都加了编号。编号规则和数据流图的编号规则相同。

例如,图5.3加了编号后获得图5.4。

    H图中每一个方框对应一张IPO图,描绘这个方框表明的模块的处理过程。每张IPO图内都应标出它所描绘的模块在H图中的编号。

 

图5.4 带编号的层次图(H图)

5.4.2  结构图

     结构图和层次图相似,也是描绘软件结构的图形工具。

结构图基本符号:

•方框——模块

•方框间连线——模块调用关系(上方的模块调用下方的模块)

•带注释的箭头——模块间传递的信息

•箭头尾部空心圆——数据信息

•箭头尾部实心圆——控制信息

 

 

图5.5 结构图的例子——产生最佳解的通常结构

 

    还有一些附加的符号,能够表示模块的选择调用或循环调用。

   

 图5.6 断定为真时调用A,为假时调用B

 

图5.7 模块M循环调用模块A、B、C

 

 

5.5 面向数据流的设计方法


5.5.1  概念

面向数据流的设计方法:把数据流图中的信息流映射成软件结构。信息流的类型决定了映射的方法。信息流有下述两种类型。

 

1. 变换流

变换流:具备较明显的输入、变换(或称主加工)和输出界面的数据流图。

参看图5.8

 

图5.8 变换流

 

2. 事务流

事务流:数据沿输入通路到达一个处理T,这个处理根据输入数据的类型在若干个动做序列中选出一个来执行。

此时数据流图形状如图5.9,是“以事务为中心的”。

图5.9中的处理T称为事务中心,它完成下述任务:

(1) 接收输入数据(输入数据又称为事务);

(2) 分析每一个事务以肯定它的类型;

(3) 根据事务类型选取一条活动通路。

 

图5.9 事务流

 

3. 设计过程

    图5.10(见书96页)说明了使用面向数据流方法逐步设计的过程。

 

 

5.5.2  变换分析

变换分析:把数据流图按预先肯定的模式映射成软件结构的一系列设计步骤的总称。

 

 

下面经过一个例子说明变换分析的方法。

1. 例子

考虑汽车数字仪表板的设计。

假设的仪表板将完成下述功能:

(1) 经过模数转换实现传感器和微处理机接口;

(2) 在发光二极管面板上显示数据;

(3) 指示每小时英里数(mph),行驶的里程,每加仑油行驶的英里数(mpg)等等;

(4) 指示加速或减速;

(5) 超速警告:若是车速超过55英里/小时,则发出超速警告铃声。

在需求分析阶段创建起相应的数据流图。

 

2. 设计步骤

第1步 复查基本系统模型。

复查的目的是确保系统的输入数据和输出数据符合实际。

第2步 复查并精化数据流图。

应该对需求分析阶段得出的数据流图认真复查,而且在必要时进行精化。使数据流图中每一个处理都表明一个规模适中相对独立的子功能。

假设在需求分析阶段产生的数字仪表板系统的数据流图如图5.11(见书97页)所示。

第3步 肯定数据流图具备变换特性仍是事务特性。

    通常地说,一个系统中的全部信息流均可以认为是变换流,可是,当遇到有明显事务特性的信息流时,建议采用事务分析方法进行设计。

     从图5.11看出,数据沿着两条输入通路进入系统,而后沿着5条通路离开,没有明显的事务中心。

    所以能够认为这个信息流具备变换流的总特征。

第4步 肯定输入流和输出流的边界,从而孤立出变换中心。

    对于汽车数字仪表板的例子,设计人员肯定的流的边界如图5.12(见书98页)所示。

第5步 完成“第一级分解”。

分解:就是分配控制的过程,对控制的自顶向下的分配----软件结构。

    图5.13说明了第一级分解的方法。位于软件结构最顶层的控制模块Cm协调下述从属的控制功能:

输入信息处理控制模块Ca:协调对全部输入数据的接收;

变换中心控制模块Ct:管理对内部形式的数据的全部操做;

输出信息处理控制模块Ce:协调输出信息的产生过程。

    数据流图被映射成一个特殊的软件结构,这个结构控制输入、变换和输出等信息处理过程。

 

图5.13 第一级分解的方法

    对于数字仪表板的例子,第一级分解得出的结构如图5.14所示。每一个控制模块的名字代表了为它所控制的那些模块的功能。

 

图5.14 数字仪表板系统的第一级分解

 

第6步 完成“第二级分解”。

第二级分解:就是把数据流图中的每一个处理映射成软件结构中一个适当的模块。

完成第二级分解的方法是:

•从变换中心的边界开始沿着输入通路向外移动,把输入通路中每一个处理映射成软件结构中Ca控制下的一个低层模块;

•而后沿输出通路向外移动,把输出通路中每一个处理映射成直接或间接受模块Ce控制的一个低层模块;

•最后把变换中心内的每一个处理映射成受Ct控制的一个模块。

       图5.15表示进行第二级分解的广泛途径。

 

图5.15 第二级分解的方法

 

    对于数字仪表板系统的例子,第二级分解的结果分别用图5.16,5.17和5.18描绘。

    这3张图表示对软件结构的初步设计结果。

 

图5.16 未经精化的输入结构

 

 

图5.17 未经精化的变换结构

 

 

图5.18 未经精化的输出结构

 

第7步 使用设计度量和启发式规则对第一次分割获得的软件结构进一步精化。

 根据模块独立原理进行精化。获得尽量高的内聚、尽量松散的耦合。需对初步分割获得的模块进行再分解或合并。

    具体到数字仪表板的例子,对于从前面的设计步骤获得的软件结构,还能够作许多修改:

• 输入结构中的模块“转换成rpm”和“收集sps”能够合并;

• 模块“肯定加速/减速”能够放在模块“计算mph”下面,以减小耦合;

• 模块“加速/减速显示”能够相应地放在模块“显示mph”的下面。

通过上述修改后的软件结构画在图5.19中。

 

图5.19 精化后的数字仪表板系统的软件结构

 

5.5.3  事务分析

    在数据流具备明显的事务特色时,也就是有一个明显的“发射中心”(事务中心)时,仍是以采用事务分析方法为宜。

事务分析的设计步骤和变换分析的设计步骤大部分相同或相似,主要差异仅在于:

    由数据流图到软件结构的映射方法不一样。

 

    由事务流映射成的软件结构包括一个接收分支和一个发送分支。

• 接收分支结构

    映射方法和变换分析映射出输入结构的方法很相像。

    从事务中心的边界开始,把沿着接收流通路的处理映射成模块。

• 发送分支结构

    包含一个调度模块,它控制下层的全部活动模块;

    而后把数据流图中的每一个活动流通路映射成与它的流特征相对应的结构。

     图5.20说明了上述映射过程。

 

 

图5.20 事务分析的映射方法

5.5.4  设计优化

    对第一次分割获得的软件结构,总能够根据模块独立原理和启发式设计规则进行优化。

   为了产生合理的分解,获得尽量高的内聚﹑尽量松散的耦合,最重要的是,为了获得一个易于实现﹑易于测试和易于维护的软件结构,应该对初步分割获得的模块进行再分解或合并。

    注意,设计优化应该力求作到在有效的模块化的前提下使用最少许的模块,以及在可以知足信息要求的前提下使用最简单的数据结构。

5.6  小结

整体设计阶段的基本目的:是用比较抽象归纳的方式肯定系统如何完成预约的任务,肯定系统的物理配置方案,肯定组成系统的每一个程序的结构。

整体设计阶段主要由两个小阶段组成。

首先须要进行系统设计;而后进行软件结构设计,肯定软件由哪些模块组成以及这些模块之间的动态调用关系。

层次图和结构图是描绘软件结构的经常使用工具。

 

在进行软件结构设计时应该遵循的最主要的原理是模块独立原理。

抽象和求精是一对互补的概念。在进行软件结构设计时就是由抽象到具体地构造出软件的层次结构。

 

软件工程师在开发软件的长期实践中积累了丰富的经验,总结这些经验得出一些颇有参考价值的启发式规则。

自顶向下逐步求精是进行软件结构设计的经常使用途径;

若是已经有了详细的数据流图,也可使用面向数据流的设计方法,由数据流图映射出软件结构。

    这样映射出来的只是软件的初步结构,还必须根据设计原理而且参考启发式规则,认真分析和改进软件的初步结构,以获得质量更高的模块和更合理的软件结构。

 

 

 

相关文章
相关标签/搜索