软件工程导论
一、软件工程学概述
1.1 软件与软件危机
一、软件的概念、特点与发展
(1)软件发展经历三个阶段:
程序设计阶段一50至60年代
程序系统阶段一60至70年代
软件工程阶段一70年代以后
(2)软件的概念:
软件是计算机系统中与硬件相互依存的另一部分,它包括程序、数据及其相关文档的完整集合。软件=程序+数据+文档
数据:是使程序能够适当处理信息的数据结构。
程序:是能够完成预定功能和性能的可执行指令序列。
文档:是开发、使用和维护过程程序所需要的图文资料。
(3)软件的特点:
1.软件本身的复杂性
2.软件的成本高昂
3.软件开发未摆脱手工开发方式
4.软件维护与硬件有本质差,维护难度高
5.软件开发不是传统硬件制造过程
6.软件是一种逻辑实体,无磨损性
二、软件危机
(1)软件危机的概念:
在计算机软件开发和维护过程中所遇到的一系列严重问题。
软件危机包含两方面内容:
1、如何开发软件,以满足对软件日益增长的需常
2、如何维护数量不断膨胀的已有软件
(2)软件危机的表现:
- 对软件开发成本和进度估算不准确
- 用户对已完成软件不满意
- 软件质量不可靠
- 软件不可维护
- 没有适当文档资料
- 软件成本在计算机系统中所占比例逐年上升
- 软件开发生产率低
(3)软件危机的原因
1、主观原因:
忽视需求分析
轻视软件维护
没有认识到程序只是软件的一部分
没有认识到软件开发只是漫长的软件生命周期中一个比较次要阶段
越到后期引入变动付出代价越高昂
2、客观原因:
软件是逻辑实体、缺乏可见性,管理和控制困难
软件不会磨损,维护意味着修改原来设计,维护困难
软件规模庞大,程序复杂性随规模增加指数上升
(4)消除软件危机的途径:
1、对计算机软件应该有正确认识
2、吸取借鉴人类长期从事各种工程项目积累的原理、概念、技术和方法
3、积极开发和使用计算机辅助开发工具
4、探索更好更有效的管理措施和手段对开发过程进行控制和管理
管理+技术
1.2 软件工程
(1)软件工程定义:
采用工程的概念、原理、技术和方法来开发与维护软件,把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来,经济的开发出高质量的软件并维护它。
(2)软件工程的本质特性:
- 关注大型程序的构造
- 中心课题是控制复杂性
- 软件经常变化
- 开发效率非常重要
- 开发人员和谐合作是关键
- 软件需有效支持用户
- 软件开发者替代其他领域人员创造产品
(3)软件工程基本原理:
- 按软件生存期分阶段制定计划并认真实施
- 坚持进行阶段评审
- 坚持严格的产品控制
- 使用现代程序设计技术
- 结果能够得到清楚的审查
- 用人少而精
- 承认不断改进软件工程实践的必要性
※(4)软件工程方法学:
把在软件生命周期全过程中使用的一整套技术方法的集合称为方法学,也称为泛型。
软件工程方法学包括三个要素:方法、工具和过程。
方法:完成软件开发各项任务的技术方法,回答“怎么做”
工具:为运用方法提供的自动或半自动软件工程支撑环境
过程:是为了获得高质量软件所需要完成的一系列任务框架,回答“何时做”
(5)软件工程方法学分类:
传统方法学(生命周期方法学)
面向对象方法学:
1.3 软件生命周期
1.4 软件过程
软件过程:是为了获得高质量软件所需要完成的一系列任务框架。
通常用软件生命周期模型描述软件过程。
瀑布模型
将软件生命周期的各项活动规定为依照固定顺序连接的若干阶段工作,最终得到软件产品
瀑布模型特点:
- 阶段间具有顺序性和依赖性。
- 推迟实现的观点。
- 质量保证的观点
- 每个阶段必须完成规定的文档
- 每个阶段结束前完成文档审查
- 及早改正错误
1、瀑布模型的优点
(强迫开发人员使用规范的方法,严格规定了每个阶段必须提交的文档,要求每个阶段)
可以强迫开发人员采用规范的方法,
严格规定了每个阶段必须提交的文档:
要求每个阶段交出的所有产品都必须经过质量保证小组的仔细验证。
2、瀑布模型的缺点
在软件开发的初期阶段就要求做出正确、全面、完整的需求分析对许多应用软件来说是极其困难的。
在需求分析阶段,当需求确定后,无法及时验证需求是否正确、完整。
作为整体开发的瀑布模型,由于不支持产品的演化,缺乏灵活性,对开发过程中很难发现的错误,只有在最终产品运行时才能暴露出来,从而使软件产品难以维护。
改进的瀑布模型
快速原型模型
快速建立可运行的程序,他完成的功能往往是最终产品功能的一个子集
快速原型模型优缺点:
优点:
1、开发的软件产品通常满足用户需求
2、软件产品开发基本是线性过程
缺点:
1、准确原型设计困难
2、原型理解可能不同
3、不利于开发人员创新
增量模型
先完成一个系统子集的开发,再按同样的开发步骤增加功能(系统子集),如此递增下去直至满足全部系统需求。
增量模型优缺点:
优点:
1、短时间内可提交完成部分功能
2、逐渐增加产品功能,用户适应产品快。
缺点:
1、增量构件划分以及集成困难。
2、容易退化为边做边改模型。
螺旋模型
在每个阶段之前都增加了风险分析过程的快速原型模型,看作增加了风险分析的快速原型模型
螺旋模型优缺点:
优点:
1、利于把软件质量作为软件开发目标。
2、减少测试
3、维护和开发不分开
缺点:
1、风险估计困难
喷泉模型
典型的面向对象的软件过程模型,体现迭代和无缝的特性
二、可行性研究
2.1 可行性研究任务
(1)可行性研究自的:
用最小的代价在最小的时间内确定问题是否能够解决。(5%-10%)
(2)可行性研究实质:
系统分析和设计过程的大大压缩和简化,在较高层次上以较为抽象的方式进行系统的分析和设计过程。
(3)可行性研究过程:
1.分析和澄清问题定义。
2.导出系统的逻辑模型。(数据流图+数据字典)
3.根据逻辑模型探索若干种可供选择解法
4.研究每种解法可行性。
经济可行性:经济效益是否大于开发成本
技术可行性:现有技术能否实现
操作可行性:系统操作方式是否可行
其他可行性:法律、社会效益。
2.2 可行性研究内容
(1)可行性研究步骤:
1.复查系统规模和目标。
对问题定义阶段初步确定的规模和目标进行肯定或改正,并列出对目标系统的约束和限制。
2.研究目前正在使用的系统。
了解现有系统能做什么,而不花费过多时间分析怎么实现这些功能。
3.导出新系统的高层逻辑模型。
现有物理系统 >>现有逻辑模型>>自标逻辑模型>>目标物理系统
4.进一步定义问题。
分析员和用户一起再次复查系统。前四个步骤构成一个循环。
5.导出和评价供选择的解法
技术角度排除不可行方案
操作可行性排除用户不能接受方案
经济可行估算成本和收益
6.推荐行动方针。
给出是否继续的结论
7.草拟开发计划。
制定进度表
开发人员、计算机资源分析
估计每阶段成本、下阶段详细分析,
8.书写文档提交审查。
2.3 系统流程图
是一种描绘物理系统的图,用图符号以黑盒子形式描绘物理系统的各部件表达数据在系统各部件之间流动的情况。而不是对数据进行加工处理的控制过程。
2.4 数据流图和数据字典(※)
数据流图(DFD)
描述信息流和数据从输入到输出过程所经受的变换。没有任何具体物理部件,只是描绘数据在软件中流动和被处理的逻辑过程。
数据流图画法:
(1)确定系统输入输出、源点以及终点
(2)画系统顶层数据流图
用加将输入输出数据连接起来,给加工、数据等命名
(3)自顶向下分解,画出分层数据流图
将加工细分,细分成几个数据流图表示
数据字典
是关于数据的信息集合,即对数据流图中包含的所有元素定义的集合。
1.数据字典的内容:数据流、数据流分量(数据元素)、数据存储、处理。
2.定义数据的方法:
由数据元素组成数据的方式:顺序、选择、重复、可选
例题:
数据字典用途:
在软件分析和设计的过程中给人提供关于数据的描述信息。
- 作为分析阶段的工具
- 估计改变一个数据将产生的影响
- 是数据库开发的第一步
2.5 成本效益分析
一、分析目的
从经济角度分析新系统的开发是否能盈利,帮助使用部门正确做出是否投资的决定。
二、成本估计
1.代码行技术
软件成本 = 每行代码的平均成本 * 源代码行数
2.任务分解技术:按开发阶段将系统划分成任务
每个任务的成本 = 人力 * 人平均工资
软件成本 = ∑独立任务的成本
三、成本/效益分析步骤
1.估计开发成本、运行费用和新系统带来的经济效益。
2.比较新系统的开发成本和经济效益。
四、成本/效益分析的方法
1.货币的时间价值F=P(1+i)
2.投资回收期
3.纯收入
4.投资回收率
三、需求分析
3.1 需求分析任务
需求分析任务
- 确定对系统的综合要求
- 功能需求:系统必须提供的服务功能
- 性能需求:系统必须满足的约束条件
(响应速度、安全性) - 可靠性和可用性需求:可靠性定量、可用性量化
- 出错处理需求:错误响应机制
- 接口需求:应用系统与环境通信格式常见用户接口需不硬件接口需求、软件接口需求、通信接口需求约束:用户或环境强加的限制条件(工具、语言等)
- 逆向需求:系统不应该做什么
- 将来可能提出要求:将来可能需要实现的需求
- 分析系统的数据要求
- 导出系统的逻辑模型
- 修正系统开发计划
3.2 与用户沟通获得需求方法
访谈
面向数据流自顶向下求精
简易的应用规格说明技术
快速建立软件原型
3.3 分析建模与规格说明
模型:
是指为了理解事物而对事物做出的一种抽象,是对事物的一种无歧义的书面描述。
模型分类:
- 数据模型:(实体-联系图):描绘数据对象及数据对象之间的关系。
- 功能模型:(数据流图):描绘数据在系统中流动时被处理的逻辑过程,指明系统具有的变换数据的功能。
- 行为模型:(状态转换图):描绘系统的各种行为模式在不同状态间转换的方式。
3.4 实体联系图、状态转换图
一、实体联系图(E-R图)
- 实体:描述数据对象
- 属性:描述数据对象的性质
- 联系:描述数据对象之间的交互方式
- 一对一联系1:1
- 一对多联系1:n
- 多对多联系n:m
- 表示方式
二、状态转换图
- 状态:系统的行为模式,包括初态,终态,中间状态
- 事件:是指在某个特定时刻发生的事情,即对系统从一个状态转换到另一个状态的事件抽象
- 表示方式
在一张状态图中只能有一个初态,而终态可以有0至多个
3.5 其他图形工具
一、层次方框图
表示方式:用树形结构的一系列矩形框描述数据的层次结构
优点:随着结构的逐步精细,对数据结构的描绘也越来越详细
二、Warnier图
表示方法:用树形结构描绘信息的层次结构
优点:可以表明信息的逻辑组织。可以表明某类信息出现的条件或是否重复出现
三、IPO图
表示方式:是输入、处理、输出图的简称,能够方便的描绘输入数据,对数据的处理和输出数据之间的关系
优点:简略描绘系统主要算法
3.6 验证软件需求
一、从哪些方面验证软件需求的正确性
- 一致性:所有需求必须是一致的,任何一条需求不能和其他需求互相矛盾。
- 完整性:需求必须是完整的,规格说明书应该包括用户需要的每一个功能或性能。
- 现实性:指定的需求应该能用现有的硬件和软件技术可以实现。
- 有效性:必须证明需求是正确有效的,确实能解决用户面对的问题。
二、验证软件需求的方法
验证需求的一致性:自然语言描述需求、形式化语言描述需求、用软件工具验证
验证需求的现实性:参照开发经验
验证需求的完整性和有效性:建立软件原型
三、用于需求分析的软件工具
PSL/PSA(问题陈述语言/问题陈述分析程序)系统
四、总体设计
4.1 总体设计设计过程
总体设计又称为概要设计或初步设计
任务:
- 确定系统中每个程序由哪些模块组成以及这些模块相互间的关系。
- 划分出物理元素。包括程序、文件、数据库、文档等。
设计过程包括系统设计阶段和结构设计阶段
- 系统设计阶段
- 设想供选择的方案:数据流图出发,将处理分组,抛弃行不通分组。
- 选取合理的方案:上一步方案选取低、中、高成本三种方案。
- 推荐最佳方案:推荐最佳方案,制定详细实现计划
- 结构设计阶段
- 功能分解:对数据流图进一步细化,进行功能分解。可以用IPO图等工具描述细化后每个处理的算法。
- 设计软件结构:层次图或结构图描绘软件结构。或数据流图导出软件结构。
- 设计数据库
- 定测试计划
- 书写文档
- 审查和复审
4.2 设计原理(※)
模块化
模块:能够单独命名,由边界元素限定的程序元素的序列,是构成程序的基本构件
模块化:把程序划分成独立命名且可独立访问的模块,每个模块完成一个子功能,把这些模块集成起来构成一个整体,可以完成指定的功能满足用户的需求。
抽象
抽出事务的本质特性而暂时不考虑它们的细节
逐步求精
逐步揭露出底层细节
Miller法则:注意力集中在(7±2)上
信息隐藏与局部化
信息隐藏:是指一个模块内包含的信息对于不需要这些信息的模块来说,是不能访问的。主要是指模块的实现细节。
局部化:指把一些关系密切的软件元素物理地放的彼此靠近,它有助于实现信息隐藏。
模块独立
模块独立性:是模块化、抽象、信息隐藏和局部化概念的直接结果。
模块独立是好设计的关键,设计是决定软件质量的关键环节。
度量标准:耦合,内聚
耦合
是对一个软件结构内不同模块之间互连程序的度量
耦合程度取决于模块接口的复杂程度、通过接口的数据等
耦合性越高,模块独立性越弱
耦合分类(程度从低->高)
无直接耦合->数据耦合->标记耦合(特征耦合)->控制耦合->外部耦合->公共耦合->内容耦合
内聚
用来度量一个模块内部各个元素彼此结合的紧密程度
内聚分类(程度从低->高)
偶然内聚->逻辑内聚->时间内聚->过程内聚->通信内聚->顺序内聚->功能内聚
同其他模块强耦合的模块意味着弱内聚;强内聚模块意味着与其它模块间弱耦合;
软件设计目标:高内聚、低耦合
4.3 启发规则
- 改进软件结构,提高模块独立性
- 模块规模应该适中
- 深度、宽度、扇入和扇出应适当
- 深度:表示软件结构中控制的层数
- 宽度:软件结构内同一个层次上的模块总数的最大值
- 扇出:一个模块直接控制(调用)的模块数目,扇出过大意味着模块过分复杂。一般一个设计的好的典型系统的平均扇出是3或4,扇出的上限是5到9。
- 扇入:指有多少上级模块调用它,扇入大说明上级模块共享该模块的数目多。好的软件结构顶层扇出比较高,中层扇出比较少,底层扇入到公共的实用模块中,即底层模块有高扇入。
- 模块的作用域应该在控域之内
- 作用域:指受该模块内一个判定影响的所有模块的集合。
- 控制域:是这个模块本身以及所有直接或间接从属于它的模块的集合。
- 力争降低模块接口的复杂程度
- 设计单入口单出口的模块
- 模块功能应该可以预测
4.4 描绘软件结构的图形工具
一、层次图与IPO图
层次图:用方框和连线表示,连线表示上下层的调用关系
HIPO图:层次图加编号
二、结构图
不仅描述调用关系,还描述传递的信息和调用方式
箭头代表调用过程传递的信息。尾部空心代表数据,实心代表控制信息
模块调用分类
1、简单调用 2、循环调用 3、选择调用
4.5 面向数据流设计方法
面向数据流设计方法也成为结构化设计方法(SD)
数据流图分类
- 交换流:由输入、变换中心和输出三部分组成
- 事务流:在多种事务中选择一个执行
变换分析:把具有变换流特点的数据流图映射成软件结构
五、详细设计
5.1 详细设计的目的任务
- 详细设计目的:确定怎样具体的是实现所要求的系统,得出对目标的精确描述
- 详细设计任务:
- 过程设计:即设计软件体系结构中所包含的每个模块的实现算法
- 数据设计:设计软件数据结构
- 接口设计:设计软件内部各模块之间的接口
5.2 结构程序设计
结构程序设计:只用三种基本的控制结构就能实现任何单入口单出口的程序
三种基本控制结构:选择、顺序、循环
扩充的控制结构:
经典的结构程序设计:
只包括三种基本结构。扩展的结构程序设计:
除了基本控制结构还包括
Do-case多分支和Do-UNTIL循环。
修正的结构程序设计:
除了基本控制结构和扩展控制结构还使用BREAK结构。
5.3 人机界面设计
人机界面设计:是接口设计的一个重要的组成部分。
设计人机界面过程常遇到的4个问题:
- 系统响应时间:重要属性:长度和易变性
- 用户帮助设施
- 出错信息处理
- 命令交互
人机界面设计指南:
- 一般交互指南
- 信息显示指南
- 数据输入指南
5.4 过程设计工具(※)
过程设计工具:描述程序处理过程的工具。
程序流程图
- 又称为程序框图,它是历史最悠久使用最广泛的描述软件设计的方法,然而它也是用得最混乱的一种方法。
盒图(N-S图)
- 出于要有一种不允许违背结构程序设计精神的图形工具的考虑。
PAD图
Problem Analysis Diagram
- 他用二维树形结构的图来表示程序的控制流,将这种图翻译成程序代码比较容易
- 能够体现程序逻辑和数据结构,面向高级程序设计语言
判定表
- 当算法中包含多重嵌套的条件选择时判定表却能够清晰地表示复杂的条件组合与应做的动作之间的对应关系。
- 组成:
左上部列出所有条件,左下部是所有可能的动作。
右上部是表示各种条件组合,右下部是和每种条件组合相对应的动作。
判定树
- 是判定表的变种,也能清晰的表示复杂的条件组合与应做的动作之间的对应关系
过程设计语言(PDL)
- 过程设计语言也叫伪码
- 用正文形式表示数据和处理过程的设计工具
5.5 面向数据结构的设计方法
Jackson图
Jackson图实质上是对第3.7节中介绍的层次方框图的一种精化。读者需要注意的是,虽然Jackson图和描绘软件结构的层次图形式相当类似,但是含义却很不相同,即层次图中的一个方框通常代表一个模块;而Jackson图即使在描绘程序结构时,一个方框也并不代表一个模块,通常一个方框只代表几个语句。层次图表现的是调用关系,通常一个模块除了调用下级模块外,还完成其他操作;而Jackson图表现的是组成关系,也就是说,一个方框中包括的操作仅仅由它下层框中的那些操作组成。
5.6 程序复杂度定量度量
程序复杂度定量度量:
定量的度量详细设计模块的质量。
McCabe方法
将程序图转化为程序流程图再计算复杂度。
计算方法:
- 流图中的区域数等于环形复杂度
- 流图G的环形复杂度V(G)=E-N+2,E是流图中边的条数,N是结点数。
- 流图G的环形复杂度V(G)=P+1,其中,P是流图中判定结点的数目。
V(G)<=10比较科学
六、实现
6.1 实现
软件生命周期中的编码和测试统称为实现。
语言选择
选择适宜的程序设计语言是减少编程的难度、减少程序测试量、容易维护的实现基础。
编码风格
- 程序内部的文档
- 数据说明
- 语句构造
- 输入输出
- 效率:效率主指处理机时间和存储器容量
6.2 软件测试基础
软件测试的自标
- 软件测试是为了发现错误而执行程序的过程。
- 编码阶段(单元测试)
- 测试阶段(各种综合测试)
软件测试准则:
- 所有测试都应该能追溯到用户需求。
- 应该远在测试之前就制定测试计划
- Pareto原理:80%的错误是由20%的模块造成的。
- 应该从“小规模”测试开始,并逐步进行大规模测试。
- 穷举测试是不可能的;测试只能证明程序有错误,但不能证明程序无错误。
- 为了尽最大可能的发现错误,应该由独立的第三方担任测试工作。
软件测试的方法(※):
黑盒测试法:
将软件看作一个黑盒子,不考虑其内部结构和处理过程,只按照规格说明书的规定,测试软件是否能够正确接收输入数据,产生正确的输出数据。即测试程序是否正确的实现了其功能。又称为“功能测试”。
白盒测试法:
完全知道程序的内部结构和处理算法,因此可以将程序看作一个透明的白盒子,根据程序内部的逻辑结构测试程序内部的主要执行通路是否能够按照预定的要求正确工作。又称“结构测试”。
软件测试的步骤:
- 单元测试(模块测试):将每个模块作为一个单独的实体进行测试。发现的错误编码和详细设计阶段的错误
- 子系统测试:将模块集成为一个子系统进行测试。着重测试模块的接口。
- 系统测试:将子系统组装为一个完整的系统进行测试。子系统测试和系统测试总称为“集成测试
- 验收测试(确认测试):在用户的参与下,往往使用实际的数据进行的测试。发现需求说明中的错误
- 平行运行:同时运行新开发出来的系统和将被它取代的旧系统,以便比较新旧两个系统的处理结果。
6.3 单元测试
测试依据:详细设计文档
测试技术(设计测试用例的方法):白盒测试技术
着重点:
- 模块接口
- 局部数据结构
- 重要的执行通路
- 出错处理通路
- 边界条件
6.4 集成测试
目标:发现与接口有关的问题
实施者:独立的测试机构或第三方人员
集成方法:非渐增测试、渐增测试
- 自顶向下集成:从主控模块开始沿着程序的控制层次自顶向下移动,逐步添加新的模块
- 自底向上集成:从最底层模块开始组装
自顶向下与自底向上相结合的方法:
上层模块使用自顶向下方法,下层模块采用底向上方法
回归测试:重新执行已经做过测试的某个子集,以保证程序的变化没有带来非预期的副作用。
6.5 确认测试
又称验收测试,目标是验证软件的有效性。
验证:为了保证软件正确的实现了某个特定要求而进行的一系列活动。
确认:为了保证软件确实满足了用户需求而进行的一系列活动。
Alpha测试:用户在开发者的场所,在开发者指导下进行。
Beta测试:用户在用户场所进行,遇到问题报告给开发者,开发者进行修改。
6.6 白盒测试
测试用例:测试输入数据和预期的输出结果。
测试方案:测试目的、测试用例的集合。
- 语句覆盖:被测试程序中的每条语句至少执行一次。
- 判定覆盖:使得被测程序中每个判定表达式至少获得一次“真”值和“假”值
- 条件覆盖:使得判定表达式中每个条件的各种可能的值至少出现一次。
- 判定/条件覆盖:使得判定表达式中的每个条件的所有可能取值至少出现一次,并使每个判定表达式所有可能的结果也至少出现一次。
- 条件组合覆盖:设计足够多的测试用例,使得每个判定表达式中条件的各种可能的值的组合都至少出现一次。
- 路径覆盖:覆盖被测程序中所有可能的路径。
控制结构测试:基本路径测试、条件测试、循环测试
6.7 黑盒测试
黑盒测试又称功能测试,着重测试软件的功能。
- 等价类划分法
- 把程序的输入数据集合按输入条件划分为若干个等价类,每一个等价类相对于输入条件表示为一组有效或无效的输入。
- 为每一等价类设计一个测试用例。
- 边界值分析法
- 输入等价类和输出等价类的边界就是应该着重测试的程序边界情况。选取的测试数据应该刚好等于、刚好小于、刚好大于边界值
6.8 调试
调试(也称为纠错)是在测试发现错误之后排除错误的过程。
方法:蛮干法、回溯法、原因排除法
结果:
- 找到了原因,然后改正和排除。
- 没找到原因,猜测一个原因,并设计附加测试用例来验证这个假设。
6.9 软件可靠性
软件可靠性:程序在给定时间间隔内,按照规格说明书的规定成功运行的概率。
软件可用性:程序在给定时间点,按照规格说明书的规定成功运行的概率