结构化设计
软件设计是软件生存周期的重要组成部分,主要包括体系结构设计、接口设计、数据设计和过程设计。
结构化设计(structured design,SD)方法是一种面向数据流的设计方法,它是以结构化分析阶段所产生的文档(包括数据流图、数据字典和软件需求说明书)为基础,自顶向下,逐步求精和模块化的过程。结构化设计通常可分为概要设计和详细设计。概要设计的任务是确定软件系统的结构,进行模块划分,确定每个模块的功能、接口,以及模块间的调用关系。详细设计的任务是为每个模块设计实现的细节。
1)软件设计的重要概念和基本原则
在具体结构化设计方法之前,先介绍与软件设计有关的几个重要概念和基本原则。
①模块化
模块是指执行某一特定任务的数据和可执行语句等程序元素的集合,通常是指可通过名字来访问的过程、函数、子程序或宏调用等。
模块化就是将一个待开发的软件划分成若干个可完成某一子功能的模块,每个模块可独立地开发、测试,最后组装成完整的程序。模块化的依据是,如果一个问题由多个问题组合而成,那么这个组合问题的复杂程度将大于分别考虑每个问题时的复杂程度之和。
一个模块有它的外部特征和内部特征。外部特征包括模块的接口和功能,内部特征包括模块的局部数据和程序代码。调用一个模块只需知道它的外部特征,而不必了解其内部特征。
模块的这一特征有助于实现Parnas提出的“信息隐蔽”原则。信息隐藏是提高软件可维护性的重要措施,在分解模块时,就应采取措施,将一些将来可能发生变化的因素隐含在某模块内,使将来因修改造成的影响尽可能地局限在一个或少数几个模块中,对于提高软件的可理解性、可修改性、可测试性和可移植性都有重要的作用。但Parnas只提供了重要的设计准则,而没有规定具体的工作步骤。
至于如何定义模块大小,Meyer定义了5条标准。
1)模块的可分解性:如果一种设计方法提供了将问题分解成子问题的系统化机制,它就能降低整个系统的复杂性,从而实现一种有效的模块化解决方案。
2)模块的可组装性:如果一种设计方法使现存的(可复用的)设计构件能被组装成新系统,它就能提供一种不需要一切从头开始的模块化解决方案。
3)模块的可理解性:如果一个模块可以作为一个独立的单位(不用参考其他模块)被理解,那么它就易于构造和修改。
4)模块的连续性:如果对系统需求的微小修改只导致对单个模块,而不是整个系统的修改,则修改引起的副作用就会被最小化。
5)模块的保护性:如果模块内部出现异常情况,并且它的影响限制在模块内部,则错误引起的副作用就会被最小化。
②内聚
内聚是指一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。设计时应该力求高内聚,理想内聚的模块应当恰好做一件事情。
内聚有如下的种类,它们之间的内聚度由弱到强排列。
1)偶然内聚:如果一个模块完成一组任务,这组任务彼此间即使有关系,其关系也是松散的,这个模块属于偶然内聚。
2)逻辑内聚:这种模块把几种逻辑上相关的功能组合在一起,每次被调用时,由传送给的模块参数来确定该模块应完成哪一种功能。
3)瞬时内聚:如果一个模块所包含的任务必须在同一时间间隔内执行,这个模块属于瞬时内聚,例如初始化模块。
4)过程内聚:如果一个模块的处理元素是相关的,而且必须按特定的次序执行,这个模块属于过程内聚。
5)通信内聚:如果一个模块的所有功能都通过使用公用数据而发生关系,这个模块属于通信内聚。
6)顺序内聚:如果一个模块的处理元素是相关的,而且必须顺序执行,通常一个处理元素的输出数据作为下一个处理元素的输入数据,则称为顺序内聚。
7)功能内聚:如果一个模块包括且仅包括为完成某一具体任务所必须的所有成份,或者说模块中所有成份结合起来是为了完成一个具体的任务,那么这个模块是功能内聚的。
③耦合
耦合是对一个软件结构内不同模块之间互联程度的度量。耦合强弱取决于模块间接口的复杂程度,进入或访问一个模块的点,以及通过接口的数据。
模块间的耦合程度强烈影响系统的可理解性、可修改性、可测试性和可靠性,在软件设计中应该追求尽可能松散耦合的系统。这样的系统中模块间联系简单,发生在某一模块的错误传播到整个系统的可能性就很小,研究、测试或维护任何一个模块不需要对系统的其他模块有很多了解。
耦合可以分成下列几种,它们之间的耦合度由高到低排列。
1)内容耦合:指两个模块之间出现了下列情况之一。一个模块访问另一模块的内部数据;一个模块不通过正常入口转到另一模块的内部;两个模块有一部分程序代码重叠;一个模块有多个入口。软件设计时应坚决禁止内容耦合,应设计成单入口、单出口的模块,避免病态连接。
2)公共耦合:多个模块引用一全局数据区的模式称为公共耦合。例如,C语言中的外部数据类型、磁盘文件等都是全局数据区。
3)外部耦合:当模块与软件以外的环境有关时就发生外部耦合。例如,输入/输出把一个模块与特定的设备、格式、通信协议耦合在一起。
4)控制耦合:如果一模块明显地把开关量、名字等信息送入另一模块,控制另一模块的功能,则称为控制耦合。
5)标记耦合:如果两个以上的模块都需要其余某一数据结构子结构时,不使用全局变量的方式而是用记录传递的方式,则称为标记耦合。
6)数据耦合:如果两个模块借助于参数表传递简单数据,则称为数据耦合。
7)非直接耦合:如果两个模块没有直接关系,它们之间的联系完全是通过主程序的控制和调用来实现的,则称为非直接耦合。
原则上讲,模块化设计总是希望模块之间的耦合表现为非直接耦合方式。但是,由于问题所固有的复杂性,有时则要根据实际情况全面权衡,选用其他类型的耦合。
模块的高内聚、低耦和原则称为模块独立原则。
④深度、宽度、扇出与扇入
深度表示软件结构中控制的层数。如果层数过多,则应考虑是否有许多管理模块过于简单,能否适当合并。
宽度是软件结构中同一个层次上的模块总数的最大值。一般说来,宽度越大,系统越复杂。对宽度影响最大的因素是模块的扇出。
一个模块的扇出是指该模块直接调用的下级模块的个数。扇出大表示模块的复杂度高,需要控制和协调过多的下级模块;但扇出过小(例如总是1)也不好。扇出过大一般是因为缺乏中间层次,应该适当增加中间层次的控制模块。扇出太小时可以把下级模块进一步分解成若干个子功能模块,或者合并到它的上级模块中去。
一个模块的扇入是指直接调用该模块的上级模块的个数。扇入大表示模块的复用程度高。
设计良好的软件结构通常顶层扇出比较大,中间扇出较少,底层模块则有大扇入。
但我们也应当注意,不应为了单纯追求深度、宽度、扇出与扇入的理想化而违背模块独立原则,分解或合并模块必须符合问题结构。
⑤作用域和控制域
模块的作用域是指受该模块内一个判定影响的所有模块的集合。模块的控制域是指该模块本身,以及被该模块直接或间接调用的所有模块的集合。软件设计时,模块的作用域应在控制域之内,作用域最好是做出判定的模块本身,以及它的直属下级模块。
⑥功能的可预测性
功能可预测是指对相同的输入数据能产生相同的输出。软件设计时应保证模块的功能是可以预测的。
2)概要设计
经过需求分析阶段的工作,系统必须已经清楚了“做什么”,概要设计的基本目的就是回答“概括地说,系统应该如何实现?”这个问题。概要设计的重要任务就是设计软件的结构,也就是要确定系统是由哪些模块组成的,以及这些模块相互间的关系。
SD方法采用结构图(Structure Chart)来描述程序的结构。构成程序结构图的主要成分有模块、调用和数据,结构图中的模块用矩形表示,在矩形框内可标上模块的名字。模块间如有箭头或直线相连,表明它们之间有调用关系。SD方法有时也使用层次图和HIPO图(层次图加输入/处理/输出图)。
整个概要设计过程主要包括。
第1步:复查基本系统模型。复查的目的是确保系统的输入数据和输出数据符合实际。
第2步:复查并精化数据流图。应该对需求分析阶段得到的数据流图认真复查,并且在必要时进行精简。不仅要确保数据流图给出了目标系统的正确的逻辑模型,而且应该使数据流图中每个处理都代表一个规模适中、相对独立的子功能。
第3步:确定数据流图的信息流类型。数据流图中从系统的输入数据流到系统的输出数据流的一连串连续变换形成了一条信息流。信息流大体可分为两种类型:变换流和事务流。
1)变换流:信息沿着输入通道进入系统,然后通过变换中心(也称主加工)处理,再沿着输出通道离开系统。具有这一特性的信息流称为变换流。具有变换流型的数据流图可明显地分成输入、变换(主加工)、输出三大部分。
2)事务流:信息沿着输入通道到达一个事务中心,事务中心根据输入信息(即事务)的类型在若干个动作序列(称为活动流)中选择一个执行,这种信息流称为事务流。事务流有明显的事务中心,各活动以事务中心为起点呈辐射状流出。
第4步:根据流类型分别实施变换分析或事务分析。变换分析是从变换流型的数据流图导出程序结构图。具体过程如下。
1)确定输入流和输出流的边界,从而孤立出变换中心。
2)完成第一级分解,设计模块结构的顶层和第一层。
3)完成第二级分解,也就是输入控制模块、变换控制模块和输出控制模块的分解,设计中、下层模块。
事务分析是从事务流型的数据流图导出程序结构图,具体过程如下。
1)确定事务中心和每条活动流的流特性。
2)将事务流型数据流图映射成高层的程序结构,分解出接收模块、发送模块(调度模块),以及发送模块所控制的下层所有的活动流模块。
3)进一步完成接受模块和每一个活动流模块的分解。
第5步:根据软件设计原则对得到的软件结构图进一步优化。
3)详细设计
概要设计已经确定了每个模块的功能和接口,详细设计的任务就是为每个模块设计实现的细节。详细设计阶段的根本目标是确定应该怎样具体地实现所要求的系统,得出对目标系统的精确描述。
结构化程序设计(Structured Programming,SP)采用自顶向下逐步求精的设计方法和单入口、单出口的控制结构。在设计一个模块的实现算法时先考虑整体后考虑局部,先抽象后具体,通过逐步细化,最后得到详细的实现算法。单入口、单出口的控制结构使程序的静态结构和动态执行过程一致,具有良好的结构,增强了程序的可读性。
应用于详细设计的工具主要包括以下几种。
1)程序流程图:程序流程图又称为程序框图,它是历史最悠久的描述过程设计的方法,然而它也是用得最混乱的一种方法。程序流程图的主要优点是对控制流程的描绘很直观,便于初学者掌握。但由于程序流程图中用箭头代表控制流,经常诱使程序员不顾结构化程序设计的精神而随意转移控制。程序流程图尽管有种种缺点,许多人建议停止使用它,但至今仍在广泛使用着。不过总的趋势是越来越多的人不再使用程序流程图了。
2)盒图(N–S图):盒图是由Nassi和Shneiderman提出的一种符合结构化设计原则的图形描述工具,它仅含五种基本的控制结构:顺序结构、IF-THEN-ELSE型分支结构、CASE型多分支结构、DO-WHILE和DO-UNTIL型循环结构、子程序结构。盒图具有以下特点。
功能域(即一个特定控制结构的作用域)明确,可以从盒图上一眼就看出来。
由于没有箭头,不可能任意转移控制。
容易确定局部和全程数据的作用域。
容易表示嵌套关系,也可以表示模块的层次结构。
坚持使用盒图作为详细设计的工具,可以使程序员逐步养成用结构化的方式思考问题和解决问题的习惯。
3)PAD图:PAD图是问题分析图(Problem Analysis Diagram)的英文缩写,它用二维树型结构的图表示程序的控制流,比较容易翻译成机器代码。PAD图具有以下特点。
使用表示结构化控制结构的PAD符号所设计出来的程序必然是结构化程序。
PAD图所描绘的程序结构十分清晰。
用PAD图表现程序逻辑,易读、易懂、易记。
容易将PAD图转换成高级语言源程序,这种转换可用软件工具自动完成。
PAD图既可表示程序逻辑,也可用于描绘数据结构。
PAD图的符号支持自顶向下、逐步求精方法的使用。
4)PDL:PDL是程序设计语言(Program Design Language)的英文缩写,也称为伪码,是一种以文本方式表示数据和处理过程的设计工具。PDL是一种非形式化语言,它对控制结构的描述是确定的,但控制结构内部的描述语法是不确定的,它可根据不同的应用领域和不同的设计层次灵活选用其描述方式,甚至可用自然语言描述。与程序语言(Programming Language)不同,PDL程序是不可执行的,但它可以通过转换程序自动转换成某种高级程序语言的源程序。
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删