总体设计过程:系统设计阶段确定系统的具体实现方案;结构设计阶段确定软件结构
文章目录
5.1 软件设计过程
1、设想供选择的方案
2、选取合理的方案
3、推荐最佳方案
4、功能分解(结构设计——过程设计)
5、设计软件结构
6、设计数据库
7、指定测试计划
8、书写文档
- 系统说明
- 用户手册
- 测试计划
- 详细的实现计划
- 数据库设计结果
9、审查和复审
5.2 设计原理
为了能获得高质量的设计结果,在软件设计过程中应该遵循下述原理(或准则)。
5.2.1 模块化与模块独立
模块化和模块独立是关系非常密切的两条设计原理。
1.模块化
所谓模块就是由边界元素限定的相邻程序元素的序列,并且有一个标识符代表它。模块化就是把程序划分成独立命名且可独立访问的模块。每个模块完成一个子功能,把全部模块集成起来构成一个整体,可以完成指定的功能,满足用户的需求。模块化可以使一个复杂的大型程序能被人的智力所管理,是软件应该具备的最重要的属性。
事实上,每个程序都相应地有一个最适当的模块数目,可使软件系统的开发成本最小。
采用模块化原理可以使软件结构清晰,不仅容易设计也容易阅读和理解。因为程序错误通常局限在有关的模块及它们之间的接口中,所以模块化使软件容易测试和调试,因而有助于提高软件的可靠性。因为变动往往只涉及少数几个模块,所以模块化能够提高软件的可修改性。模块化也有助于软件开发工程的组织管理。一个复杂的大型程序可以由许多程序员分工编写不同的模块,并且可以进一步分配技术熟练的程序员编写较复杂的模块。
2.模块独立
只有合理地划分和组织模块,才能获得模块化所带来的好处,极大地提高软件的质
指导模块划分和组织最重要的原理就是模块独立。量。开发具有独立功能而且和其他模块之间没有过多的相互作用的模块就可以做到模块独立。换句话说,希望这样设计软件结构,使得每个模块完成一个相对独立的特定子功能,并且和其他模块之间的关系很简单。
为什么模块的独立性很重要呢?主要有两条理由:第一,有效的模块化(即具有独立的模块)的软件比较容易开发出来。这是由于能够分割功能而且接口可以简化,当许多人分工合作开发同一个软件时,这个优点尤其重要。第二,独立的模块比较容易测试和维护。这是因为相对说来,修改设计和程序需要的工作量比较小,错误传播范围小,需要扩充功能时能够“插人”模块。总之,模块独立是软件设计的关键,而设计又是决定软件质量的关键环节
3.3.2 抽象
抽象是人类在认识复杂现象、解决复杂问题的过程中使用的最强有力的思维工具。
在现实世界中,一定事物、状态或过程之间总会存在某些相似的方面(共性),把这相似的方面集中和概括起来,暂时忽略它们之间的差异,这就是抽象。或者说抽象就是
取出事物的本质特性而暂时不考虑其他细节。由于人类思维能力的限制,如果一次面临的因素太多,是不可能作出精确思维的。设计复杂系统的唯一有效的方法是用层次的方式分析和构造它。一个复杂的软件系统应首先用一些高级的抽象概念来理解和构造,这些高级概念又可以用一些较低级的概念来理解和构造,如此进行下去,直至最低层的具体元素。这种层次的思维和解题方式必须反映在程序结构中,每级抽象层次中的一个概念
以某种方式对应于程序的一组成分。当考虑对任何问题的模块化解法时,可以提出许多抽象的层次。在抽象的最高层次使用问题环境的语言,以概括的方式叙述问题的解法;在较低抽象层次采用更过程化的方法,把面向问题的术语和面向实现的术语结合起来叙述问题的解法;最后,在最低的抽象次用可以直接实现的方式叙述问题的解法。
5.2.3 逐步求精
逐步求精是人类解决复杂问题时采用的基本方法,也是许多软件工程技术的基础可以把逐步求精定义为:“为了能集中精力解决主要问题而尽量推迟对问题细节的5实券谢闻关官齐累昌党斯是考虑。”
逐步求精之所以如此重要,是因为人类的认知过程遵守Miler法则:一个人在任何时候都只能把注意力集中在(7士2)个知识块上。
事实上,可以把逐步求精看做是一项把一个时期内必须解决的种种问题按优先级排序的技术。它让软件工程师把精力集中在与当前开发阶段最相关的那些问题上,而忽略那些对整体解决方案来说是重要的,然而目前还不需要考虑的细节性问题,这些细节将留到以后再考虑。逐步求精技术确保每个问题都将被解决,而且每个问题都在适当的时候解决,但是,在任何时候一个人都不需要同时处理7个以上知识块。
在用逐步求精方法解决问题的过程中,问题的某个特定方面的重要性是随时间变化的。最初,问题的某个方面可能无关紧要、无须考虑,但是后来同样的问题会变得很重要必须解决。因此,逐步求精方法能够确保每个问题都得到解决,并且在适当的时间解决在任何时刻都不需要同时处理7个以上知识块。
求精实际上是细化过程。从在高抽象级别定义的功能陈述(或信息描述)开始。也是说,该陈述仅仅概念性地描述了功能或信息,但是并没有提供功能的内部工作情况或信息的内部结构。求精要求设计者细化原始陈述,随着每个后续求精(细化)步骤的完成而提供越来越多的细节。
抽象与求精是一对互补的概念。抽象使得设计者能够说明过程和数据,同时却忽略低层细节。事实上,可以把抽象看做是一种通过忽略多余的细节,同时强调有关的细节而实现逐步求精的方法。求精则帮助设计者在设计过程中揭示出低层细节。这两个概都有助于设计者在设计演化过程中创造出完整的设计模型。
5.3.4 信息隐藏
信息隐藏原理指出,在设计软件模块时应该使得一个模块内包含的信息(过程和数据)对于不需要这些信息的模块来说是不能访问的。
实际上,应该隐藏的不是有关模块的一切信息,而是模块的实现细节。“隐藏"意味着可以通过定义一组独立的模块来实现有效的模块化,这些独立的模块彼此间仅仅交换那些为了完成系统功能而必须交换的信息。
使用信息隐藏原理设计软件模块有助于减少修改软件时所犯的错误。
5.3.5 局部化
所谓局部化是指把一些关系密切的软件元素物理地放得彼此靠近。局部化与信息隐藏密切相关。显然,局部化有助于实现信息隐藏。
5.4 度量模块独立性的标准(低耦合,高内聚)
模块的独立程度可以由两个定性标准来度量,这两个标准分别称为内聚和耦合。内聚衡量一个模块内部各个元素彼此结合的紧密程度;耦合衡量不同模块彼此间互相依赖(连接)的紧密程度。
5.4.1 内聚
内聚度量一个模块内的各个元素彼此结合的紧密程度,它是信息隐藏和局部化概念的自然扩展。
设计软件时应该力求做到高内聚(功能内聚和顺序内聚),通常中等程度的内聚(通信内聚和过程内聚)也是可以使用的,而且效果和高内聚相差不多;但是,低内聚(偶然内聚逻辑内聚和时间内聚)效果很差,不要使用。
内聚和耦合是密切相关的,模块内的高内聚往往意味着模块间的松耦合。内聚和耦合都是进行模块化设计的有力工具,但是实践表明内聚更重要,应该把更多注意力集中到提高模块的内聚程度上。
5.4.2 耦合
耦合是对一个软件结构内不同模块之间互连程度的度量。耦合的强弱取决于模块间接口的复杂程度,进人或访问一个模块的点,以及通过接口的数据。
在软件设计中应该追求尽可能松散耦合的系统。模块间合松散,有助于提高系统的可理解性、可测试性、可靠性和可维护性。
模块之间典型的耦合有数据耦合、控制耦合、特征耦合、公共环境耦合和内容耦合。应该采用下述的设计准则:尽量使用数据耦合,少用控制耦合和特征耦合,限制公共环境耦合的范围,完全不用内容耦合。
5.5 启发规则
总结长期以来开发软件所积累的丰富经验,得出了一些启发式规则。这些启发式规则都能给软件工程师以有益的启示,往往能帮助工程师找到改进软件设计。高软件质量的途径。下面是几条典型的启发式规则。
(I)改进软件结构、提高模块独立性。设计出软件的初步结构以后,应该仔细审查析这个结构,通过模块分解或合并,力求降低合、提高内聚。
(2)模块规模应该适中。模块规模过大,则可理解程度很低;模块规模过小则开销大于有效操作。通过模块分解或合并调整模块规模时,不可降低模块独立性。
(3)深度、宽度、扇出和扇入都应适当。
(4)模块的作用城应该在控制域之内。
(5)力争降低模块接口的复杂程度。接口复杂或与模块功能不一致,是紧耦合或低内聚的征兆,应该重新分析这个模块的独立性。
(6)设计单入口,单出口的模块。这条启发式规则警告软件工程师不要使模块间出现内容耦合。
(7)模块功能应该可以预测。模块功能应该能够预测(即只要输入的数据相同就产生同样的输出),但也不要使模块功能过分局限。
5.6 描绘软件结构的图形工具
1.层次图和 HIPO 图
层次图用于描绘软件的层次结构。层次图中的一个矩形框代表一个模块,方框间的连线表示模块间的调用关系。层次图很适于在自顶向下设计软件的过程中使用。
HIPO图是“层次图加输入/处理/输出图”的英文缩写。它用层次图描绘软件结构,和层次图中每个方框相对应,有一张IPO图(或表)描绘这个方框代表的模块的处理过程。
2.结构图
结构图和层次图类似,也是描绘软件结构的图形工具。图中一个矩形框代表一个模块,框间连线表示模块间的调用关系。通常还用带注释的箭头描述在模块调用过程中传递的信息。
5.7 面向数据流的设计方法
面向数据流的设计方法的目标是给出设计软件结构的一个系统化的途径。这种设计方法定义了一些“映射”规则,利用这些映射可以把数据流图变换成软件结构。
5.7.1 数据流的类型
面向数据流的设计方法把数据流图映射成软件结构,数据流的类型决定了映射的方法。数据流有下述两种类型。
1.变换流
如果信息沿输入通路进入系统,同时由外部形式变换成内部形式,进入系统的信息通过变换中心,经加工处理以后再沿输出通路变换成外部形式离开软件系统,则具有上述特征的数据流就称为变换流。
2.事务流
原则上所有信息流都可以归类为变换流,但是,如果信息沿输入通路到达一个称为事务中心的处理T,这个处理根据输人数据的类型在若干个候选的动作序列中选取出一个来执行,则这类数据流应该划为一类特殊的数据流,称为事务流。
5.7.2 设计步骤
面向数据流方法主要有下述几个设计步骤。
1.复查基本系统模型
复查经结构化分析过程画出的基本系统模型,以确保系统的输入输出数据符合实际
2.复查并精化数据流图
认真复查需求分析阶段画出的数据流图,并在必要时加以精化。不仅要确保数据流图给出了正确的目标系统逻辑模型,而且应该使数据流图中的每个处理都代表一个规模适中、相对独立的子功能。
3.确定数据流图具有变换特性还是事务特性
一般地说,一个系统中的所有信息流都可以认为是变换流,但是,当遇到有明显事务特性的信息流时,建议采用事务分析方法进行设计。在这一步,设计人员应该根据数据流图中占优势的属性确定数据流的全局特性。此外,还应该把具有和全局特性不同的特点的局部区域孤立出来,以后可以按照这些子数据流的特点精化根据全局特性得出的软件结构。
4.确定数据流的边界
对于变换流来说,分析确定输人流和输出流的边界,从而孤立出变换中心。对于事务流来说,分析确定输入流的边界,从而孤立出事务中心。
5.完成“第一级分解”
软件结构代表对控制的自顶向下的分配,所谓分解就是分配控制的过程,而第一级分解就是分配顶层控制。
对于变换流的情况,位于软件结构最顶层的总控模块协调下述三个从属模块的控制功能。
- 输入信息处理控制模块,此模块协调对所有输入数据的接收。
- 交换中心控制模块,此模块管理对内部形式的数据的所有操作。
- 输出信息处理控制模块,此模块协调输出信息的产生过程。
对于事务流的情况,位于软件结构最顶层的总控模块管理下属的接收分支和发送分支的工作。接收分支由输人流映射而成。发送分支的顶层是一个调度模块,它根据输入数据的类型调用相应的活动分支。
机械地遵循上述映射规则很可能会得出一些不必要的控制模块,如果它们确实用外不大,那么可以而且应该把它们合并。反之,如果控制模块功能过分复杂,则应该把它分解为两个或多个控制模块,或者适当地增加中间层次的控制模块。
6.完成“第二级分解”
所谓第二级分解就是把数据流图中的每个处理映射成软件结构中一个适当的模块
对于变换流来说,完成第二级分解的方法是从变换中心的边界开始沿着输入通路向外移动,把输入通路中每个处理依次映射成软件结构中“输人信息处理控制模块”控制下的一个低层模块;然后从变换中心的边界开始沿着输出通路向外移动,把输出通路中每个处理依次映射成直接或间接受“输出信息处理控制模块”控制的一个低层模块;最后把变换中心内的每个处理映射成受“变换中心控制模块”控制的一个模块。
对于事务流来说,映射出接收分支结构的方法和变换分析映射出输入结构的方法很相似。发送分支的结构包含一个调度模块,它控制下层的所有活动模块,然后把数据流图中的每个活动流通路映射成与它的流特征相对应的结构。
设计一个大型系统时,通常把变换分析和事务分析应用到同一个数据流图的不同部分,由此得到的子结构形成“构件”,可以使用它们构造完整的软件结构。
7、设计优化
对第一次分割得到的软件结构,总可以根据模块独立原理和启发式设计规则进行优化为了产生合理的分解,得到尽可能高的内聚、尽可能松散的耦合,最重要的是,为了得到一个易于实现、易于测试和易于维护的软件结构,应该对初步分割得到的模块进行再分解或合并。