3、结构化设计
3、结构化设计
概念与原理
结构化设计的两个步骤
概要设计
将软件需求转化为数据结构和软件系统结构。
详细设计
过程设计,细化结构,得到软件详细数据结构和算法。
结构化设计与分析的关系
数据设计
把数据模型和核心数据字典转化为数据结构。
体系结构设计
把功能模型中数据流图转化成计算机模块的框架。
接口设计
功能模型中数据流图转化成软件内部、软件与其它协作系统、软件与用户之间的通信方式。
过程设计
行为、功能模型中处理规格说明转换成构建过程描述。
模块化
概述
模块指用一个名字就可以调用的相邻程序元素序列。比如一个函数就可以称为模块,可以用函数名直接调用。
模块化设计是按适当原则把软件划分为一个个较小的、相关而独立的模块。
软件工程基本定理
其中
这个定理并不意味着模块越多越好,因为模块多了只是模块内部更简单,但接口成本也会加大,最后总成本可能会更高:
抽象
根据Miller法则,人脑的内存很小,最多同时处理
把问题分块,抽出事物的本质特性,暂时不考虑细节。抽象让设计者能说明过程和数据,同时忽略底层细节。
逐步求精
为了能集中精力解决主要问题,推迟对细节问题的考虑,与抽象概念互补,是对上一步抽象内容的细化。求精让设计者在设计过程中能揭示底层细节。
信息隐蔽
模块只对外暴露接口,隐藏自己内部实现细节。其中包含的信息不允许无关人员访问。
模块独立
模块具有相对独立的功能,和其他模块之间没有过多作用。这样做有两个好处:开发阶段容易分工合作、测试维护,修改工作量小,错误传播范围小、扩充功能容易。
定性度量独立性的标准:
耦合和内聚,详见下一个标题。
模块独立
耦合
定义
耦合是软件结构中不同模块间互连程度的度量。
影响因素
模块间接口越复杂,耦合越紧密。通过模块接口的数据越多耦合越紧密。设计时应该追求尽量松散的耦合关系。
常见的耦合
非直接耦合
两个模块可以分别独立工作,完全不需要对方存在(最理想状态)。
数据耦合
两个模块通过参数传递信息,参数必须是数据(模块间只传递数据信息)。这种耦合相对松散,也比较理想。
特征耦合(标记耦合)
调用模块和被调用模块之间传递数据结构而不是简单数据,模块间传递的不是简单变量,而是像高级语言中的数据名、记录名和文件名等数据结果,这些名字即为标记,其实传递的是地址。
控制耦合
两个模块通过参数传递控制信息。这种信息的形式不重要,无论是指令还是单独的数字,只要会影响到其他模块的执行流程,就叫控制耦合。这种耦合相对紧密,尽量避免。
i
编程常用的flag变量其实就属于控制耦合。
公共环境耦合
两个或多个模块通过一个公共数据环境发生作用。
在本例中,CDN三个模块通过一个公共数据区发生作用。
这种耦合分两种情况:
① 如果所有的模块都只会发送或读取(没人又读又写),那和数据耦合是一样的,耦合度比较低。
② 如果有人又读又写,那它的耦合度介于数据耦合和控制耦合之间,这样耦合度就高。
内容耦合
一模块访问另一模块内部数据;
模块不通过正常入口转到另一模块内部;
i
goto语句其实就不是正常入口,会直接跳到模块中部,但正常应该从顶部进入。这就是为什么说goto会破坏程序结构。
两模块有部分程序代码重叠(汇编程序);
模块有多个入口。
这四条占一个就是内容耦合。
危
内容耦合的耦合度超高,不要弄出这种东西。
设计原则
使用耦合的优先级:
不准用内容耦合。
内聚
定义
内聚是模块内各元素彼此结合紧密程度的度量。
常见的内聚
功能内聚
一个模块中各部分是完成某一功能必不可少的组成部分。模块中任何元素缺一不可。最理想的内聚。
顺序内聚
模块内各处理元素和某一功能紧密相关,而且必须按次序执行。理想的内聚。
比如修改数据库中的一条记录,必须先查后改。
通信内聚
模块中各个部分都用相同的输入数据或产生相同输出数据。中等内聚度。
过程内聚
模块中处理元素之间有相关,按特定次序执行。中等内聚度。
循环体和计算模块有关联,但不是很紧密。如果把他俩分开依旧可以做到这个业务。
时间内聚
多功能模块中所有功能在同一时间内执行。比如初始化、终止、紧急故障处理模块。这些东西可能关系不大,因为需要它们同时完成才放一起的。较低内聚度,除了上述特殊情况尽量别用。
逻辑内聚
一个模块完成的功能在逻辑上相似。这种做法会增加耦合度,不易修改,效率低,属于较低内聚度。
如果仅因为逻辑相似就放一起,后期在调用的时候可能还需要区分一下到底用这个模块中的哪部分,或者由谁来调这个模块,这就增加了控制成本。
偶然内聚
模块内各部分完全没关或者很松散。较差的内聚度。
设计原则
启发规则
启发规则是前人经验的总结。
①要改进软件结构来提高模块的独立性
把初步的结构分解或合并,尽量降低耦合提高内聚。抽出共同部分;如果有的部分不需要单独作用,就可以合并:
②模块的规模应该适中
如果太大就说明分解不充分,太小的话独立性又会降低,增加接口通信开销(其实就是上面提到的,要把模块数量控制在最小成本区)。
③软件结构参数适当
参数
深度
软件结构的控制层数,标志系统大小和复杂程度。大系统深度不宜太小,小系统深度也不宜太大。
宽度
软件结构中同一层模块数的最大值(算单层总和),越大系统越复杂。
扇出
一个模块直接控制或调用的模块数。太大说明功能太复杂,应该拆开,太小说明可以和别的合并。一般3-9最好。
扇入
有多少上级模块直接调用就是多少的扇入。表征了这个模块的共享程度(或其实现功能的普遍程度)。同样不能过大也不能过小。
这个软件结构深度为5;宽度7;模块N的扇出4;模块I的扇入3。
改善
改善方法就是把扇出或扇入太大的模块拆散,添加中间层:
④模块的作用域应该在控制域内
概念
一个模块的作用域是:收到该模块内判定影响的所有模块的集合。
一个模块的控制域是:模块本身以及所有直接或间接从属它的模块集合。
如果模块的作用域不在控制域内,就容易增大控制耦合。
如果模块E中的判定影响了模块D的运作,那D就是E的作用域。本例中E的作用域在它的控制域之外,它就需要额外开一个接口给D去传这个判定(先给C,C再给D)。
改善
上移判定点
下移作用域
⑤降低接口复杂程度
比如一元二次方程求根:
QUAD_ROOT(TBL, X)
这种写法接收两个数组,不够好;
QUAD_ROOT(A, B, C, ROOT1, ROOT2)
这种写法接收五个简单变量,易于理解,而且可以省略IO的预处理。
⑥模块出入口要唯一
避免内容耦合。
⑦模块功能可预测
输入之后,至少应该知道它会大概输出一个啥东西。
一个功能不可预测的例子:
这种模块的输出依赖上次输入,如果我不知道上次输入了啥,就完全没法预测输出,功能完全不可测试。
⑧模块功能不能过度受限
比如一个用来做查询的模块,一次只能查固定的10条数据。如果后期需求有增加就无法满足。
面相数据流的设计方法
任务
面相数据流的设计要解决的任务,就是将软件需求分析阶段生成的逻辑模型数据流图映射成表达软件系统结构的软件结构图。结构化设计属于面向数据流的设计方法。
软件结构图(SC图)
调用关系和接口表示
在SC图中用矩形框表示模块,并用名字来标记它,写明模块的调用关系和接口。
默认从上到下,不用画箭头。数据信号用空心圆,控制信号用实心圆。
选择调用
循环调用
信息流类型
变换流
信息沿输入通路进入系统,由外部形式变换成内部形式,通过变换中心加工处理后再沿输出通路变换成外部形式离开软件系统。比如纸质票据原来是纸媒信息(外部形式),输入后变为一条数据库记录(内部形式),可能之后还会被打印到纸上(外部形式)。
事务流
信息沿输入通路到一处理,由处理根据输入信息类型在若干动作序列中选一个执行。这个处理称事务中心,完成以下任务:
接收事务(输入信息);分析每个事务确定其类型;根据事务类型选择一条通路。
i
其实就是Switch语句的作用。
面向数据流设计过程
流程图
变换分析(例子)
变换分析是把具有变换流特点的数据流图映射成软件结构。
完成的功能
智能仪表板
(1)通过模/数转换实现传感器和微处理机接口;
(2)在发光二极管面板上显示数据;
(3)指示每小时英里数(mile/h)、行驶里程和每加仑油行驶的英里数(mile/Gal)等;
(4)指示加速或减速
(5)超速警告:发出超速警告铃声。
步骤
①复查基本系统模型
确保系统输入和输出数据符合实际。
②复查并精化数据流图
正确、处理项完成相对独立功能。
精化之后的数据流图:
③确定数据流图具有变换特性还是事务特性
本例中没有明显事务中心(一进多出),为变换型。
④找出变换中心
确定数据流边界(分别找输入和输出的边界):
输入边界的确定:
如果从一个处理
比如:
在这一部分中,“读旋转信号”的处理产生的数据流“信号/秒”仍然是下一个处理的输入,并不是系统要求的输出,所以“读旋转信号”在输入边界内。而处理“确定加速/减速”产生的数据流是加速或减速的指示,这个数据是系统要求的输出(4),所以“确定加速/减速”这个处理不在输入边界内。
i
可以这么理解,输入边界其实是逻辑输入的边界。既然系统的核心是做计算,那么收集物理数据做预处理,再发给计算模块的过程其实都算是输入,即使它不是直接从外部输入的。
“输入”是从系统内部划分的,不是输入给整个系统的才算输入。
输出边界的确定
输出其实跟上边一样,核心逻辑算完了数,即使还没直接输出到外部,但是数据已经离开了系统的核心。本例中“产生里程表示”、“发出铃声”等其实都只是对核心逻辑输出数据的后处理,所以也属于输出边界。
⑤一级分解
一级分解就是把上边分的三个部分列出来:
在本例中,一级分解可以分解为如下的软件结构图:
⑥二级分解
对一级分解中的三个部分再做细分。
输入结构:
输入部分从下往上读。
变换结构:
列出变换中心所做的几种处理:
输出结构:
输出图从上往下读。
⑦对初步软件结构精化
按启发规则做合并或拆分。
事务分析
事务分析是把具有事务特点的数据流图映射成软件结构。
具有事务特点,就是有明显的处理中心。
映射过程
这种结构根据输入决定要走哪条通路。事务中心上方就是接收通路,下方就是处理通路。
例:图书管理系统
事务中心就是要求类型处理。它根据输入的内容决定要干什么。
画图时按上边区分好两个通路就行:
人机界面设计
人机界面设计问题
①系统响应时间
系统响应时间指从用户完成某控制动作,到软件给出预期响应的时间。系统响应时间有两个重要属性:长度和易变性:
长度
响应时间过长用户会感到不安、沮丧,用户觉得系统立即响应时间范围0.1-1秒,超出1秒会让用户注意到延迟。应该根据开发阶段预期的时间设计不同的处理方式,比如1-10秒的延迟让光标变成沙漏,18秒以上显示处理窗口或进度条。
如果时间过短也不一定是好事,可能会迫使用户加快操作节奏,导致出错。
易变性
易变性指响应时间相对平均响应时间偏差(方差、稳定性),越低越好,否则会让用户误认为系统工作异常。
②用户帮助措施
i
就是常见的help命令或按钮,大软件也可能是客服或联系支持。
手册和联机帮助(不离开用户界面),联机帮助分为集成帮助和附加帮助,集成帮助设计在软件里面,附加帮助系统建成后加到软件中,前者可用性更强。
帮助信息的类别
请求帮助
帮助菜单,特殊功能键,比如HELP 命令。
独立窗口显示
比如单独设一个help键。
组织帮助信息
平面结构,通过关键字访问;l层次结构,查更详细信息;l超文本结构。
③出错信息处理
软件出了问题,你要怎么让一个外行能自己解决?
有以下原则:
以用户可用理解术语;
提供清楚、易理解报错信息(出错位置、原因);
从错误中恢复的建设性意见;
信息用颜色等在视觉上引人注目;
可能造成负面后果。
④命令交互
建议保留的命令交互方式:
控制序列:Ctrl-C(拷贝) Ctrl-H(帮助) Ctrl-P(打印)
i
把这些覆盖掉那就真不是人揍的了,很难想象碳基生物能搞出这种活。
功能键:F1(帮助)
键入命令(留一个调试接口去处理一些比较复杂的错)
命令宏机制:让用户能自定义名字代表一串常用命令序列。
人机界面设计指南
①一般交互
尽量保持人机界面菜单选择、命令输入、数据显示风格一致;
提供有意义信息反馈:双向通信;
破坏性动作前要确认:删除、覆盖(确定要删除…吗?);
尽量允许取消(撤回操作);
减少两次操作之间必须的记忆量;
提高对话、移动和思考的效率;
允许犯错误:保护不受致命错误破坏;
按功能对动作分类,设计屏幕布局;
提供帮助措施;
用简单的动词或动词短语作为命令名。
②信息显示
只显示与当前工作有关信息,不宜过多;
简单易懂方式表示数据:图形、图表;
使用一致标记、标准缩写和可预知颜色;
产生有意义出错信息;
使用模拟的方式显示信息等(与现实中的物理概念联系起来):
③数据输入
减少用户输入动作:鼠标选择、滑动标尺等;
使当前不适用命令不起作用,比如禁用按钮、标签;
注意
虽然书上这么写,但本人强烈不建议这么干。如果用户发现某个按钮被禁用了,但是没有任何提示什么的,会严重影响用户体验。
交互灵活:保留各种输入方式;
让用户控制交流;
对所有输入都提供帮助;
消除冗余输入:数据单位、整钱后键入.00、提供缺省值等。
过程设计
过程设计任务
确定模块算法;
确定模块使用数据结构;
确定模块接口(系统外部接口、用户界面、内部模块间接口细节、输入数据和输出数据)。
结构化程序设计
定义
经典定义:
程序代码通过顺序、选择、循环三种控制结构连接,单入口单出口。
扩展定义:
可限制使用GOTO语句、DO_UNTIL和DO_CASE
修正定义:
LEAVE和BREAK,可从循环中转移出来。
结构化程序设计工具
程序流程图
基本结构
①顺序型;②选择型;③当循环;④直到循环
标准图符
优缺点
优点
对控制流程描绘直观,便于初学者掌握。
缺点
不是逐步求精好工具,过早考虑控制流程,非整体结构;
用箭头代表控制流,程序员随意转移控制;
不易表示数据结构和调用关系。
N-S图
标准图符
①顺序型;②选择型;③Switch;④当循环;⑤直到循环;⑥调用子程序
计算阶乘
优点
功能域(一特定控制结构的作用域)明确;
不可能任意转移控制;
容易确定局部和全程数据的作用域;
容易表现嵌套关系,也可表示模块的层次结构。
PAD图
标准图符
计算阶乘
优点
使用PAD图设计的程序必然是结构化程序;
PAD图描绘的程序结构十分清晰;
用PAD图表现程序逻辑,易读、易懂、易记;
容易将PAD图转换成高级语言源程序;
支持自顶向下逐步求精。
求精过程
每一块逻辑都可以单独抽出扩展,比如对
判定表
左上部列出所有条件;
左下部所有可能做的动作;
右上部表示各种条件组合的一矩阵;
i
这个矩阵不一定要列出全部
右下部是和每种条件组合相对应的动作。
判定树
有点像索引图,根据判定表可画出来。
优缺点
优点:
形式简单,易看出含义,易于掌握和使用。
缺点:
简洁性不如判定表,相同数据元素重复写多遍,越接近叶端重复次数越多。
过程设计语言(PDL)
概念
是一种伪码,用正文形式表示数据和处理过程设计工具。
McCabe方法
流图的画法:
流程图映射到流图:
注意,有的结点应该合并。
复合条件的流图映射:
程序复杂度
三种计算方法:
①
区域就是封闭空间,是图的面数。
注意
整张图也是一个面。
②
和①一模一样,
③
面向数据结构的设计方法
总共有两种方法,Jackson和Warnier方法。这里只说Jackson方法。
Jackson图
顺序结构
选择结构
根据条件,
重复结构
Jackson方法步骤
确定输入数据和输出数据逻辑结构,用Jackson图表达;
确定输入结构和输出结构中有对应关系(因果)的单元;
描绘数据结构的Jackson图导出描绘程序结构Jackson图;
列出所有操作和条件,分配到Jackson图中;
用伪码表示。
Jackson方法示例
题目
将高考后考生的基本情况文件和考生高考成绩文件合并成一个新文件。
图(a)是考生基本情况文件和考生高考成绩文件数据结构,考生基本情况文件(简称考生情况文件)只取考生的准考证号、姓名和通讯地址,以便公布考生成绩和发放录取通知书用。考生高考成绩文件(简称考分文件)包括准考证号和考分。这两个文件均由考生记录重复组成,考生记录又由几个数据项顺序组成。
图(b)给出了考生新文件的数据结构,包括准考证号、姓名,通讯地址和考分。
步骤
①找出输入结构和输出结构中的对应关系
②由Jackson图导出程序结构
③列出并分配所有操作
1:停止
2:打开两个输入文件
3:建立输出文件
4:从两个输入文件各读一条记录
5:生成一条新纪录
6:将新纪录写入输出文件
7:关闭全部文件
④写伪代码
产生新文件 seq
打开两个输入文件
从两个输入文件各读一条记录
分析考生记录 iter until 文件结束
处理考生记录 seq
产生准考证号
产生姓名
产生通讯地址
产生考分
生成一条新纪录
将新纪录写入输出文件
从两个输入文件各读一条记录
处理考生记录 end
分析考生记录 end
关闭全部文件
停止
产生新文件 end