软件工程笔记
本文最后更新于 2025-03-13,文章内容可能已经过时。
软件工程笔记
第一章 软件工程概述
导学目标
了解软件危机的起源、产生原因、主要表现特征
了解软件工程的概念、基本原理、分类等
掌握生命周期的各个阶段以及生命周期模型
掌握统一过程的各个阶段和各个工作流之间的关系
掌握软件过程的改进策略
掌握敏捷开发
第一节 软件危机
1、计算机发展的第一阶段(20世纪60年代以前)
软件生产个体化
软件规模小
编写和使用者往往是同一个人
软件设计是人们头脑中一个隐含的过程
只有程序清单,无其他文档材料
2、计算机发展的第二个阶段(20世纪60年代中期-70年代中期)
出现了软件作坊
有专门的软件开发组织,但是仍然沿用了早期个体化的软件开发方法
暴露问题
用户有新的需求,就必须要修改程序
运行环境变化,就需要修改程序
程序个性化特点,使软件难以维护,成本比较大
软件工程诞生
1968年,北大西洋公约组织计算机科学家讨论软件危机,正式提出“软件工程”
3、软件危机的定义
在计算机软件开发和维护过程中遇到的一系列严重问题
回答两个问题
怎么开发软件(用户需求变了怎么办?)
开发完了怎么维护
4、主要表现
开发成本和进度估计不准确
报价过高
延迟交付
用户对已交互软件不满意
没有充分沟通就开始匆忙写程序
软件产品质量靠不住
软件可维护性差
程序中的错误很难改正,不能适应用户新增的需要
软件没有适当的文档资料
开发和维护起来都比较困难
5、产生原因
软件自身的特点
软件缺乏可见性
软件规模庞大,结构很复杂
软件开发与维护方法不正确
6、解决办法
技术措施
改变认识(软件≠程序,软件=程序+数据+文档)
开发和使用更好的工具
管理措施
协调好人员配置,管理严密,共同完成项目
第二节 软件工程
1、软件工程定义
1993年IEEE给出了更全面更具体的定义
把系统化、规范化、可度量的途径应用于软件开发、运行和维护过程中;研究其实现途径
2、软件工程7条基本原理
用分阶段的周期计划严格管理
把软件的生命周期划分为若干个阶段,相应的制定出切实可行的计划,并严格按照计划对软件的开发和维护工作进行管理
坚持进行阶段评审
在每个阶段都进行严格的评审,尽早发现错误
实现严格的产品控制
采用现代程序设计技术
结果应能清楚的审查
开发人员应该小而精
承认不断改进软件工程实践的重要性
3、软件工程方法学分类
传统方法学
面向对象方法学
第三节 软件生命周期
1、软件生命周期的定义
从软件的产生、发展、成熟到衰亡的全过程
2、软件生命周期的组成
软件定义
问题定义
可行性研究
需求分析
软件开发
总体设计
详细设计
编码和单元测试
综合测试
软件维护
软件维护
1.3.1 软件生命周期的8个阶段
国标《计算机软件开发规范》分为8个阶段
可行性研究和计划(=问题定义+可行性研究)
关键问题
问题是什么?
有没有可行的解决办法?
大致的计划
产物
问题定义报告(问题的性质、目标、规模)
可行性研究报告(经济、技术、社会可行性)
项目的开发计划(粗略)
需求分析
关键问题
明确目标系统必须具备哪些功能
可行性研究的需求分析是粗略的,需求分析是完整、准确、具体、清晰
产物
需求规格说明书(用正式的文档准确的记录对目标系统的需求)
总体设计
关键任务
怎样实现目标系统
设计多种方案,推荐最佳方案,设计体系结构
产物
总体设计说明书(记录总体设计的结果)
详细设计(针对每一个模块)
关键任务
该怎样具体实现目标系统
产物
详细设计说明书(设计每个模块的算法和数据结构)
实现
关键任务
选择合适的语言和工具翻译详细设计结果,测试模块
产物
实现阶段文档(程序清单、单元测试报告)
集成测试
关键任务
将通过单元测试的模块组装起来进行测试
通过各种类型的测试使软件达到预定的要求
产物
测试报告(测试计划、详细测试方案、实际测试结果)
确认测试
关键任务
由用户根据需求规则说明书进行测试
产物
测试报告(测试计划、测试方案、测试结果)
使用与维护
关键任务
通过各种必要的活动使系统持久的满足用户的需要
四类维护类型
改正性维护
适应性维护
完善性维护
预防性维护
第四节 软件过程
1.4.1 软件过程的定义
软件过程是一个为创造高品质软件所需要完成的活动、动作和任务的框架
白话解释:软件过程描述为了开发出用户所需要的的软件,什么人在什么时间做了什么事情以及怎么去做的过程。
为了能够描述软件过程,通常使用生命周期模型,也叫做过程模型
实际从事软件开发工作时,软件规模、类型、开发环境以及技术方法等会影响到各阶段的划分和执行顺序,形成不同的软件生命周期模型,即过程模型。
1.4.2 软件过程模型——瀑布模型
瀑布模型是软件工程中使用最广泛的模型之一
传统的瀑布模型如图所示:
特点
阶段间具有顺序性和依赖性
前一阶段结束之后,后一阶段才能开始
前一阶段的输出文档就是后一阶段的输入文档
推迟实现的观点
瀑布模型在编码前设置了系统分析和系统设计各个阶段,尽可能推迟程序的物理实现
实践表明,编码开始的越早,最终完成开发所需时间更长。这是因为前面工作做得不扎实,导致大量返工,甚至发生无法弥补的问题,带来灾难性后果
质量保证观点
每个阶段都必须完成规定的文档
每个阶段结束前都要对所完成的文档进行评审
传统瀑布模型过于理想化了,事实上人在工作的过程中不可能不犯错误,因此实际的瀑布模型都是带反馈环的。带反馈环的瀑布模型如图:
瀑布模型的优缺点
优点
提高软件质量
降低维护成本
缺点
模型缺乏灵活性
要求用户在不经过实践就提出完整准确的需求不切实际
1.4.3 软件过程模型——快速原型模型
定义
快速建立一个能反应用户主要需求的原型系统,根据用户的使用意见反复修改原型系统,开发出真正符合用户需求端的产品
快速原型模型如下
快速原型模型的优缺点
优点
在需求确定上优于瀑布模型
开发人员通过建立原型系统学会了很多东西
有些原型系统可以称为最终产品的一部分
缺点
快速建立的原型系统并且经过连续的修改,可能导致产品质量低下,原型系统的内部结构不好
1.4.4 软件过程模型——增量模型
定义
把软件产品作为一系列的增量构建来设计、编码、集成和测试
增量模型如图所示
与瀑布模型和快速原型模型的区别
瀑布模型和快速原型模型就是一次把一个满足所有需求的产品提交给用户
增量模型分批向用户提交产品
增量模型的优缺点
优点
较短时间内向用户提交可完成部分工作产品
用户有充裕的时间学习和适应新产品
软件结构是开放的,方便向现有产品添加新构件
缺点
向软件中添加新构建,不影响原产品是比较困难的
前面的增量模型表明,必须在开始实现各个构件之前就全部完成需求分析、规格说明和概要设计阶段,相对来说风险较小
风险更大的增量模型一旦确定用户需求后,并行的构建不同的构件,其模型如下:
1.4.5 软件过程模型——螺旋模型
定义
通过使用原型或者其他方法方法最小化风险。可以简单的将这个生命周期模型看作是每个阶段之前带有分析的原型模型
简化螺旋生命周期模型如下
笛卡尔坐标四象限表达四方面活动
制定计划:确定方案、选择目标、确定约束条件
风险分析:评估方案、确认风险、减小风险
实施工程:开发、验证下一级产品
客户评估:评价开发工作、计划下一阶段
沿螺线自内向外每旋转一圈开发出更完善新版本
完整的螺旋生命周期模型如图
螺旋模型的优缺点
优点
适用于内部开发的大规模项目
缺点
需要风险评估的经验
1.4.6 软件过程模型——喷泉模型
面向对象生命周期模型,体现迭代和无缝特性
迭代
求精,系统某部分常被重复工作多次,相关功能在每次迭代中逐渐加入演进系统
无缝
分析、设计、编码各阶段间不存在明显边界
喷泉模型如图
1.4.7 软件过程模型——统一过程模型
前面几个都是一维,统一过程为二维
统一过程模型应视为一种自适应方法学。也就是说,要根据具体所开发的软件产品进行修改。
统一过程的五个核心工作流
需求流
任务:准确确定客户的需求并从客户的角度找出存在的限制条件
分析流
任务:分析和提取需求,清楚的指出产品要做什么
设计流
任务:细化分析流制品,直至达到程序员可实现的形式
实现流
任务:用选择的实现语言实现目标软件产品
测试流
任务:对实现流产品进行测试
软件过程模型——统一过程模型
统一过程模型四个阶段
开始阶段:确定产品是否值得开发
细化阶段:针对目标,将目标细化成需求
构件阶段:产生软件可供测试的第一个版本
转换阶段:确保客户的需求得到满足
1.4.8 软件过程改进——SW-CMM模型
SW-CMM的五个成熟度级别
初始级
最低级别,有效的软件过程管理方法本质上没有得到使用
可重复级
使用了基本的软件项目管理措施
定义级
有充分的软件生产文档
管理级
为每个项目设计了质量目标和生产目标
最优级
持续改进软件过程
SW-CMM模型如图
1.4.9 敏捷开发
什么是敏捷?
Ivar Jacobson给出一个非常有用的论述:每个人都是敏捷的。敏捷团队是适应变更的灵活团队。变更就是软件开发本身,软件构建有变更、团队成员有变更、适应新技术会带来变更,各种变更都会对开发的软件产品以及项目本身造成影响。我们必须接受”支持变更“的思想,它应该根植于软件开发中的每一件事中,因为它是软件的心脏与灵魂。敏捷团队意识到软件是团队中所有人共同开发完成的,这些人的个人技能和合作能力是项目成功的关键所在。
Scrum敏捷过程模型
Scrum得名于橄榄球比赛,是Jeff Sutherland和他的团队在20世纪90年代早期发展的一种敏捷过程模型,近年来,Schwaber和Beedle对其做了近一步的发展
Scrum过程由“需求、分析、设计、演化、交付”等框架性活动组成。每一个框架活动过程中,发生于一个过程模式中的工作任务称为一个冲刺。冲刺中进行的工作适用于当前的问题,由Scrum团队规定并常常进行实时修改
Scrum过程流说明
待定项:一个能为用户提供商业价值的项目需求或者特性的优先级列表。待定项中可以随时加入新项,产品经理根据需要评估待定项并修改优先级
冲刺:由一些工作单元组成,这些工作单元是达到待定项中的需求所必须的,并且能在预定的时间段内(一般是30天)完成。冲刺过程中不允许有变更,因此冲刺给开发人员团队提供了短暂而稳定的工作环境
Scrum例会:Scrum团队每天召开的短会,一般是15分钟,主要回答三个问题:
上次例会后做了什么?
遇到了什么困难?
下次例会前计划做些什么?
演示:向客户交互软件增量,为客户演示所实现的功能并由客户对其进行评价。注意演示不需要包含计划中的所有功能,但是该时间段内的功能必须完成
第二章 可行性研究
导学目标
了解可行性研究的目的、任务等
掌握系统流程图的定义和基本思想
掌握数据流图的定义、基本符号以及分层结构的画法
掌握数据字典的4类元素、基本类型、以及数据元素的组成关系
了解成本/效益分析的目的以及手段
第一节 可行性研究
1、目的
用最小的代价在最短时间内确定问题是否可以被解决
2、任务
确定问题的定义(找准问题)
导出系统的逻辑模型
逻辑模型:概念上的模型
物理模型:实实在在纯在的模型
提供解法,从三个方面考究解法的可行性
技术可行性
经济可行性
操作可行性
法律可行性
开放方案可行性
制定初步的计划
3、步骤
复查系统规模和目标
研究目前正在使用的系统
导出新系统的高层逻辑模型
根据 【现有的物理模型】——>【现有的逻辑模型】——>【目标的逻辑模型】——>【目的的物理模型】
进一步定义问题
导出评价和供选择的解法
推动行动方案
草拟开发计划
书写文档提交复查
第二节 系统流程图
2.2.1 概述
目的
概括的描绘物理系统(是一种工具)
基本思想
利用图形符号(黑盒子)描绘组成系统的每个部件
注意事项
系统流程图表达的是数据在系统各个部件的流动情况
系统流程图不等于程序流程图
2.2.1 系统流程图的基本符号
基本符号
系统流程图示例
第三节 数据流图(DFD)
2.3.1 数据流图
数据流图是一种图形化技术,它描述信息流和数据从输入移动到输出的过程中所经历的变换
数据流图(DFD)描述系统逻辑模型、图中并没有具体的物理元素,只描述信息在系统中流动处理情况
数据流图是系统逻辑的图形表示,可以方便用户理解
设计数据流图时只需要考虑系统必须完成的基本逻辑功能,不要考虑具体如何去实现它
2.3.2 数据流图的符号
四种基本符号
正方形(或立方体):表示数据的原点或者终点
圆角符号(或圆形):代表变换数据的处理
开口矩形(或两条平行线):代表数据存储
箭头:表示数据流,即数据特定的流动方向
注意事项
数据流图不等于程序流程图,不会出现某个控制流的条件
数据流图的附加符号
2.3.3 案例
问题描述
工厂采购部采购员每天需一张订货报表,按零件编号排序列出所需订货零件。 对订货零件列下述数据:零件编号、名称、订货数量、目前价格,主次要供应者等。零件入库或出库称事务,通过仓库终端把事务报告订货系统。零件库存量少于库存临界值需订货。
数据流图的画法(由外向里、自定向下)
确定系统的输入输出
由于系统包括了哪些功能一时难以弄清楚,可以使范围尽量大一些,把可能有的内容全部包括进去
应该向用户了解系统从外界接受了什么数据、系统向外界输出了什么数据等信息
根据用户的答复画出数据流图的外围
由外向里画系统的顶层数据流图
首先,将系统的输入和输出数据用一连串的加工连接起来。
在数据流的值发生变化的地方就是一个加工
然后,给各个加工命名
最后,给文件命名
自顶向下逐层分解,给出分层数据流图
对于大型系统,为了控制复杂性,便于理解,需要采用自顶向下逐层分解的方法进行,即用分层的方法将一个数据流图分解成几个数据流图来分别表示
解法
从问题描述中提取数据流的4种成分
先考虑数据的源点和终点
源点:仓库管理员
终点:采购员
在考虑数据的处理
处理:处理事务、产生报表等
最后考虑数据流和数据存储
数据流:事务、订货信息、订货报表等
数据存储:订货信息、库存信息
解法
画出数据流图的基本系统模型
把基本系统模型细化,描述主要功能
对系统主要功能进一步细化
结束,涉及到具体如何实现功能时,不应再分解
2.3.4 数据流图分层
为表达数据加工情况,需采用层次结构数据流图。顶层数据流图包含一个加工项,底层流图指加工项不再分解的数据流图,中间层流图只在顶层和底层之间,是对其上层父图的细化
分层数据流图示意图如下
2.3.5 数据流图命名
命名规则
数据流(或数据存储)命名
用名词,区别于控制流
不使用缺乏具体含义的名词
代表整个数据流的内存,而不仅仅反应它的某些成分
处理命名
名字应该反应整个处理过程,而不是一部分功能
用动宾词语,尽量避免使用“处理”、“加工”等笼统动词
通常仅包括一个动词,否则分解
2.3.6 数据流图用途
作为信息交流的工具
作为分析和设计的工具
用数据流图辅助物理系统设计时,能够在数据流图上画出许多组自动化边界,每组自动化边界可能意味着一个不同的物理系统
自动化边界划分方案一
方案一
方案二
2.3.7 考题示例
工资计算系统包含如下功能:
计算工资
根据人事部门给出的出勤表和业绩表计算奖金和缺勤扣款,通过生成的奖金发放表及工资基本信息库的信息计算应发工资,根据应发工资表计算所得税,根据后勤部门给出的水电扣款及缺勤扣款表和所得税款计算出实发工资,生成实发工资表和工资清单。
打印工资清单
根据工资清单完成工资条的打印,给职工
工资转存
根据实发工资表生成职工工资存款清单并将其发送到银行
请画出数据流图
顶层数据流图
功能级数据流图
细化功能级数据流图
第四节 数据字典(DD)
2.4.1 数据字典
定义
数据字典是数据流图中所有元素定义的集合
四类元素
数据流
数据元素(数据流分量)
数据存储
处理
定义方法
对数据自顶向下进行分解
数据元素组成数据的方式
顺序:已确定次序连接两个或者多个分量
选择:从两个或者多个可能的元素中选择一个
重复:把指定的分量重复0次或者多次
可选:一个分量可有可无(0次或者1次)
定义符号
2.4.2 考题案例
题目
北京某高校可用的电话号码有以下几类:校内号码由4位数字组成,第一位数字不是0;校外号码又分为本市电话和外地电话两类,拨校外电话需先拨0,若是本市电话则再接着拨8位数字(第一位不是0),若是外地电话则拨3位区码,再拨8位电话号码(第一位不是0)
解法
电话号码 = [校内电话号码 | 校外电话号码]
校内电话号码 = 非零数字 + 3位数字
校外电话号码 = [本市号码 | 外地号码]
本市号码 = 数字零 + 8位数字
外地号码 = 数字零 + 3位数字 + 8位数字
非零数字 = [ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ]
3位数字 = 3 { 数字 } 3
8位数字 = 非零数字 + 7位数字
7位数字 = 7 { 数字 } 7
数字 = [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ]
第五节 成本/效益分析
目的
从经济的角度分析开发一个特定的新系统是否划算
成本估计
软件开发成本主要表现为人力消耗,有三种估算技术
代码行技术
任务分解技术
自动估计成本技术
成本效益分析的方法
估计开发成本、运行费用、以及经济效益
第三章 需求分析
导学目标
了解需求分析的目的和基本任务等
了解常见的获取需求的方式
掌握需求分析建模和结构化分析方法
掌握实体联系图(E-R图)画法
了解数据规范化分类及目的
掌握状态转换图的画法
了解层次方框图和IPO图
第一节 需求分析的任务
3.1.1 需求分析
软件定义时期的最后一个阶段,准确的回答系统必须做什么这个问题
系统分析员和用户一起商定
清楚、准确、具体的描述软件产品必须具备的功能、性能、运行规则等各方面的要求
3.1.2 需求分析的任务
基本任务
准确的回答系统必须做什么的问题
具体任务
确定软件系统的综合需求
分析系统的数据需求
数据模型、信息模型E-R图、层次方框图
导出软件系统的逻辑模型
数据流图、E-R图、状态转化图、数据字典、算法
修正系统的开发计划
验证软件需求分析的正确性
编写软件规则需求说明书
确定软件系统的综合需求
功能需求:系统必须提供的服务
系统要做什么?
系统什么时候做什么?
系统何时修改或者升级?
性能需求:系统必须满足的定时约束或者容量约束等
软件开发的技术性指标,包括响应时间、存储容量限制、执行的速度以及吞吐量
可靠性和可用性需求
系统可靠性要求?
系统平均出错时间?
出错后重启系统允许的时间?
出错处理需求
系统对环境错误应该如何响应
接口需求:系统与它的环境通信格式要求
用户接口需求
硬件接口需求
软件接口需求
通信接口需求
约束需求:设计或实现约束时应遵守的限制条件例如精度、工具、语言、设计、标准、平台等
环境约束
界面约束
用户或者人的因素约束
文档约束
数据约束
资源约束
安全保密要求约束
软件成本消耗与开发进度需求
逆向需求:说明系统不应该做什么
将来可能提出的要求
3.1.3 需求分析的注意事项
工作大致分为4个阶段
对问题的识别
分析与综合
制定规格说明
评审
3个基本原则
必须能够表达和理解问题的数据域和功能域
必须按自顶向下、逐步分解的方式对问题进行分解和不断细化
要给出系统的逻辑视图和物理视图
3.1.4 优秀需求分析的特点
完整性
正确性
可行性
必要性
划分优先级
无二义性
可验证性
第二节 获取需求的方法
3.2.1 需求获取存在的问题
客户说不清楚自己的需求
需求常常变化
问题的复杂性和对问题空间理解的不完备性与不一致性
3.2.2 需求获取的方法
访谈
基本形式:正式、非正式访谈
需要收集大量人员意见:使用调查表
情景分析技术:对用户将来使用目标系统解决某个具体问题的方法和结果进行分析
情景分析技术的用途:
一定程序上演示目标系统的行为,方便用户理解
保证用户在需求分析过程中扮演一个积极主动的角色
面向数据流自顶向下求精
结构化分析方法
从系统的高层数据流图输出端出发
对不清楚的地方可以与用户和其他人员交流
利用数据流图、数据字典、IPO图向用户解释系统
根据用户反馈进行补充
细化数据流图
简易的应用规格说明
面向团队的需求收集法
提倡用户与开发者密切合作
信息系统领域使用的主流技术
典型过程
初步访谈
开发者和用户分别写产品需求
组织会议,会前审查产品需求
会议讨论,严禁批评与争论
针对每个议题创建一张意见一致的列表
小组制定小型规格说明,供大家讨论
快速建立软件原型(了解)
实现用户看得见的功能
快速
容易修改
第三节 需求分析建模
3.3.1 需求分析的四个阶段
调查研究
系统分析员协同程序员向用户做需求调查,根据可行性报告和项目开发计划报告,确定系统必须做什么,并获得当前系统的具体模型,用数据流图或者IPO 图表示出来
【补充数据字典、修改IPO 图】
分析综合
系统分析员从数据流和数据结构出发,逐步细化所有软件的功能,剔除其中不合理部分,增加其需要的部分,最终合成系统的解决方案,并给出目标系统的详细逻辑模型
【系统分析员和用户追踪数据流图,复查系统逻辑模型】
书写需求分析文档
把分析的结果用正式的文档记录下,主要包括4份文档资料:系统规格说明书、数据需求、用户系统描述、修正的开发计划
【系统规格、数据要求、用户系统描述等文档】
需求分析评审
作为需求分析的复查手段,在需求分析的最后一步,应该对功能的正确性、完整性和清晰性以及其他需求给与评价。
【评审结果】
3.3.2 需求分析阶段常用的模型(逻辑模型)有三种
数据模型
E-R图:描述数据对象与数据对象之间的关系
功能模型
数据流图:描述数据在软件系统中移动时被变换的逻辑过程
行为模型
状态转换图:描绘了系统中的各种状态以及不同状态间转换的方式
需求分析步骤
3.3.3 结构化分析方法(SA)
结构化分析方法是70年代中期提出的一种面向数据流、自顶向下、逐步求精进行需求分析的方法
核心思想是分解化简问题、物理与逻辑表示分开、进行数据与逻辑抽象
适用于分析大型数据处理系统,特别适合企事业管理系统
使用的建模工具主要包括
数据流图:表达系统内数据的流动情况
数据字典:用于定义系统中的数据元素
结构化语言、判断表、判定树:描述数据流的加工的工具
第四节 实体联系图
3.4.1 概念性模型
定义
概念性模型也叫做信息模型,是一种面向问题的数据模型,按照用户的观点对数据进行建模
最常用的概念数据模型
实体联系图(Entity-Relationship Diagram),简称ER图
作用
有助于理解业务和系统中数据组成的关系及交互
可以使用简单的符号使不熟悉计算机的用户也能理解
组成ER模型的三要素
数据对象
具有一系列不同性质或属性的事物,仅有单个值的事物不是数据对象
数据对象可以是外部实体、事物、行为、事件、角色、单位、地点、结构等
数据对象使用矩形框表示
数据对象之间是彼此有关联的
属性
定义了实体或者联系所具有的性质
用椭圆形或者圆角矩形表示
联系
数据对象彼此之间相关连接的方式
联系可以是一对一、一对多、多对多的关系
用菱形框表示
联系也可以有属性
3.4.2 实例分析
一个图书馆借阅管理数据库要求提供下述服务:
(1)可随时查询书库中现有书籍的品种、数量与存放位置。所有各类书籍均可由书号唯一标识。
(2)可随时查询书籍借还情况,包括借书人单位、姓名、借书证号、借书日期和还书日期。我们约定:任何人可借多种书,任何一种书可为多个人所借,借书证号具有唯一性。
(3)当需要时,可通过数据库中保存的出版社的电报编号、电话、邮编及地址等信息下相应出版社增购有关书籍。我们约定,一个出版社可出版多种书籍,同一本书仅为一个出版社出版,出版社名具有唯一性。
根据上述描述,画出满足需求的E-R图
第五节 数据规范化
目的
减少数据冗余
避免插入或者删除异常
简化数据修改的过程
范式
范式消除数据的冗余程度
范式分为1NF~5NF,1NF 冗余程度最大,5NF 冗余程度最小
第一范式(1NF)
数据的冗余程度最大
每个属性必须是原子值,即仅仅是一个简单值而不含内部结构
第二范式(2NF)
满足一范式条件
每个非关键字属性都由整个关键字决定
第三范式(3NF)(一般使用最多)
满足第二范式条件
每个非关键字属性都仅由关键字决定,一个非关键字属性值不能依赖于另一个非关键字属性值
第六节 状态转换图
3.6.1 状态转换图
状态转换图
简称状态图
通过描绘系统的状态及引起系统状态转换的事件,来表示系统的行为
状态
任何可以被观察到的系统行为模式
一个状态代表系统的一种行为模式
状态规定了系统对事件的响应方式
主要状态
初态(初始状态):仅1个
终态(最终状态):0-N个
中间状态
状态图可以表示系统循环运行的过程,而不关心循环如何启动
状态图可以表示系统单程生命期,需注明初始状态和最终状态
事件
某个特定时刻发生的事情
引起系统做动作或从一个状态转换到另一个状态
简而言之,事件就是引起系统做动作或状态转换的控制信息
符号
在状态图中,初态是实心圆
终态是同心圆(内圆为实心圆)
中间状态是圆角矩形,共分成上中下三个部分
状态名称(必须有)
状态变量名和值(可选)
活动表(可选)
示例
示例名词解释
活动表
语法格式:事件名(参数表)/动作表达式
事件名可以是任何事件的名称,经常使用3种标准事件
entry事件:指定进入该状态的动作
exit事件:指定退出该状态的动作
do事件:指定在该状态下的动作
参数表:需要时可以为事件指定
动作表达式:描述应该做的具体动作
事件表达式
语法格式:事件说明[守卫条件]/动作表达式
事件说明的语法:事件名(参数表)
守卫条件是一个布尔表达式
如果同时使用事件说明和守卫条件,则当且仅当事件发生且布尔表达式为真时,状态转换才发生;如果只有守卫条件而没有事件说明,只要守卫条件发生,则状态转换就发生
动作表达式是一个过程表达式,当状态转换开始时执行该表达式
3.6.2 案例
图书状态图
电话系统状态图
第七节 其他图形工具
3.7.1 层次方框图
用树形结构的一系列多层次的矩形框描述数据的层级结构
示例
3.7.2 IPO 图
输入、处理、输出图的简称
IBM 公司发展完善的一种图形工具
示例
第四章 总体设计
导学目标
了解总体设计目的、必要性、基本任务等
了解总体设计的两个阶段、9个步骤
掌握总体设计原理,耦合和内聚是重中之重
掌握启发式规则
掌握层次图、HIPO图、结构图等
掌握面向数据流的设计方法,变换和事务是重中之重
总体设计
总体设计的必要性
全局性分析,选择最佳方案和最合理的软件结构
总体设计又叫做概要设计或初步设计
开发阶段的开始:决定怎么做(框架)
基本目的:概括的说系统该如何实现
重要任务是设计软件的结构,也就是确定系统中每个程序是由哪些模块组成,以及这些模块相互之间的关系
总体设计阶段的主要任务
划分物理元素
物理元素的组成包括:程序、文件、数据库、人工过程和文档等
每个物理元素处于黑盒子级
设计软件结构
确定系统中的模块组成与模块之间的关系
第一节 设计过程
4.1.1 总体设计过程共分为两个阶段
系统设计:确定系统的实现方案
结构设计:确定软件结构
4.1.2 总体设计的过程包含9个步骤
分类:
1-3步骤为系统设计
4-9步骤为结构设计
步骤:
1.设想出选择的方案
2.选择合理的方案
3.推荐最佳方案
4.功能分解
5.设计软件结构
6.设计数据库
7.制定测试计划
8.书写文档
9.审查的复审
1、设想供选择的方案
综合考虑各种方案,选择出最佳方案
从数据流图DFD出发
区分DFD的各种处理,并分组
考虑各种处理的实现策略
设想并列出方案,但不评价
实例讲解:客房管理系统局部方案选择
预定请求类型的选择
只接受电话预定
只接受网上预定
只接受上门预定
接受上述方案中任意组合预定
夜审时间与餐费列入方法的确定
中午12点
早晨8点
餐费列入住宿费
。。。。
2、选择合理的方案
系统分析员应该提供4份资料
系统流程图
组成系统的物理元素清单
成本效益分析
实现这个系统的进度计划
考虑需求规则说明书的要求并进一步征求用户的意见
实例讲解:口算高手
物理元素清单:
一个程序
六个模块:输入模块、校验模块、退出模块、计算模块、打印模块、显示模块
成本效益分析:
货币的时间价值
投资回报期
纯收入
投资回收率
实现进度计划:
可行性分析0.5天
需求分析1天
总体设计1天
详细设计与编码1天
软件测试3天
文档资料编写、审议1天
3、推荐最佳方案
分析员对比各种合理方案的利弊,推荐最佳方案
用户和技术专家审查通过
使用部门负责人认可
完成系统设计,进入结构设计阶段
注意:以上都为总体设计过程的系统设计阶段
4、功能分解
先进行结构设计,确定程序由哪些模块组成以及这些模块之间的关系,是总体设计阶段的任务
后进行过程设计,确定每个模块的处理过程,是详细设计阶段的任务
把复杂的功能进一步分解成一些简单的功能,对大多数程序员都是浅显易懂的
实例讲解:口算高手
模块组成
5、设计软件结构
把模块组织成良好的层次系统:顶层模块调用下层模块,下层模块调用更下层模块
软件结构可以由层次图或者结构图来描绘
6、设计数据库
模式设计
子模式设计
完整性和安全性设计
优化:模式和子模式优化,利于存取
7、制定测试计划
测试方法选择:白盒测试/黑盒测试
测试内容设计:模块测试、功能测试、性能测试......
测试条件:人员/设备......
测试用例设计
测试人员安排
测试时间进度
8、书写文档
系统说明
系统流程图:描绘系统的构成方案
组成的物理元素清单
成本效益分析
最佳方案概括描述
精化的数据流图
软件结构:层次图或结构图
模块算法:IPO等工具
用户手册:修正/更改初步用户手册
测试计划
测试策略
测试方案
预期的测试结果
测试进度计划
详细的实现计划
数据库设计结果
9、审查和复审
第二节 设计原理
4.2.1 模块化
模块
是数据说明和可执行语句的序列,每个模块单独命名并且可以通过名字对模块进行访问
模块化设计
把大型软件按照规定的原则划分为一个较小的、相对独立但又相关的模块的设计方法
模块化设计的指导思想
功能分解、信息隐藏、模块的独立性
模块示例
C语言子程序
函数
宏
对象
方法
模块化分解示例
设函数
C(x)
定义问题x的复杂程度,函数E(x)
确定问题x
需要的工作量(时间),对于两个问题P1
和P2
,如果C(P1) > C(P2)
,显然E(P1)>E(P2
)根据人类解决一般问题的经验,如果一个问题由P1和P2两个问题组合而成,那么它的复杂程序大于分别考虑每个问题的复杂度之和,即
C(P1+P2) > C(P1) + C(P2)
综上所述,
E(P1+P2) > E(P1) + E(P2)
把复杂问题分解成许多容易的小问题,原来的问题也就容易解决了,这也是模块化原理的根据
模块化和软件成本
控制结构(程序结构)
控制结构是软件模块间关系的表示
控制结构的层次规则
只有一个顶层(0层)模块
除顶层外,任一模块都会在它的邻层存在一模块与它相关
同层模块间不存在联系
软件结构度量术语
软件结构度量术语解释
宽度:软件结构内同一层次上的模块总数的最大值
深度:一个模块包含自身及其他模块的层数
扇出:一个模块直接调用的模块数,平均扇出3-4
扇入:有多少个上级模块调用它
软件结构度量图示例
软件结构度量示例
4.2.1 抽象
抽象就是抽象出事物的本质特性而暂时不考虑它们的实现细节
当考虑对任何问题的模块化解法时,可以考虑多层次抽象
最高层:使用问题的环境语言,概括的叙述问题的解法
较低层:采用更过程化的方法,将面向问题和面向实现结合起来叙述问题的解法
最低层:直接实现的方式,叙述问题的解法
4.2.3 逐步求精
定义
集中精力解决主要问题而尽量推迟对问题细节的考虑
作用
帮助软件工程师集中精力在与当前开发最相关的那些方面上,忽略那些对整体解决方法必要但是暂时不需要考虑的细节
实质
将一段时期内的问题按照优先级排序,逐个细化
4.2.4 信息隐藏和局部化
定义
模块内部的数据与过程,对不需要了解这些数据与过程的模块隐藏起来。只有那些为了完成总体功能在模块间必须交换的信息,才允许在模块间进行传递
原理
使一个模块的信息(数据和过程)对于不需要这些信息的模块来说是不能访问的
隐藏
意味着有效的模块化可以通过定义一组独立的模块而实现,这些独立的模块彼此间仅仅交换那些为了完成系统功能而必须交换的信息。目的是提高软件的独立性,即当修改或者维护模块时减小把一个错误传播到其他模块中去的机会
局部化
把一些关系密切的软件元素物理的放的彼此靠近,比如局部变量
实例
4.2.5 模块独立
概念
模块化、抽象、信息隐藏和局部化概念的直接结果
完成相对独立的子功能
模块之间的关系很简单
重要性
独立的模块化比较容易开发
独立的模块化比较容易测试和维护
度量标准(衡量模块的独立程度)
内聚:衡量一个模块内部各个元素彼此结合的紧密程度
耦合:衡量不同模块彼此间互相依赖(连接)的紧密程度
4.2.5.1 耦合
耦合强弱取决于模块间接口的复杂程度,进入或访问一个模块的点以及通过接口的数据等
应该尽可能追求松散耦合的系统,因为模块间的耦合程度强烈影响着系统的可理解性、可测试性、可靠性和可维护性
设计规则:尽量使用数据耦合,少用控制耦合和特征耦合,限制公共环境耦合的范围,完全不用内容耦合
耦合的7种类型
耦合的强度
非直接耦合/无耦合:最低(不可能发生的)
数据耦合:低耦合,一般来说,一个系统内可以只包含数据耦合
控制耦合:中耦合,模块分解后通常可以用数据耦合代替它
公共环境耦合:可以是全程变量、共享通信区、内存的公共覆盖区、任何存储介质上的文件、物理设备等。复杂程度随耦合模块个数变化。一读一取,松耦合;既读又去,介于数据耦合和控制耦合之间。
内容耦合:最高程度的耦合
实例说明
1、无直接耦合
两个模块之间完全独立,无任何连接
这里的模块1和模块2就是无直接耦合
Max_Value和Min_Value就是无直接耦合
2、数据耦合
一个模块在调用另一模块时,被调用模块的输入和输出都是一些简单的数据,属于松散耦合
示例
3、特征耦合
两个模块通过传递数据结构(不是简单的数据,而是记录、数组等)加以联系或者都与同一个数据结构有关系
实例
若将住户情况分别改为用水量和用电量则可以将特征耦合变为数据耦合
住户情况是一个数据结构,图中的模块都与该数据结构有关系计算水费和计算电费本来没有关系,但是由于都使用了住户情况这同一数据结构而产生依赖关系,他们之间就是特征耦合
4、控制耦合
模块向下属模块传递的信息(如开关、标志等)控制了被调用模块的内部逻辑
去除模块间控制耦合的方法
控制耦合增加了理解和编程的复杂性,调用模块必须知道被调用模块的内部逻辑,增加了相互依赖
将被调用模块内的判定上移到调用模块中进行
将被调用模块分解成若干单一功能模块
将控制耦合改为数据耦合的例子
5、外部耦合
一组模块均与同一外部环境关联,(例如I/O模块与特定的设备、格式、通信协议相关联),它们之间就存在外部耦合
外部耦合必不可少,但这种模块的数目应该尽可能少
6、公共耦合
一组模块引用同一个公用数据区(也称全局数据区、公共数据环境)
公共数据区指:全局数据区、共享通信区、内存公共覆盖区等
公共耦合示例:模块A、B、C存在错综复杂的联系
公共耦合存在的问题
软件可理解性降低
定位错误比较困难
软件可维护性差
软件可靠性差
公共数据区及全程变量无保护措施,因此要慎用
7、内容耦合
发生情况
一个模块访问另一个模块的内部数据
一个模块不通过正常入口,转到另一个模块内部
两个模块有一部分程序代码重叠(只可能出现在汇编程序中)
一个模块有多个入口(一个模块有多种功能)
图示
4.2.5.2 内聚
标志着一个模块内各个元素彼此结合的紧密程度
简单的说,理想内聚模块只做一件事情。设计时应该力求做到高内聚,通常中等程度的内聚也是可以的,而且效果和高内聚差不多。但是坚决不要使用低内聚
内聚和耦合是紧密相关的,模块内的高内聚往往意味着模块间的松耦合,内聚和耦合都是进行模块化设计的有力工具,但是实践表明内聚更重要,应该把注意力集中到提高模块的内聚程度上
内聚设计原则:
力求高内聚
中等内聚也可以使用
低内聚不要用
与耦合的关系:高内聚意味着低耦合
实践证明,内聚更重要,应该把更多注意力集中在提高模块的内聚程度上
内聚类型
1、偶然内聚
如果一个模块完成一组任务,这些任务即使彼此间有关系,关系也是很松散的,并且修改困难,0分
偶然内聚实例
模块M中的三个语句没有任何关系
缺点:可理解性差、可修改性差
2、逻辑内聚
把几种逻辑上相似的功能组合在一模块内,每次调用由传给模块的参数来确定执行那种功能且修改困难,1分
逻辑内聚示例
缺点:增强了耦合程度(控制耦合),不易修改,效率低
3、时间内聚
模块完成的功能必须在同一时间内完成,时间内聚比逻辑内聚要好一些,3分
时间内聚示例
比如初始化系统模块、系统结束模块、紧急故障处理模块等
4、过程内聚:
使用程序流程图作为工具设计软件时得到的模块且模块内各处理成分相关,且必须以特定顺序执行,属于中内聚,评分是5分
过程内聚示例:
5、通信内聚
模块中所有元素都使用同一个输入数据或产生同一个输出数据,属于中内聚,评分7分
通讯内聚示例
6、顺序内聚
使用数据流图工具设计软件时得到的模块,也叫作信息内聚。模块完成多个功能,各功能都在同一数据结构上操作,每一功能有唯一入口,属于高内聚,评分9分
顺序内聚示例:
7、功能内聚
最高内聚,是理想内聚,只做一件事情
模块仅包括为了完成某个功能所必须的所有成分,缺一不可;功能内聚属于高内聚,内聚性最强,评分10分
功能内聚示例:
第三节 启发规则
在长期开发过程中累积出的经验,虽不能普遍使用,但仍具有好的启示
常用的启发规则
改进软件结构,提高模块的独立性
降低耦合、提高内聚
模块规模应该适中
模块太大,分解不充分;模块太小,开销大于有效操作且接口复杂
深度、宽度、扇出和扇入要适中
深度:表示软件结构中控制的层数,粗略的表示一个系统的大小和复杂程度,深度为该模块包含自身其他模块的层数
宽度:软件结构内同一层次上的模块总数的最大值,宽度越大,系统越复杂,对宽度影响最大的是扇出
扇出:一个模块直接调用的模块数,平均扇出3-4
好的软件结构通常顶层扇出较高,中间扇出较少,底层扇入较高
扇入:表明一个模块有多少个直接上级调用它
模块的作用域应该在控制域之内
作用域:受该模块内一个判断影响的所有模块集合
控制域:这个模块本身以及直接或者间接从属于它的模块集合
改变作用域与控制域的方式:判断点上移或者作用域对象下移
实例:
A的控制域是A,B,C,D,E,F
修改作用域和控制域:A中判断点上移或者将G移到A下面
力争降低模块接口的复杂程度
设计单入口单出口模块
容易理解、容易维护
模块功能应该可以预测
第四节 描绘软件结构的图形工具
4.4.1 层次图
用来描绘软件的层次结构,也成H图
与层次方框图的关系
层次图中一个矩形代表一个模块
方框间的连线表示调用关系而不是组成关系
其他与层次方框图类似
适于自顶向下设计软件的过程中使用
示例
4.4.2 HIPO图
是IBM公司发明的“层次图加输入/处理/输出”的简称
为了使HIPO图具有可追踪性,在H图里处理最顶层的方框外,其他方框都加了编号
示例
4.4.3 结构图
结构图和层次图类似,一个方框代表一个模块,箭头或直线代表调用关系,带注释的箭头表示模块调用过程中来回传递的消息
示例
结构化设计方法
首先研究分析、审查数据流图,弄清数据的加工过程
根据数据流图确定问题类型(变换型or 事务型),分别处理
由数据流图推导出系统的初始结构图
利用一些试探性规则改进系统的初始结构图,直到符合要求为止
修改和补充数据字典
制定测试计划
结构化设计方法符号约定
结构图中的附加符号
第五节 面向数据流的设计方法
结构化设计方法就是基于数据流的设计方法
面向数据流的设计方法把信息流(数据流图)映射成软件结构
数据流图的两种类型;根据数据流类型的不同,系统对应的系统结构也不同
变换型数据流——>变换型结构
事务型数据流——>事务型结构
系统结构基本模型
数据流图基本模型
变换型数据流示例
事务型数据流示例
在大型DFD中,变换型和事务型结构往往共存
4.5.1 面向数据流设计方法的设计步骤
精化DFD
确定DFD类型
把DFD映射到系统模块结构,设计出模块结构的上层
基于DFD逐步分解高层模块,设计出下层模块
根据模块独立化原理,精化模块结构
模块接口描述
4.5.2 结构化设计的两种映射方法
SC:结构图
初始的结构图
4.5.3 变换分析方法
步骤
区分传入、变换中心、传出部分,在DFD上标明分界线
第一级分解,建立初始结构图,设计顶层和第一层模块
第二级分解,自顶向下分解,设计出每个分支的中下层模块
示例
变换中心
第一级分解
第一级分解后的SC
第二级分解:传入分支的分解
第二级分解:传出分支的分解
第二级分解:中心加工分支的分解
4.5.4 事务分析方法
虽然在任何时候都可以使用变换分析,但是当数据流具有明显的事务特点时,也就是有一个明显的发射中心(事务中心时),采用事务分析会更合适
步骤
在DFD上确定事务中心、发送部分、接受部分
画出结构图框架,把DFD上的三部分分别映射为事务控制模块、发送动作模块和接收模块
分解细化接受分支和发送分支,完成初始结构图
示例
4.5.5 设计优化
在设计早期阶段尽量对软件结构进行精化
在有效模块化前提下,使用最少的模块和最简单的数据结构
第五章 详细设计
导学目标
了解详细设计的目的、任务、原则等
了解结构程序设计的三种基本结构、分类、优点等
了解人机界面设计经常遇到的问题以及设计过程
掌握过程设计工具的画法:程序流程图、盒图,PAD图、判定表、判定树
掌握面向数据结构设计之Jackson图及Jackson方法
掌握程序复杂度度量的McCabe方法
详细设计
目的:确定应该怎样具体地实现所要求的系统
任务:设计出程序的蓝图
原则:逻辑模块的描述简明易懂;采用结构化程序设计方法,降低程序的复杂度,提高程序的可阅读性、可测试性和可维护性
第一节 结构程序设计
1965年,E.W.Dijkstra提出在高级语言中取消goto语句
1966年,Bohm和Jacopini提出三种基本结构——顺序、选择、循环
定义
是一种程序设计技术,采用自顶向下,逐步求精的设计方法和单入口单出口的程序结构
用顺序结构和循环结构可以实现选择结构,因此理论上最基本的控制结构只有两种
三种基本的控制结构
其他常用的控制结构
结构程序设计的优点
符合人们解决复杂问题的规律
先整体,后细节
不使用goto,易理解、易沟通
结构清晰、易于修改
程序结构设计分类
经典结构程序设计:顺序、IF-THEN-ELSE、DO-WHILE
扩展结构程序设计:DO-CASE、DO-UNTIL
修正结构程序设计:LEAVE、BREAK
第二节 人机界面设计
人机界面设计的重要性
人机界面的设计质量,直接影响用户对软件产品的评价
5.2.1 人机界面的设计问题
系统响应时间
指用户完成某个控制动作,到软件给出预期的响应
两个属性:
长度:不能过长也不能过短,适中
易变性:系统响应时间相对于平均响应时间的偏差
用户帮助措施
常见的用户帮助措施有:集成和附加
集成:从一开始就设计在软件里面
附加:在系统建成后添加到软件中
帮助措施必须解决下述问题:
是否是任何时候?
怎样请求帮助?
怎样显示帮助信息?
怎样返回到正常的交互方式中?
怎样组织帮助信息?
出错信息处理
是出现问题时,交互式系统给出的“坏消息”
交互式系统给出的出错信息或者是警告信息,有以下属性:
信息应该用用户可以理解的术语描述问题
信息应该提供有助于从错误中恢复的建设性意见
信息应该指出错误可能导致的哪些负面后果
信息应该伴随着听觉或者视觉上的提示
信息不能带有指责色彩
命令交互
提供命令交互时,需要考虑的问题
是否每个菜单选项都有对应的命令
采用何种命令形式
学习和记忆命令的难度有多大
用户是否可以定制或缩写命令
5.2.2 人机界面的设计过程
用户界面设计是一个迭代的过程
创建设计模型
原型实现这个原型
用户适用和评估
根据用户意见进行修改
用户界面工具箱或用户界面开发系统
用于界面设计和原型软件开发的软件工具
用户界面设计主要依靠设计者的经验,总结得出设计指南
一般交互指南
保持一致性
提供有意义的反馈
在执行有较大的破坏性动作之前要求用户确认
允许取消绝大多数操作
减少在两次操作之间必须记忆的信息量
提高对话、移动和思考的效率
允许犯错误
按功能对动作分类,并据此设计屏幕布局
提供对用户工作内容敏感的帮助措施
用简单动作或者动作短语作为命令名
信息显示指南
只显示与当前工作内容有关的信息
不要用数据淹没用户
使用一致的标记、标准的缩写和可预知的颜色
允许用户保持可视化语境
产生有意义的出错信息
使用大小写、缩进和文本分组以帮助理解
使用窗口分隔不同类型的信息
使用模拟方式显示信息
高效率的使用显示屏
数据输入指南
减少用户的输入动作
保持信息显示与数据输入的一致性
允许用户自定义输入
交互应该是灵活的
使在当前语境中不适用的命令不起作用
让用户控制交互流
对所有输入动作都提供帮助
消除冗余的输入
第三节 过程设计工具
详细设计使用的工具
描述程序的处理过程
可以分为图形、表格和语言三类
能提供对设计的无歧义描述,指明控制流程、处理功能、数据组织和其他方面的实现细节
详细设计工具的种类
程序流程图
盒图(N-S图)
PAD图
判定表、判定树
过程设计语言
5.3.1 程序流程图
程序框图,是20世纪70年代程序设计的主要工具,总的趋势是越来越多的人不再使用
程序流程图的主要缺点
不是逐步求精的好工具
箭头代表控制流,程序员不受任何约束
不易表示数据结构
程序流程图中使用的符号
使用示例:求一元二次方程的解
5.3.2 盒图(N-S图)
主要特点
功能域明确
不可能随意转移控制
容易确定局部和全局数据的作用域
很容易表现嵌套关系
基本符号
示例:求一元二次方程的解
5.3.3 PAD图
是问题分析图,它用二维树形结构的图来表示程序的控制流,将这种图翻译成程序代码比较容易
主要特点
必然是结构化程序
程序结构十分清晰
程序逻辑易读、易懂、易记
容易将PAD图转换为高级语言源程序
既可以表示程序逻辑,又可以表示数据结构
支持自顶向下,逐步求精的方法
基本符号
示例
5.3.4 判定表
在某些数据处理中,某数据流图的加工需要依赖于多个逻辑条件的取值,也就是完成加工的一组动作是某一组条件的取值组合而引发的,这时候使用判定表来描述比较合适
当算法中包含多重嵌套条件的条件选择时,用程序流程图、盒图、PAD图、过程设计语言(PDL)都不易清楚地描述。然而判定表确能够清晰的表示复杂的条件组合与应做的动作之间的对应关系
判定表组成
左上部列出的是所有的条件
左下部列出的是所有的操作
右上部表示各种条件组合的一个矩阵
右下部表示对应每种条件组合的操作
判定表示例:发货判定表
5.3.5 判定树
判定表虽然能清晰的表示复杂的条件组合与应做的动作之间的对应关系,但是其含义不是一眼就能看出来的,初次接触这种工具的人需要有个简短的学习过程
判定树是判定表的变种,它也能清晰的表示复杂的条件组合与应做的动作之间的对应关系。判定树的优点在于不需要任何说明,一眼就能看出其含义,易于理解和使用
判定树示例:发货判定树
5.3.6 过程设计语言(PDL)
也称伪码,是用正文的形式表示数据处理和过程的设计工具
PDL特点
具有关键字固定语法
自然语言的描述
数据说明的手段
模块定义和调用的技术
5.3.7 过程设计工具的选择
不太复杂的判断逻辑,使用判断树会比较好
复杂的判断逻辑,使用判定表比较好
如果一个处理逻辑既包含了一般的顺序执行动作,又包含了判断或循环逻辑,则使用结构化语言(PDL)比较好
第四节 面向数据结构的设计方法
定义
面向数据流的设计方法:根据数据流,确定软件结构
面向数据结构的设计方法:根据数据结构,设计程序处理过程的方法
目标
得出对处理过程的描述,也就是在完成软件设计之后,使用面向数据结构设计方法来设计每个模块的处理过程
方法
Jackson
Warnire
5.4.1 Jackson图
三种类型
顺序结构:数据由一个或者多个元素组成,每个元素按照确定次序出现一次
选择结构:数据包含两个或多个元素,按一定条件在这些元素中选择一个
重复结构:根据条件,由一个数据元素出现0次或者多次构成
优点
便于表示程序结构
形象直观、可读性好
既能表示数据结构,也能表示程序结构
改进Jackson图
Jackson图的缺点
表示选择或者重复结构时,选择或者循环结束条件不能直接在图上表示出来,影响了图的表达能力
改进策略
框间连线由斜线变为直线,增加选择和重复结构条件
改进后结构图
Jackson方法步骤
确定输入数据和输出数据逻辑结构,用Jackson图表达
确定输入结构和输出结构中有对应关系(因果)的单元
描绘数据结构的Jackson图导出描绘程序结构Jackson图
列出所有操作和条件,分配到Jackson图中
用伪码表示
5.4.2 考题分析
某仓库管理系统每天要处理大批单据的事务文件。单据分为订货单和发货单两种,每张单据由多行组成,订货单每行包括零件号、零件名、单价、数量等四个数据项,发货单每行包括零件号、零件名、数量等三个数据项,用Jackson结构图表示该事务文件的数据结构
第五节 程序复杂度度量
目的
定量的衡量软件模块的性质
方法
McCabe
Halstead
5.5.1 McCabe方法
McCabe方法是根据程序控制流的复杂程度定量度量程序的复杂程度,这样度量出的结果叫程序的环形复杂度
相关概念
这里使用流图
用圆表示结点点,一个圆代表一条或多条语句
程序流程图中的一个顺序的处理框序列和一个菱形判定狂,可以映射成流图中的一个结点
流图中的箭头线称为边,它和程序流程图中的箭头线类似,代表控制流。
在流图中一条边必须终止于一个结点,即使这个结点并不代表任何语句(实际上相当于一个空语句)。
由边和结点围成的面积称为区域,当计算区域数时应该包括图外部未被围起来的那个区域。
步骤
根据过程设计结果画出相应流图描述程序控制流,基本图形符号如下图所示
程序流程图映射成流图
计算环形复杂度(三种方法)
V(G)=区域数
V(G)=E-N+2 E为流图中边数,N为流图中节点数
V(G)=P+1 P为判定点数上图示例
V(G)=4 (区域数)
V(G)=11(边数)-9(结点数)+2=4
V(G)=3(判定结点数)+1=4
第六章 实现
导学目标
了解编码的目的、依据以及分类等
掌握软件测试的基本概念等
掌握单元测试、集成测试、验收测试等
掌握白盒技术和黑盒技术方法
了解软件调试的方法和软件可靠性的基本概念
第一节 编码
6.1.1 基本概念
实现 => 编码+ 测试
编码:把软件设计的结果翻译成某种程序设计语言书写的程序
测试:在软件投入生产性运营之前,尽可能多的发现软件中的错误。目前软件测试仍然是保证软件质量的关键步骤。
调试:通过测试发现错误后还需要进行错误改正。调试是测试阶段最困难的工作
6.1.2 程序设计语言
目的
把模块的过程性描述翻译为用选定的程序设计语言书写的源程序
依据
编码的依据主要是概要设计和详细设计的说明文档
程序设计语言分类
机器语言
机器语言是一种二进制代码表示的低级语言
示例
1011011000000000:加法
1011010100000000:减法
优点
计算机可以直接识别的
缺点
不易理解和使用、重用性较差
汇编语言
汇编语言是使用助记符表示的低级语言,需要经过汇编程序翻译成机器语言才能执行
示例
机器指令:1000100111011000
汇编指令:MOV AX,BX
优点
比机器语言易读写、易调试和修改
执行速度快、占内存少
针对硬件编制
缺点
不能编写复杂程序
依赖于机型、不通用、不可移植
汇编语言的执行过程
高级语言
高级语言是面向用户的,接近于自然语言的
示例
for(int i=0;i<10;i++){println(i)}
优点
编码效率高
通用性强、兼容性好,便于移植
缺点
运行效率低
对硬件的操作不如汇编
语言选择的标准
系统用户的要求
如果开发系统由用户维护,通常要求用他们熟悉的语言书写
可以使用的编译程序
运行目标系统环境可提供编译程序限制可选用语言的范围
可以得到的软件工具
有支持程序开发的软件工具可以利用
工程规模
规模庞大,现有语言不适用,设计实现供该工程项目使用程序设计语言
程序员的知识
如果和其他标准不矛盾,应选择程序员熟悉的语言
软件可移植性要求
软件的应用领域
6.1.3 编码规则
编码风格
逻辑简明清晰、易读易懂是重要标准
应该遵循5个方面的规则
程序内部的文档
数据说明
语句构造
输入输出
效率
1、程序内部文档
恰当的描述符
选取含义鲜明的名义
如:
void printScore( )
命名规则要一致
缩写规则要一致
如:
message
缩写为msg
适当的注解
序言性注解
中间注解
良好的视觉组织
空格
空行
缩进
2、数据说明
数据说明在编写程序时确定
数据说明的次序应该标准化(按数据结构或者数据类型说明)
常量->简单变量->数组->公用数据块->文件
整形->实型->字符->逻辑
多个变量名在一个语句说明,按字母顺序排列
复杂数据结构用注解说明实现方法和特点
3、语句构造
不要把多个语句写在同一行
student.name = “zs; student.age = 18;
尽量避免复杂的条件测试
尽量减少非条件的测试
if(not(a>b))
可替换为
if(a<=b)
避免大量使用嵌套循环和条件嵌套
利用括号使表达式运算次序更加清晰
int a[5] = {1,2,3,4,5};
int p = &a ;
println(p++)
避免使用goto语句
4、输入和输出
对所有输入数据都进行检验,保证输入有效
检查输入项重要组合合法性
保持输入格式简单
使用数据结束标记,不要求用户指定数据数目
提示交互式输入请求,如可用选择或边界数值
程序设计语言对格式有严格要求时,应保持输入格式一致
设计良好输出报表
给所有输出数据加标志
5、效率
程序的运行时间
简化算术和逻辑表达式
嵌套循环,确定是否有语句可从内层往外移
避免使用多维数组
尽量避免使用指针和复杂的表
使用执行时间短的算术运算
不要混合使用不同的数据类型
尽量使用整数运算和布尔表达式
存储器效率
大中型计算机考虑操作系统页式调度特点,将程序功能合理分块,每个模块或一组密切相关程序体积与每页容量相匹配,减少页面调度
微型计算机关键是程序简单性,选择生成较短目标代码且存储压缩性能优良的编译程序
输入输出效率
所有输入/输出都应有缓冲,减少通信的额外开销
对二级存储器(如磁盘)选用最简单访问方法
二级存储器的输入/输出以信息组为单位进行
如“超高效”输入/输出很难被理解,不采用
第二节 软件测试基础
6.2.1 相关概念
软件测试的目标
测试是为了发现程序中的错误而执行程序的过程
好的测试方案是极有可能发现迄今尚未发现的尽可能多的错误的测试
成功的测试是发现了迄今尚未发现的错误的测试
软件测试的定义
为了发现程序中的错误而执行程序的过程
软件测试只能查找出程序的错误,并不能证明程序中没有错误
为什么要测试?
软件开发过程必须伴有质量保证活动
软件测试时软件质量保证的关键因素
软件开发过程中的各个阶段都可能引入新的差错
软件测试的准则
所有测试应能追溯到用户需求
测试的目的是发现错误,其中最严重的是不能满足用户需求的错误
应尽早地和不断地进行软件测试
不应把软件测试仅看作是软件开发一独立阶段,应把它贯穿到软件开发各阶段中
把Pareto原理应用到软件测试中
Pareto原理说明,测试中发现80%的错误很可能是由程序中20%的模块造成的
测试应从小规模开始,逐步进行大规模测试
穷举测试是不可能的
应该由第三方进行测试工作
黑盒测试与白盒测试
黑盒测试(功能测试):如果知道产品应具有功能,可通过测试来检验是否每个功能都能正常使用
白盒测试(结构测试):如果知道产品内部工作过程可通过测试来检验产品内部动作是否按照规格说明书的规定正常进行
软件测试的一般过程
确定测试方案
预定要测试的功能
应该输入的测试数据和预期结果
设计测试用例
设计测试方案的基本目标
确定最可能发现某个错误或者某类错误的测试数据
通常采用黑盒测试,白盒测试做补充
逻辑覆盖
等价值划分
边界值分析(处理边界情况时程序最容易发生的错误)
错误推测
软件测试步骤
模块测试(单元测试)
模块测试的目的是保证每个模块作为一个单元能正确运行,所发现的往往是编码或者详细设计的错误
子系统测试
把通过单元测试的模块组成一个子系统来测试,着重测试的是模块间的接口
系统测试
把经过测试的子系统装配成一个完整的系统来测试,所发现的往往是软件设计和需求说明中的错误
无论是子系统测试还是系统测试,都包含检测和组装两层含义,通常称为集成测试
验收测试(确认测试)
类似于系统测试,区别是需要用户参与,可能使用的是实际数据进行测试。目的是验证系统是否真正满足了用户需要,发现的问题往往是需求说明书中的错误
平行测试
同时运行新开发出来的系统和即将被它取代的旧系统,以便比较新旧两个系统的处理结果
软件测试步骤如图
第三节 软件测试过程分类
6.3.1 单元测试
基本说明
测试之前必须先通过编译程序检查并改正所有错误
用详细设计说明书做指南,对重要的执行通路进行测试
单元测试可以使用白盒测试
多个模块的测试可以并行进行
测试重点
模块接口
数据是否正确进出模块
局部数据结构
检查局部数据说明、初始化、默认值是否有问题
重要执行通路
重要执行路径是否有错误计算、不正确比较或不适当控制流
出错处理通路
错误描述是否能以理解
记下的错误是否与实际错误不同
错误处理之前,错误条件已引起系统干预
错误处理不正确
描述错误的信息不足以帮助定位错误
边界条件
是单元测试中最重要的任务
测试方法
结合使用
代码审查(人工)
先由编写人非正式地进行,再由审查小组正式进行,可以查出30%—70%的逻辑错误和编码错误
编写者讲解,审查组审查
预排法
计算机测试
开发驱动软件:作为主程序
开发存根软件:代替被测试的模块所调用的模块
6.3.2 集成测试
基本说明
集成测试是测试和组装软件的系统化技术
主要目标是发现与接口有关的问题
由模块组装成程序有两种方法
非渐增式测试方法
先分别测试每一个模块,再把所有的模块一次组装进行测试
渐增式测试方法
把下一个要测试的模块和已经测试好的模块结合起来进行测试,测试完以后再把下一个应该测试的模块结合进来测试
两种方法的优缺点
成本开销
非渐增式测试方法分别测试每一个模块,需要编写的测试程序比较多,成本开销较大;
渐增式测试方法利用已测试的模块作为部分测试程序,成本开销较小
发现错误时间
渐增式可以较快的发现模块间接口错误
非渐增式最后一次将所有模块组合在一起,因此发现错误较晚
错误定位
非渐增式方法一下子把所有模块组合在一起,发现错误比较难定位;
渐增式方法发现错误,则可以容易定位为刚加入的模块有错误
测试彻底程度
渐增式测试方法是把已经测试好的模块和新加入的模块一起测试,已测试好的模块可以在新的条件下受到新的校验,测试更加彻底
测试进度
使用非渐增式测试方法可以并行测试所有模块,因此能充分利用人力,加快进度
集成测试的两种策略
1、自顶向下集成
自顶向下的结合方法是一个日益为人们广泛采用的测试和组装软件的途径
由四个步骤完成
对主控制模块进行测试,测试时用存根程序代替所有直接附属于主控制模块的模块
根据选定的结合策略(深度优先或者宽度优先),每次用一个实际模块代替一个存根程序
在结合进一个模块的同时开始测试
为了保证加入的模块没有引入新的错误,可能需要进行回归测试
自顶向下的结合策略能够在测试的早期对主要的控制和关键抉择进行检验
2、自底向上集成
自底向上测试是从原子模块开始组装和测试的
由四个步骤完成
把低层模块组合成某个特定软件的子功能的族
写一个驱动程序,协调测试数据的输入与输出
对由模块组成的子功能族进行测试
去掉驱动程序,沿软件结构自下向上进行移动,把子功能族组合起来形成更大的子功能族
6.3.3 回归测试
重新执行已作过测试的某子集,保证变化没带来非预期副作用
三类不同的测试用例
检测软件全部功能的代表性测试用例
专门针对可能受修改影响的软件功能附加测试
针对被修改过软件功能测试
6.3.4 确认测试
也叫验收测试,目标是检验软件的有效性
确认:为了保证软件确实满足了用户需求而进行的一系列活动
验收:保证软件实现了某个特定要求的一系列活动
验收标准
软件的有效性:如果软件的功能和性能如同用户所合理期待的那样,就说明软件是有效的
需求分析阶段产生的需求规则说明书是软件有效性的标准,也是确认测试的基础
确认测试范围
某些已经测过的纯粹技术性的特点可能不需要再次测试
对用户特别感兴趣的功能和性能,可能要增加一些测试
通常使用生产中的实际数据进行测试
可能需要设计并执行和用户使用相关的测试
验收测试必须有用户积极参与或者以用户为主
验收测试一般使用黑盒测试法
软件配置复查
是验收测试的重要内容,检查软件手册等
Alpha测试
用户对即将面市软件产品(称α版本)进行测试,开发者坐在用户旁边,随时记下错误情况和使用中问题,是受控环境下测试
Beta测试
在一个或者多个用户场所进行,开发者不在现场
第四节 软件测试技术
6.4.1 设计测试方案
是测试阶段的关键性问题
测试方案包括测试目的、输入数据和预期结果
测试用例= 输入数据+ 预期结果
测试方案的基本目标是确定一组最可能发现某个错误或者某类错误的测试数据,其中使用的典型技术是白盒测试和黑盒测试
6.4.2 白盒测试
常见的白盒测试技术
逻辑覆盖
控制结构测试
1、逻辑覆盖
(1) 语句覆盖
使程序中的每个语句至少执行一次
只需要设计一个测试用例
A=2,B=0,X=4 覆盖模块:sacbed 覆盖路径:1-4-5-6-7
语句覆盖只关心判断表达式的值,不关心表达式中每个条件取不同值的情况
语句覆盖是最弱的逻辑覆盖
(2) 判定覆盖
又叫分支覆盖,不仅每个语句至少执行一次,每个判定的真假分支至少执行一次
设计两组测试用例:
A=3,B=0,X=3 覆盖模块:sacbd 覆盖路径:1-4-5-3
A=2,B=1,X=1 覆盖模块:sabed 覆盖路径:1-2-6-7
两组测试用例可以覆盖所有判定路径的真假
判定覆盖是弱的逻辑覆盖
(3) 条件覆盖
不仅每个语句至少执行一次,而且使判定表达式中的每个条件都取到各种可能的结果
设计两组测试用例
A=2,B=0,X=4 满足条件:A>1,B=0,A=2,X>1 覆盖模块:sacbed 覆盖支路:1-4-5-6-7
A=1,B=1,X=1 满足条件:A<=1,B≠0,A≠2,X<=1 覆盖模块:sabd 覆盖支路:1-2-3
条件覆盖一般比判定覆盖要强
但是也有可能是相反的情况
满足条件覆盖,但是不满足判定覆盖
A=2,B=0,X=1 满足条件:A>1,B=0,A=2,X<=1 覆盖模块:sacbed 覆盖支路:1-4-5-6-7
A=1,B=1,X=2 满足条件:A<=1,B≠0,A≠2,X>1 覆盖模块:sabed 覆盖支路:1-2-6-7
(4) 判定/条件覆盖
既满足判定覆盖,又满足条件覆盖
选取足够多的测试数据,使得判定表达式中的每个条件都取到各种可能的值,而且每个判定表达式也都取到各种可能的结果
判定/条件覆盖也不一定比条件覆盖更强
设计测试用例
A=2,B=0,X=4 满足条件: A>1,B=0,A=2,X>1 覆盖模块: sacbed 覆盖支路:1-4-5-6-7
A=1,B=1,X=1 满足条件: A<=1,B≠0,A<=2,X<=1 覆盖支路: 1-2-3
该组测试用例就是条件覆盖所用的测试用例
(5) 条件组合覆盖
选取足够多的测试数据,使得每个判定表达式中条件的各种可能组合至少出现一次
8种组合方式如下:
A>1,B=0
A>1,B≠0
A<=1,B=0
A<=1,B≠0
A=2,X>1
A=2,X<=1
A≠2,X>1
A≠2,X<=1
可设计4种测试用例:
A=2,B=0,X=4 满足条件【1、5】:A>1,B=0,A=2,X>1 覆盖模块:sacbed 覆盖支路:1-4-5-6-7
A=2,B=1,X=1 满足条件【2、6】:A>1,B≠0,A=2,X<=1 覆盖模块:sabed 覆盖支路:1-2-6-7
A=1,B=0,X=2 满足条件【3、7】:A<=1,B=0,A≠2,X>1 覆盖模块:sabed 覆盖支路:1-2-6-7
A=1,B=1,X=1 满足条件【4、8】:A<=1,B≠0,A≠2,X<=1 覆盖模块:sabd 覆盖支路:1-2-3
(6) 点覆盖
使程序执行路径至少经过流图的每一个节点一次
点覆盖标准和语句覆盖标准相同
(7) 边覆盖
使程序执行路径至少经过流图的每一条边一次
通常边覆盖和判定覆盖是一致的
(8) 路径覆盖
使程序中每条可能的路径都至少执行一次
2、控制结构测试
现有的很多种白盒测试技术,是根据程序的控制结构设计测试数据的技术
常用的控制结构测试技术
基本路径测试
循环测试
(1) 基本路径测试
Tom McCabe提出的一种白盒测试技术,步骤如下:
根据过程设计的结果画出相应的流图
计算流图的环形复杂度
V(G)=区域数 V(G)=E-N+2 E为流图中边数,N为流图中节点数 V(G)=P+1 P为判定点数
确定线性独立路径的基本集合
独立路径:至少包含一条在定义该路径之前不曾用过的边
环形复杂度:独立路径基本集的上界
设计测试用例覆盖基本集合的路径
计算环形复杂度示例
e=10,n=7
V=e-n+2=5
V=区域数=5
V=判定节点数+1=5
独立路径示例
路径1:1-2-4-6-7
路径2:1-2-4-2-5-7
路径3:1-2-5-7
路径4:1-3-7
路径5:1-3-5-7
注意:一些独立路径无法独立测试,程序的正常流程不能形成独立执行该路径所需的数据组合,这种情况下这些路径必须作为其他路径的一部分来测试
(2) 循环测试
分为3种:简单循环、嵌套循环、串接循环
简单循环
跳过循环
只通过循环一次
通过循环两次
通过m次,m<n-1(n是允许通过循环的最大次数)
通过n-1,n,n+1次
嵌套循环
从最内层循环开始,把所有其它层循环设置为最小值
对最内层循环做简单循环的全部测试
逐步外推,测试时保持所有外层循环变量取最小值,其它嵌套内层循环变量取“典型”值
反复进行,直到所有各层循环测试完毕
串接循环
各个循环互相独立,可用与简单循环相同方法进行测试
几个循环不是互相独立,需要使用测试嵌套循环
6.4.3 黑盒测试
黑盒测试着重测试软件功能
黑盒测试并不能取代白盒测试,它们之间是互补关系
白盒测试在测试阶段的早期进行,黑盒测试主要用于测试过程的后期
黑盒测试力图发现的错误类型
功能不正确或者遗漏了功能
界面错误
数据结构错误或外部数据库访问错误
性能错误
初始化和终止错误
设计黑盒测试方案应该考虑的问题
怎样测试功能的有效性
哪些类型的输入可以构成好的测试用例
系统是否对特定的输入值敏感
怎样划定数据类的边界
系统能够承受什么样的数据率和数据量
数据的特定组合将对系统运行产生什么影响
常见的黑盒测试技术
等价划分
边界值分析
错误推测
1、等价划分
把程序的输入域划分成若干数据类,从每一数据类选取少数有代表性数据做为测试用例
每类中的一个典型值在测试中的作用与这一类中所有其他值的作用相同
划分等价类的标准
覆盖
不相交
代表性
等价类划分原则
如果规定了输入值范围则可以划分出一个有效的等价类(输入值再此范围内)和两个无效等价类(输入值小于最小值或大于最大值)
例如:学生成绩
如果规定了输入数据的个数,类似的可以划分出一个有效的等价类和两个无效的等价类
例如:整形数组的访问
int arr [4] ; 实际arr数组的范围 arr[0]-arr[3] 一个有效的等价类: arr[1] 两个无效的等价类: arr[-1]、arr[4]
如果规定了输入数据的一组值,而且程序对不同输入值做不同处理,则每个允许的输入值是一个有效的等价类,此外还有一个无效等价类(任一个不允许的输入值)
例如:星期值的枚举
enum Weekday{Sun、Mon、Tue、Wed、Thu、Fri、Sat} 7个有效等价类:Sun、Mon、Tue、Wed、Thu、Fri、Sat 一个无效类:monta
如果规定了输入数据必须遵循的规则,则可以划分出一个有效的等价类和若干个无效等价类
例如学生的年龄:
int age; (0< age <=100) 一个有效类: 18 多个无效类:-1 ,200,3.5等
如果规定了输入数据是整形,则可以划分出正整数、负整数、0 三个有效等价类
例如规定数据输入数据num为整形
int num; num取值范围:-65535-65535 三个有效类:-1,0,1 多个无效类:-70000,70000
如果处理的对象是表格,则应该使用空表以及含一项或者多项的表
确认测试用例
建立等价类表,列出所有划分出等价类,为每一等价类规定一唯一编号
设计一新测试用例,尽可能多覆盖尚未被覆盖有效等价类,重复,直到所有有效等价类被覆盖
设计一新测试用例,仅覆盖一尚未被覆盖无效等价类,重复,直到所有无效等价类被覆盖
等价划分实例:
某城市电话号码有三部分组成
地区码:空白或三位数字
前缀:非0或1开头的4位数字;
后缀:4位数字
解:
2、边界值分析
处理边界时,程序最容易发生错误
着重测试边界情况,选取测试数据为刚刚等于,刚刚小于,刚刚大于的边界值
3、错误推断
靠经验和直觉推测程序可能存在错误,有针对性编写检查这些错误的测试用例
三种技术策略使用方法
优先使用边界值分析
必要时使用等价划分和错误推断补充测试方案
6.4.4 总结
黑盒测试和白盒测试的比较
黑盒测试是站在用户的角度,根据需求规则说明书对软件的外部功能进行测试
白盒测试是根据程序的内部逻辑进行测试
无论黑盒测试还是白盒测试都不能进行穷尽测试
第五节 调试
软件调试是在进行了成功的测试之后才开始的工作。它与软件测试不同,调试的任务是进一步诊断和改正程序中潜在的错误
软件调试过程图
软件调试过程步骤
执行测试用例
评估结果,找出错误的内在原因
找到则改正错误,并进行回归测试
未找到,加测试用例证明猜测原因
软件调试过程图
蛮干法
将内存内容打印出来分析
程序特定部位设置打印语句
使用调试工具
回溯法
确定最先发现“症状”位置。人工沿程序控制流程向回追踪源代码,直到找到错误根源或确定错误产生范围
原因排除法
对分查找法、归纳法、演绎法
对分查找法
如果已知每个变量在程序内若干关键点正确值,用赋值或输入语句在程序中点附近“注入”正确值,运行程序检查输出
正确,错误原因在程序上半部分;反之,在程序后半部分
反复使用,将程序出错范围缩小到容易诊断的程度
归纳法
归纳法是一种从特殊推断一般的逻辑方法
归纳法调试的想法是:从一些线索着手,通过分析它们之间的关系来找出错误
归纳法的步骤
演绎法
演绎法是从一般原理和前提出发,经过排除和精化的过程来推导出结论的逻辑方法
根据已有测试用例,设想所有可能出错原因
逐个排除不正确的
验证余下假设确是出错原因
第六节 软件的可靠性
基本概念
可靠性:程序在给定时间间隔及环境条件下,按规格说明书的规定,成功运行的概率
可用性:给定的时间点,按规格说明书规定,成功运行概率导学目标
第七章 维护
导学目标
掌握软件维护的概念以及4类维护性活动
掌握软件维护的本质和过程
掌握软件可维护性的定义及决定因素
掌握软件再工程的定义以及6类活动
第一节 软件维护的概念
在软件产品开发出来并交付用户使用之后,就进入了软件的维护阶段
维护是软件生命周期的最后一个阶段,其基本任务是保证软件在一个相当长的时期能够正常运行
软件工程的主要目的是提高软件的可维护性,减少软件维护所需要的工作量,降低软件系统的总成本
软件维护的定义
软件维护就是软件已经交付使用后,为了改正错误或者满足新的需要去修改软件的过程
4类维护
改正性维护:针对原有错误
适应性维护:针对环境变化
完善性维护:针对功能扩展
预防性维护:针对未来发展
软件维护的特点
结构化维护与非结构化维护差别巨大
非结构化维护
软件配置的唯一成分是程序代码
缺少程序文档,难以理解源程序
所有改动的后果是难以估量的
缺乏测试文档,无法进行回归测试
结构化维护
减少精力浪费,提高维护的总体质量
维护的代价高昂
1970年维护费用占总预算35%-40%,1990年上升至70%-80%
无形代价:
用户不满
维护时引入了潜在错误,降低了软件质量
造成开发过程混乱,生产率大幅度下降
维护问题很多
理解别人写的程序非常困难
需要维护的软件没有合格的文档
维护软件时不能指望开发人员给我们仔细说明软件
绝大多数软件在设计时没有考虑到将来修改
软件维护不是一项吸引人的工作
第二节 软件维护的过程
本质
软件维护的本质是修改和压缩了的软件定义和开发过程
软件维护的过程
维护组织
在开始前就要明确责任,有维护管理员和系统管理员
维护报告
软件维护人员通常给用户提供空白的维护要求表,由维护管理员和系统管理员进行评价
软件组织内部指定软件修改报告
满足维护要求表中提出的要求所需要的工作量
维护要求的性质
这项要求的优先次序
与修改有关的事后数据
维护事件流
确定维护的类型
对于改正性维护:从估量错误的严重程度开始
严 重:分派人员,立即开始问题分析处理过程
不严重:和其他要求的软件开发资源任务一起统筹安排
对于适应性和改善性维护的要求沿着相同的事件流通路进行
维护工作主要包括修改软件设计、复查、必要的代码修改、单元测试和集成测试、验收测试和复审
复查试图回答下述问题
在当前处境下设计、编码、测试的哪些方面能用不同方法进行
哪些维护资源是应该有而事实上还没有
什么是主要障碍
要求有预防性维护吗
保存维护记录
评价维护活动
第三节 软件可维护性
软件可维护性定义
维护人员理解、改正、改进这个软件的难易程度
决定可维护性的因素
可理解性:用户理解软件的结构、功能、接口等的难易程度
可测试性:容易理解的程度、测试工具、程序复杂度
可修改性:软件容易修改的程度
可移植性:把程序从一种计算环境转移到另一种计算环境的难易程度
可重用性:对同一事物不加或者稍作修改就可以多次重复使用
文档
文档是软件可维护性的决定性因素
用户文档:描述系统功能和使用方法,不关心如何实现
功能描述
安装文档
安装手册
参考手册
操作员指南
系统文档:描述系统的设计、实现和测试等方面的内容
可维护性复审
在开发过程的每一个阶段都应该把减少今后的维护工作量作为努力的目标,不仅在开发时期要注意尽量提高软件的可维护性,在维护时期更要注意保持可维护性
在每个阶段结束的技术审查和管理复查中,应该着重对可维护性进行复审
第四节 软件再工程
软件再工程:如何修改老程序以适应新需求
反复多次做修改程序的尝试
通过仔细分析程序,尽可能多的掌握内部细节,再修改
再深入理解原有基础上,采用软件工程方法重新设计、编码和测试那些需要变更的部分
以软件工程方法学为指导,对程序重新设计、编码和测试,为此可以使用CASE工具(逆向工程和在工程工具)来帮助理解原有的设计
软件再工程的过程模型
软件再工程是一个循环模型,包括库存目录分析、文档重构、逆向工程、代码重构、数据重构和正向工程6类活动
第八章 面向对象方法学概述
导学目标
了解面向对象方法学和传统方法学的区别以及思想
掌握面向对象的概念,如对象、实例、消息等
了解面向对象建模的目的、基本原则以及三种模型
掌握对象模型之间类图的基本符号和关系
掌握动态模型中的相关基础知识
掌握功能模型用例图的基本知识以及用例建模的主要工作
了解面向对象中三种模型相互之间的关系
第一节 面向对象方法学概述
两种方法学
传统软件工程方法学适用于中小型软件产品开发
面向对象软件工程方法学适用于大型软件产品开发,面向对象技术已成为当前最好的开发技术
8.1.1 面向对象方法学要点
1、面向对象方法学要点
面向对象方法学的出发点和原则是尽可能模拟人类的思维方式,使开发软件的方法与过程尽可能的接近人类认识世界,解决问题的方法与过程
客观世界中的实体既有静态的属性,又有动态的行为
与传统方法相反,面向对象是一种以数据或信息为主线,把数据和处理相结合的方法
面向对象方法把对象作为由数据及可以施加在这些数据上的操作所构成的统一体
面向对象方法是一种新的思维方法,把程序看做是相互协作又彼此独立的对象集合
2、面向对象有4个要点
认为客观世界是由各种对象组成的,任何事物都是对象,复杂对象由简单对象以某种方案组合而成
一切皆对象
把所有对象都划分成各种对象类,每个对象类都定义了一组数据和一组方法
按照子类与父类的关系,把若干个对象组合成一个层次结构的系统
继承关系
对象彼此之间仅能通过传递消息互相联系
封装
总结:面向对象的方法学方程式
OO=对象+类+继承+传递消息实现通信
8.1.2 面向对象方法学的优点
与人类习惯的思维方式一致
传统的面向过程的方法以算法为核心,将数据和过程独立,忽略了数据和操作之间的内在联系
面向对象的软件技术以对象为核心,把描述事务静态属性的数据结构和表示事务动态行为操作放在一起构成整体
例子:比如人有姓名、年龄属性,有吃饭、跑步行为
稳定性好
传统的软件开发方法以算法为核心,开发过程基于功能分析和功能分解,系统结构紧密依赖于功能需求。当功能需求发生变化时,将引起软件结构的整体修改,因此是不稳定的
面向对象的软件开发方法以对象为核心,不是基于功能分解的,当用户需求变化时,只需要做局部修改,因此是稳定的
可重用性好
传统的软件重用技术是利用标准函数库来构建新系统,但是标准函数缺乏必要的柔性,不能适应不同应用场合的不同需要。标准函数库必须运行在相应的数据结构上,如果要重用这样的模块,则相应的数据也必须重用
面向对象重用技术具有很强的自含性,将数据和操作平等对待。对象固有的封装性和信息隐藏机制,使其具有独立性
有两种方法可以实现可重复使用一个对象类
创建该类的实例
从该类派生出一个满足当前需要的新类
较易开发大型软件产品
用面向对象方法学开发软件时,构成软件系统的每个对象就像是微型程序,有自己的数据、操作、功能和用途
可以把一个大型软件产品分解成本质上相互独立的小产品来处理
可维护性好
面向对象软件的稳定性要好
面向对象的软件比较容易修改
面向对象的软件比较容易理解
易于测试和调试
第二节 面向对象的概念
8.2.1 对象
1、基本认识
对象可以是具体的物理实体的抽象(如汽车、狗),也可以是人为的概念(如上学、考试)或者是任何有明确边界和意义的东西
对象是对问题域中某个实体的抽象
客观世界中的实体既有静态属性,又有动态行为。因此面向对象方法学中的对象是由描述该对象属性的数据以及可以对这些数据施加的所有操作封装在一起构成的统一体【对象 = 静态属性 + 动态操作】
通常把对象中的操作叫做服务或者方法
对象与外界的界面也就是该对象向公众开放的操作
使用对象时,只需知道它向外界提供的接口形式而无须知道它的内部实现算法
2、对象的形象表示
3、定义
定义1:对象是具有相同状态的一组操作的集合
定义2:对象是对问题域中某个东西的抽象,也就是对象是属性值和操作的封装
定义3:对象::= <ID,MS,DS,MI>,形式化定义
ID:对象的标识或名字 MS:对象中的操作的集合
DS:对象的数据结构 MI:对象受理的消息名集合
即:对象 = 对象名 + 操作 + 数据结构 + 消息
总之,对象中的数据表示对象的状态,一个对象的状态只能该对象的操作来改变
4、特点
以数据为中心
对象是主动的
实现了数据封装
本质上具有并行性
模块独立性好
8.2.2 类
类是对相同属性和行为的一个或者多个对象的描述
示例:圆类
具有相同的属性:圆心坐标、半径、颜色等
具有相同的操作:显示自己、放大缩小半径等
8.2.3 实例
实例是由某个特定类所描述的一个具体的对象,
示例:狗就是动物类的一个实例
8.2.4 消息
消息就是要求某个对象执行在定义它的那个类中所定义的某个操作的规格说明
消息由三部分组成
接受消息的对象
消息名
0个或多个变元
示例:MyCircle.Show(GREEN)
MyCircle: 接受消息的对象的名字
Show: 消息名
GREEN: 消息的变元
8.2.5 方法
对象所能够执行的操作
示例:MyCircle.Show(GREEN)
8.2.6 属性
就是类中所定义的数据,它是对客观世界实体所具有性质的抽象
示例:Circle类定义的圆心坐标、半径、颜色等
8.2.7 封装
封装就是信息隐蔽,在面向对象的程序中,把数据和实现操作的代码集中起来放在对象内部,外部是不可以直接访问和修改数据的
对象具有封装性的条件
有一个清晰的边界
有确定的接口
受保护的内部实现
8.2.8 继承
从广义上讲,继承是指能够直接获得已有的性质和特征,而不必重复定义他们
在面向对象的技术中,继承是子类自动的共享基类中定义的数据和方法的机制
继承具有传递性
8.2.9 多态性
在面向对象的软件技术中,多态性是指子类对象可以像父类对象那样使用,同样的消息既可以发送给父类对象,也可以发送给子类对象
8.2.10 重载
函数重载:在同一个作用域内的若干个参数特征不同函数可以使用相同的函数名
运算符重载:同一个运算符可以施加在不同类型的操作数上面
第三节 面向对象建模
1、为什么要建模?
要解决问题之前首先要理解问题,建模就是为了帮助我们更好的去理解问题
2、什么是模型?
模型是由一组图示符号和组成这些符号的规则组成,利用他们来定义和描述问题域中的术语和概念。
3、建模的目的
主要是为了减少复杂性
模型提供了组织大量信息的一种有效机制
面向对象方法最基本的原则是按照人类习惯的方式,用面向对象的观点建立问题域的模型,开发出尽可能自然地表现求解方法的软件
4、用面向对象技术开发软件时,需要建立三种模型
对象模型:描述系统的数据结构
动态模型:描述系统的控制结构
功能模型:描述系统的功能
对任何大系统来说,上述三种模型都是必不可少的,但是在不同的应用问题中,三种模型的重要程度可能会有所不同
用面向对象的方法开发软件时,在任何情况下,对象模型始终是最基本、最重要、最核心的
第四节 对象模型
8.4.1 类图的基本符号
1、概念
对象模型表示静态的、结构化的系统的数据性质。它是模拟客观世界实体的对象以及对象之间的映射关系,描述了系统的静态结构
对象模型为建立动态模型和功能模型提供了实质性的框架
Booch、Rumbaugh和Jacobson于1996年设计出统一建模语言UML0.9
1997年11月,国际对象管理组织OMG批准把UML1.1作为基于面向对象的标准设计语言
通常使用UML提供的类图来建立对象模型
2、类图的基本符号
类图描述类以及类与类之间的静态关系
类图是一种静态模型,是创建其他UML图的基础
一个系统可以由多张类图来描述
一个类可以出现在多张类图中
3、定义类
UML中类的表示
类名应该遵循的准则
使用标准术语
使用具有确切含义的名词
必要时使用名词短语作名字
总之,类名应该是富于描述性、简洁、而且无二义性的
4、定义属性
UML属性的描述格式
可见性 属性名:类型= 初值{性质串}
属性的可见性及表示
公有的(public):
+
私有的(private):
-
保护的(protected):
#
注意:没有默认可见性
属性名和类型之间用
:
分隔类型和初值之间用
=
隔开{ }
括起来的性质串明确列出该属性的所有取值示例:
- 成绩:float = 100
5、定义服务
UML操作的描述格式
可见性 操作名(参数表):返回值类型{性质串}
参数表是用逗号分隔的形式参数的序列,语法如下:
参数名:类型= 参数值
8.4.2 表示关系的符号
类与类之间通常有关联、泛化(继承)、依赖和细化4种关系
1、关联
表示两个类的对象之间存在某种语义上的联系
(1)普通关联
最常见的关联关系,只要在类与类中存在连接关系就可以使用
普通关联的符号是连接两个类之间的直线
通常关联是双向的
重数的表示方法:(默认重数是1)
0 .. 1 表示0到1个对象 0 .. *或* 表示0到多个对象 1+或1 .. * 表示1到多个对象 1 .. 15 表示1到15个对象 3 表示3个对象
(2)关联的角色
在任何关联中都会涉及参与此关联的对象所扮演的角色
示例:
(3)限定关联
通常用于一对多或者多对多的关联关系中,将其转化为一对一或者多对一
示例:
(4)关联类
为了说明关联的性质,可能需要一些附加信息,可以引入关联类来记录这些信息
关联类通过一条虚线与关联连接
关联类与一般类一样,具有属性、操作和关联
示例:
2、聚集
是关联的特例;分为两种
(1)聚合
类与类之间是“has a”的关系,整体与部分关系,较弱
菱形端代表整体事物类,代表部分事物类可属于整体事物类
聚合关系中代表部分事物对象与代表聚合事物对象生存期无关,删除聚合对象不一定删除代表部分事物对象。
(聚合中的整体和部分是可以分割的)
(2)组合
是“contains-a”的关系,整体与部分关系较强,部分完全隶属于整体
组合中删除组合对象,同时也就删除代表部分事物对象
(组合关系系中中的整体和部分是不可分割的,是共存亡)
3、泛化
就是指的继承关系,也就是类间“一般到特殊”的关系
示例:
4、依赖
描述两个模型元素(类、用例等)之间的语义连接关系
独立模型元素的变化必然会影响依赖它的模型元素
在UML的类图中,用带箭头的虚线连接有依赖关系的两个类,箭头指向独立的类
示例:
5、细化
对同一事物在不同抽象层次上描述时,这些描述关系为细化关系
假设两个模型元素A,B描述同一事物,如果B是在A的基础上更详细的描述,则称B细化了A
虚线箭头指向被细化的类
示例:
第五节 动态模型
一旦对象模型建立好了以后,就需要考察对象的动态行为
所有对象都具有自己的生命周期
各个对象相互间触发就形成了一系列的状态变化
用UML提供的状态图来描绘对象的状态、触发状态转换的事件以及对象的行为(对事件的响应)
状态转换图
3.6.1 处
回顾
表示一个对象(或模型元素)生存史,显示触发状态转移的事件和因状态改变导致的动作
三种状态
初态
终态
中间状态
活动
语法格式: 事件名/动作表达式
三种事件:
entry事件:入口活动
do事件:内部执行的活动
exit事件:出口活动
动作表达式:描述应该做的具体动作
状态转换
语法格式:事件说明[守卫条件]/动作表达式
事件说明的语法:事件名(参数表)
守卫条件是一个布尔表达式
如果同时使用事件说明和守卫条件,则当且仅当事件发生且布尔表达式为真时,状态转换才发生;如果只有守卫条件而没有事件说明,只要守卫条件发生,则状态转换就发生
动作表达式是一个过程表达式,当状态转换开始时执行该表达式
示例:after事件名,3min参数表,turn off 动作表达式
第六节 功能模型
功能模型指明了系统应该做什么,更直接的反映了用户对目标系统的需求
通常,功能模型由一组数据流图组成
建立功能模型有助于软件开发人员更深入的理解问题域,改进和完善自己的设计
UML提供的用例图是进行需求分析和建立功能模型的强有力工具,在UML 中把用用例图建立起来的系统模型称为用例模型
1、用例图
一幅用例图包含的模型元素有系统、行为者、用例及用例之间的关系
用例图中的元素组成
方框代表系统
椭圆代表用例
线条人代表行为者
连线代表关系
系统
是一个提供用例的黑盒子,内部如何工作,用例如何实现,对于建立用户模型来说都是不重要的
边线表示系统的边界,用于划定系统的功能范围
用例
一个用例是可以被行为者感受到的、系统的一个完整的功能
用例具有下述特征:
用例代表某些用户可见的功能,实现一个具体的用户目标
用例总是被行为者启动的,并向行为者提供可识别的值
用例必须是完整的
用例是一个类,它代表一类功能,而不是使用该功能的一个具体实例
用例的实例是系统的一种实际使用方法,通常称之为脚本
行为者
行为者是指与系统交付的人或者其他系统,它代表外部实体
行为者代表的是一种角色,而不是具体的人或物
在用例图中,用直线连接行为者和用例,表示两者之间交换消息,叫通信联系
用例之间的关系
主要有扩展和使用两种关系,它们是泛化关系的两种不同形式
扩展关系:
向一个用例中添加一些动作后构成了另一个用例,这两个用例之间的关系就叫做扩展关系
使用关系:
当一个用例使用另一个用例时,两个用例之间就构成了使用关系
使用与扩展的异同
描述一般行为变化时使用扩展关系;在两个或者多个用例中重复描述又想避免这种重复,可以选择使用关系
2、用例建模
几乎在任何情况下都需要使用用例,通过用例可以获取用户需求,规划和控制项目
一个用例模型由若干幅用例图组成。创建用例模型的工作包括:
定义系统
寻找行为者和用例(关键工作)
描述用例
定义用例之间的关系
确定模型
寻找行为者:可以通过请用户回答问题的方法
谁将使用系统的主要功能?
谁需要借助系统的支持来完成日常工作?
谁来维护和管理系统?
系统控制哪些硬件设备?
系统需要与哪些其他系统交互?
哪些人或系统对本系统产生的结果感兴趣?
寻找用例:找到了行为者,可以通过行为者回答问题来获取用例
行为者需要系统提供哪些功能?行为者自身需要做什么?
行为者是否需要读取、创建、删除、修改或者存储系统中的某类信息
系统中发生的事件需要通知行为者吗?行为者需要通知系统某些事件吗?从功能观点看,这些事件能做什么?
行为者的日常工作是否因为系统的新功能而被简化或提高了效率?
系统需要哪些输入输出?输入来自何处?输出到哪里?
当前使用的系统存在的主要问题是什么?
第七节 三种模型之间的关系
1、三种模型相互补充、相互配合
功能模型:指明了系统应该做什么
动态模型:明确规定了什么时候做
对象模型:定义了做事情的实体
2、关系
针对每个类建立的动态模型,描述了类实例的生命周期或运行周期
状态转换驱使行为发生,这些行为在数据流图中被映射为处理,在用例图中被映射为用例,它们同时与类图中的服务相对应
功能模型中的处理(用例)对应于对象模型中的类所提供的服务
数据流图中的数据存储以及数据的源点/终点,通常是对象模型中的对象
数据流图中的数据流往往是对象模型中的属性值,也可能是整个对象
用例图中的行为者,可能是对象模型中的对象
功能模型中的处理(用例)可能产生动态模型中的事件
对象模型描述数据流图中的数据流、数据存储以及数据源点/终点的结构
第九章 面向对象分析
导学目标
了解面向对象分析的目标、关键要素及过程
掌握建立对象模型、动态模型、功能模型的步骤
了解服务的相关知识
第一节 面向对象分析概述
1、无论我们采用何种方式开发软件,分析的过程都是提取需求的过程。分析工作主要包括3项内容,理解、表达、验证。
理解:系统分析员通过与用户及领域专家交谈,力求完全理解用户需求和该应用领域内关键性的背景知识
表达:用某种无二义性的方式把这种理解表达成文档资料
验证:由于问题复杂,交流过程中可能出现偏差,理解可能达不到预期的效果,因此我们需要去验证文档资料的正确性,若有问题,需要重新进行修改
2、面向对象分析(OOA)的关键
识别问题域内的类与对象,并分析它们之间的关系,建立起针对于问题域的正确模型
3、面向对象分析(OOA)的定义
抽取和整理用户需求并建立问题域精确模型的过程
4、面向对象分析(OOA)的过程
获取用户需求
建立模型(需要向领域专家学习并自信研究类似的问题域)
书写需求规格说明书
复审
5、面向对象建模中的三个要素
对象模型:几乎在解决任何一个问题
动态模型:当问题设计到交互和时序时
功能模型:解决运算量很大的问题
6、复杂问题的对象模型:(5个层次)
主题层
类与对象层
结构层
属性层
服务层
7、需求陈述的内容包括问题的范围、功能需求、性能需求、应用环境及假设条件等。需求陈述说明的是做什么,而不是怎样去做
8、书写需求陈述的要点
要尽力做到语法准确
不要把实际需求和设计决策混为一谈
需求陈述可繁可简
需求陈述要具体无二义性、完整性和一致性
需求陈述案例
银行ATM取款机
1、需求陈述
某银行拟开发一个自动取款机系统,它是一个由自动取款机、中央计算机、分行计算机及柜员终端组成的网络系统。ATM和中央计算机由总行投资购买【ATM系统的组成】
总行拥有多台ATM,分别设在全市各主要街道上。分行负责提供分行计算机和柜员终端。柜员终端设在分行营业厅及分行下属的各个储蓄所内。该系统的软件开发成本由各个分行分摊【总行ATM以及分行ATM的功能和应用领域】
银行柜员使用柜员终端处理储户提交的储蓄事务。储户可以用现金或支票向自己拥有的某个账户内存款或开新账户。储户也可以从自己的账户中取款。通常,一个储户可能拥有多个账户【储户和ATM系统和账户之间的关系】
柜员负责把储户提交的存款或取款事务输进柜员终端,接收储户交来的现金或支票,或付给储户现金。柜员终端与相应的分行计算机通信,分行计算机具体处理针对某个账户的事务并且维护账户【储户、柜员、分行计算机、账户之间的关系】
拥有银行账户的储户有权申请领取现金兑换卡。使用现金兑换卡可以通过ATM访问自己的账户。目前仅限于用现金兑换卡在ATM上提取现金(即取款),或查询有关自己账户的信息(例如,某个指定账户上的余额)。将来可能还要求使用ATM办理转账、存款等事务
所谓现金兑换卡就是一张特制的磁卡,上面有分行代码和卡号。分行代码唯一标识总行下属的一个分行,卡号确定了这张卡可以访问哪些账户。通常,一张卡可以访问储户的若干个账户,但是不一定能访问这个储户的全部账户
每张现金兑换卡仅属于一个储户所有,但是,同一张卡可能有多个副本,因此,必须考虑同时在若干台ATM上使用同样的现金兑换卡的可能性。也就是说,系统应该能够处理并发的访问【系统要求:并发访问】
当用户把现金兑换卡插入ATM之后,ATM就与用户交互,以获取有关这次事务的信息,并与中央计算机交换关于事务的信息
首先,ATM要求用户输入密码,接下来ATM把从这张卡上读到的信息以及用户输入的密码传给中央计算机,请求中央计算机核对这些信息并处理这次事务
中央计算机根据卡上的分行代码确定这次事务与分行的对应关系,并且委托相应的分行计算机验证用户密码
如果用户输入的密码是正确的,ATM就要求用户选择事务类型(取款、查询等)。当用户选择取款时,ATM请求用户输入取款额。最后,ATM从现金出口吐出现金,并且打印出账单交给用户
第二节 建立对象模型
面向对象分析的首要工作,就是建立问题域的对象模型
对象模型描述了类与对象以及它们之间的相互关系,表示了目标系统的静态数据结构
静态数据结构对应用细节依赖较少,比较容易确定;当用户需求发生变化时,静态数据结构相对比较稳定。因此,用面向对象方法开发出绝大多数软件时,都首先建立对象模型,然后再建立其他两个子模型
1、建立对象模型的步骤
确定分析类
确定类的关联
划分主题
确定属性
识别继承
反复修改
9.2.1 确定类与对象
1、找出候选的类与对象
边界类
通常一参与者与一用例之间交互或通信关联对应一边界类
示例:ATM的系统用例图
边界类 说明 SetupForm 开新账户的操作界面 DepositForm 存款的操作界面 ATMWithdrawForm ATM用户取款的操作界面 ATMBalanceForm ATM查询余额的操作界面 ATMPasswordForm ATM改变密码的操作界面 TellerWithdrawForm Teller用户取款的操作界面 TellerBalanceForm Teller查询余额的操作界面 TellerPasswordForm Teller改变密码的操作界面
控制类
控制类负责协调边界类和实体类,通常在现实世界没有对应的事物。一般来说,一个用例对应一个控制类
示例
控制类 说明 SetupControl 负责执行开新账户 DepositControl 负责执行存款 WithdrawControl 负责执行取款 BalanceControl 负责执行查询余额 PasswordControl 负责执行改变密码
实体类
实体类通常是用例中的参与对象,对应着现实世界中“事物”
非正式分析法:提取需求陈述中的名词
示例:用非正式分析法提取ATM系统中的实体类
银行,自动取款机(ATM),系统,中央计算机,分行计算机,柜员终端,网络,总行,分行,软件,成本,市,街道,营业厅,储蓄所,柜员,储户,现金,支票,账户,事务,现金兑换卡,余额,磁卡,分行代码,卡号,用户,副本,信息,密码,类型,取款额,账单,访问
ATM系统分析员根据领域知识或常识提取出隐含的类。如通信链路、事务日志等
2、筛选出正确的类与对象
显然根据非正式分析方法只能确定候选的类与对象,还需要通过严格的标准去筛选出正确的,去掉不正确的
筛选的标准
冗余
如果两个类表达了同样的信息,应该保留此问题域中最富于描述力的名称。
示例:储户与用户,现金兑换卡与磁卡及副本应去掉“用户”、“磁卡”、“副本”,保留“储户”和“现金兑换卡”
无关
如果类与当前要解决的问题无关,需要删除与本问题密切相关类放进目标系统,去掉“成本”、“市”、“街道”、“营业厅”、“储蓄所”
笼统——直接去掉
示例:银行(总行和分行)、系统、软件、信息、访问(事务)
属性——直接去掉
示例:现金、支票、取款额、账单、余额、分行代码、卡号、密码和类型
操作
需求陈述中既作名词又作动词的词,慎重考虑是作类合适,还是作类中操作合适
实现—— 直接去掉
事务日志、通信链路
经过初步筛选还剩下的类
ATM系统筛选后的类:
银行,自动取款机(ATM),系统,中央计算机,分行计算机,柜员,终端,网络,总行,分行,软件,成本,市,街道,营业厅,储蓄所,柜员,储户,现金,支票,账户,事务,现金兑换卡,余额,磁卡,分行代码,卡号,用户,副本,信息,密码,类型,取款额,账单,访问确定类与对象
ATM系统的实体分析类
9.2.2 确定关联
1、初步确定关联
步骤
提取需求陈述中的动词词组
发现隐含关联
与用户及领域专家讨论后补充
示例:ATM系统
直接提取动词短语得出的关联
ATM、中央计算机、分行计算机及柜员终端组成网络
总行拥有多台ATM
ATM设在主要街道上
分行提供分行计算机和柜员终端
柜员终端设在分行营业厅及储蓄所内
分行分摊软件开发成本
储户拥有账户
分行计算机处理针对账户的事务
分行计算机维护账户
柜员终端与分行计算机通信
柜员输入针对账户的事务
ATM与中央计算机交换关于事务的信息
中央计算机确定事务与分行的对应关系
ATM读现金兑换卡
ATM与用户交互
ATM吐出现金
ATM打印账单
系统处理并发的访问
需求陈述中隐含的关联
总行由各分行组成
分行保管账户
总行拥有中央计算机
系统维护事务日志
系统提供必要安全性
储户拥有现金兑换卡
根据问题域知识得出的关联
现金兑换卡访问账户
分行雇用柜员
2、筛选
初步分析得出的关联只能作为候选关联,还需要做进一步的筛选
筛选的标准
已删去的类之间的关联
删掉某候选类,与这个类有关的关联也删去,或重新表达
已删去“系统”、“网络”、“市”、“街道”、“成本”、“软件”、“事务日志”、“现金”、“营业厅”、“储蓄所”、“账单”候选类,关联也应删去:
ATM、中央计算机、分行计算机及柜员终端组成网络
ATM设在主要街道上
分行分摊软件开发成本
系统提供必要安全性
系统维护事务日志
ATM吐出现金
ATM打印账单
柜员终端设在分行营业厅及储蓄所内
与问题无关的或应在实现阶段考虑的关联应该删去
如ATM系统的例子中,“系统处理并发的访问”需要删去.
瞬时事件
关联应该描述问题域的静态结构,而不应该是一个瞬时事件
以ATM系统为例,“ATM读现金兑换卡”、“ATM与用户交互”、“中央计算机确定事务与分行对应关系”隐含“中央计算机与分行通信”
三元或者三元以上的关联
三个或三个以上对象关联,可分解为二元关联或限定关联
在ATM系统例子中,“柜员输入针对账户的事务”分解成“柜员输入事务”和“事务修改账户”
“ATM与中央计算机交换关于事务的信息”隐含“ATM与中央计算机通信”和“在ATM上输入事务”这两个二元关联
派生关联
可以去掉那些可以使用其他关联定义的冗余关联
例如在ATM例子中,“总行拥有多台ATM”实质上是“总行拥有中央处理机”和“ATM与中央计算机通信”
3、进一步完善
正名
应该选择含义更明确的名字作为关联名
例如:"分行提供分行计算机和柜员终端"改为"分行拥有分行计算机"和"分行拥有柜员终端"
分解
把“事务”分解成“远程事务”和“柜员事务”
补充
需补充"柜员输入柜员事务"、"柜员事务输进柜员终端"、"在ATM上输入远程事务"和"远程事务由现金兑换卡授权"
标明重数
ATM原始系统的原始类图
9.2.3 划分主题
为了降低复杂程度,可以把系统划分成不同主题,确定主题时我们应该按照问题领域而不是功能分解来进行划分。原则是使不同主题内的对象相互间依赖和交互最少
9.2.4 确定属性
1、选择
误把对象当属性
如果某个实体的独立存在比它的值更重要,则应该把它作为一个对象而不是对象的属性
误把关联类的属性当做一般对象的属性
把限定误当成属性
误把内部状态当成了属性
过于细化
存在不一致的属性
加上属性的实体类图
9.2.5 识别继承关系
一般说来,可以有两种方式建立继承关系
自底向上:抽象出现有类的共同性质泛化出父类
自顶向下:把现有类细化成子类
识别出继承关系的实体类图
9.2.6 反复修改
下面以ATM为例,讨论可能做的修改
分解“现金兑换卡类”
现金兑换卡有两独立功能:标志储户访问账号的权限;含有分行代码和卡号的数据载体。(卡权限和现金兑换卡)
“事务”由“更新”组成
更新包括取款、存款、查询。有自己属性(类型、金额),应独立存在
合并“分行”和“分行计算机”
类似的应该合并“总行”与“中央计算机”
修改后的实体类图
第三节 建立动态模型
1、应用场景
开发交互式系统时,动态模型十分重要
2、建立动态模型的步骤
编写典型交互行为脚本
从脚本中提取事件及相关对象,用顺序图表达
确定对象状态及状态间转换关系,用状态图描绘
比较各个对象的状态图,确保事件之间的匹配性
9.3.1 编写脚本
1、定义
系统在某一执行期内出现的一系列事件
2、目的
保证不遗漏重要的交互步骤
3、范围
并不固定,主要由编写脚本的目的决定
4、顺序
首先编写正常情况下的脚本
考虑特殊情况
考虑出错情况
5、编写脚本示例:以ATM为例
正常情况
ATM请储户插卡;储户插入一张现金兑换卡。
ATM接受该卡并读它上面的分行代码和卡号。
ATM要求储户输入密码;储户输入自己密码“1234”等数字。
ATM请求总行验证卡号和密码;总行要求“39”号分行核对
储户密码,通知ATM这张卡有效。
ATM要求储户选择事务类型(取款、转账、查询等);储户选择“取款”。
ATM要求储户输入取款额;储户输人“880”
异常情况
ATM请储户插卡;储户插入一张现金兑换卡。
ATM接受该卡并读它上面的分行代码和卡号。
ATM要求储户输入密码;储户误输入“8888”。
ATM请求总行验证;总行向有关银行咨询后拒绝这张卡。
ATM显示“密码错”,请用户重新输入密码;储户输入1234;
ATM请总行验证后知道输入的密码正确
ATM请储户选择事务类型;储户选择“取款”
ATM询问用户取款额;储户不想取款,敲“取消”键
ATM退出现金兑换卡,请储户拿走;储户拿走
ATM请储户插卡
9.3.2 画顺序图
从脚本提取所有外部事件,确定每类事件发送和接收对象
针对系统中的典型功能,画出顺序图
ATM用户取款顺序图示例:
9.3.3 画状态图
用一张状态图描绘类的行为,集中考虑具有交互行为类
根据一张顺序图画出状态图之后,再把其他脚本的顺序图并进来
ATM类的状态图
总行类的状态图
分行类的状态图
第四节 建立功能模型
功能模型表明了数据之间的依赖关系以及有关的数据处理功能,它由一组数据流图组成
1、建立功能模型的步骤
画出基本系统模型图
画出功能级数据流图
描述处理框功能
2、画出基本系统模型图
基本系统模型由若干个数据源点和终点,及一个处理框组成,这个处理框代表了系统加工、变换数据的整体功能。基本模型指明了目标系统的边界
示例:ATM系统的基本系统模型
3、画出功能级数据流图
把基本模型中的单一处理框分解成若干个处理框,就可以得到功能级数据流图
示例:ATM系统的功能级数据流图
3、描述处理框功能
把数据流图细化分解到一定程度,就可以描述各个处理框功能了
描述可以是说明性的,也可以是过程性的
示例:ATM中更新账户这个处理功能的描述
更新账户(账号、事务类型、金额) -> 现金额、账单数据、信息
如果取款金额超过该账户当前金额,拒绝该事务且不付出现金
如果取款额不超过账户当前余额,从余额中减去取款额作为新的余额,付出储户要取的现金
如果事务是存款,把存款额追加到余额中得到新余额,不付出现金
如果事务是查询,不付出现金
在上述任何一种情况下,账单内容都是:ATM号,日期,时间,账号,事务类型,事务金额,新余额
第五节 定义服务
常规行为
定义类中每个属性都是可以访问的
从事件导出操作
与数据流图中处理框对应的操作
利用继承减少冗余操作
应该尽量利用继承机制减少所需定义的服务数目
第十章 面向对象设计
导学目标
了解面向对象设计的目的、设计准则以及启发式规则
掌握软件重用的三个层次、可重用的成分以及重用的好处
了解系统分解中的4个部分以及两种交互方式
第一节 面向对象设计的准则及启发式规则
分析:提取整理用户的需求,建立问题域精确模型(OOA)
设计:转变需求为系统实现方案,建立求解域模型(OOD)
在实际的软件开发中,分析和设计的界限是模糊的
分析和设计的活动是一个反复迭代的过程
面向对象方法学在概念和表示方法上的一致性,保证了在各项开发活动之间的平滑(无缝)过渡,领域专家和开发人员能够比较容易地跟踪整个系统开发过程,这是面向对象方法与传统方法比较起来所具有的一大优势
1、面向对象设计准则
模块化
对象就是模块
抽象
类抽象、提高可重用性
信息隐蔽
通过封装性实现、提高模块独立性
弱耦合
在面向对象方法中,耦合是指不同对象之间相互关联的紧密程度
对象间耦合分为两大类:交互耦合和继承耦合
交互耦合:对象间通过消息连接实现(松散)
降低消息连接复杂度(减少参数个数,降低参数复杂度
减少信息数
继承耦合:一般类和特殊类之间耦合(紧密)
有继承关系基类和派生类是系统中粒度更大模块
强内聚
服务内聚:只完成一个功能
类内聚:一个类只有一个用途,否则分解
一般特殊内聚:设计合理,是对领域知识正确抽取
可重用性
尽量利用已有类(类库、已创建类)
创建新类考虑以后可重用性
2、启发式规则
设计结果清晰易懂
用词一致
使用已有的协议
较少消息模式的数目
避免模糊的定义
一般-特殊结构的深度要适当
应该是类层级中包含的层次数适当
设计简单的类
避免包含过多的属性
有明确的定义
简化对象之间的合作关系
不要提供太多服务
使用简单的协议
经验表明,通过复杂消息相互关联的对象是紧耦合的,对一个对象的修改往往导致其他对象的修改
使用简单的服务
本类中的操纵尽可能简单
把设计变动减至最小
第二节 软件重用
重用也叫再用或复用,是指同一事物不做修改或稍加改动就多次重复使用
1、软件重用的三个层次
知识重用
方法和标准的重用
软件成分的重用
2、软件成分重用的三个层次
代码重用
调用库中的模块
设计重用
重用某个软件系统的设计模型
分析重用
重用某个系统的分析模型
3、典型的可重用软件的成分
项目计划
成本估计
体系结构
需求模型和规格说明
设计
源代码
用户文档和技术文档
用户界面
数据
测试用例
3、类构件重用的三种方式
实例重用
使用者无须了解实现细节就可以使用适当的构造函数创建类的实例
继承重用
提供了一种安全地修改已有类构件
多态重用
降低了消息连接的复杂程度,提供了简便可靠的软构件机制
4、软件重用的好处
质量
每一次重用,软件质量都会有所改善
生产率
软件开发全过程时间较少,带来生产率提高
成本
软件重用可以减少净成本
第三节 系统分解
1、系统分解的定义
把系统分解成若干个比较小的部分,然后在分别设计每个部分
系统的主要组成部分是子系统,各个子系统之间应该具有尽可能简单、明确的接口,再划分和设计子系统时,应当尽量减少子系统之间的依赖
2、面向对象设计模型,逻辑上由4大部分组成
问题域
直接负责实现客户需求子系统
人机交互
实现用户界面子系统包括可复用的GUI子系统
任务管理
确定各类任务,把任务分配给适当的硬件或软件去执行
数据管理
负责对象的存储和检索的子系统
3、子系统间交互方式
客户-供应商关系
“客户”子系统了解“供应商”子系统接口,反之则不能
平等伙伴关系
各子系统都有可能调用其它子系统,或为其它子系统提供服务。交互方式复杂,各子系统需要了解彼此接口
第四节 设计问题域子系统
1、设计基础
面向对象分析所得出的问题域模型
2、设计任务
对问题域模型做相应的补充或者修改
3、补充或者修改的内容
调整需求
用户需求或外部环境变化
分析模型不完整、不准确
重用已有的类
根据问题解决的需要,把从类库或其他来源得到既存类增加到问题解决方案中去
重用已有类的典型过程:
标出候选类中对本问题无用的属性和服务
在重用类和问题域类之间添加泛化关系
标出问题域类从已有类继承来的属性和服务
修改与问题域类的关联
把问题域类组合到一起
设计者往往通过引入一个根类而把问题域类组合到一起
添加一般化类
某些特殊类要求一组类似的服务,应加入一般化的类,定义为所有特殊类共用的一组服务名,服务都是虚函数;在特殊类中定义其实现
调整继承的层次
在OOA阶段建立的对象模型中可能包括多继承关系,但实现时使用程序设计语言可能只有单继承,需对分析结果修改
4、示例
ATM系统实例
ATM系统问题域子系统划分成三个更小的子系统,分别是ATM站子系统、中央计算机子系统、分行计算机子系统
星型拓扑结构
以专用电话线连接
第五节 设计人机交互子系统
1、设计基础
在面向对象分析过程中对用户需求做的初步分析
2、设计任务
确定人机交互细节,窗口报表形式,命令层次等
3、设计策略:
分类用户
通常可以按照技能水平、职位、所属集团进行分类
描述用户
应该将使用系统的每类用户情况详细的记录下来
设计命令的层次
研究现有的人机交互的含义和准则
确定初始的命令层次
精化命令层次
设计人机交互类
第六节 设计任务管理子系统
在实际系统中,许多对象之间往往存在相互依赖关系。设计工作的一项重要内容就是,确定哪些是必须同时动作的对象,哪些是相互排斥的对象。进一步设计任务管理子系统
系统总有许多并发行为,需按照各自行为的协调和通信关系,划分各种任务(进程),简化并发行为的设计和编码
确定各类任务,把任务分配给适当的硬件和软件去执行
根据动态模型去分析、定义
1、分析并发性
并发对象
无交互行为的对象
同时接受事件的对象
定义任务
检查各个对象的状态图,找没并发对象的路径(任何时候路径中只有单个对象是活跃的),称控制线
通过分离出控制线设计任务
并发任务分配
每个任务分配到独立的处理器
配到相同处理器,通过操作系统提供并发支持
2、设计任务管理子系统
确定事件驱动型任务
指睡眠任务(不占用cpu),某个事件发生,任务被触发,醒来做相应处理,又回到睡眠状态
确定时钟驱动型任务
按特定时间间隔去触发任务进行处理。如某些设备需要周期性获取数据
确定优先任务
高优先级任务分离出独立的任务,在规定时间内完成
确定关键任务
严格可靠性、精心设计和编码、严格测试
确定协调任务
三个以上任务,引入协调任务,控制封装任务间协调
尽量减少任务数
任务多,设计复杂、不易理解、难维护
确定资源需求
计算系统载荷,每秒处理业务数,处理一个业务花费时间,估算所需cpu (或其他固件)处理能力
综合考虑,确定哪些任务硬件实现,哪些任务软件实现
第七节 设计数据管理子系统
1、选择数据存储管理的模式
文件管理系统
成本低、简单
操作级别低,不同操作系统的文件系统差别大
关系数据库管理系统
优点:
提供了各种最基本的数据管理功能
为多种应用提供一致的接口
标准化的语言
缺点:
运行开销大
不能满足高级应用的需求
与程序设计语言连接不自然
面向对象数据库管理系统
扩展的关系型数据库:
增加抽象数据类型,继承等机制
增加了创建管理类和对象的通用服务
扩展的面向对象语言
扩充了面向对象程序设计语言的语法和功能
增加数据库存储和管理对象机制
2、设计数据管理子系统
设计数据格式
设计数据格式与数据存储管理模式密切相关
文件系统:达到第一范式、减少文件数量、减小存储空间
关系型数据库管理系统:达到第三范式、满足性能及存储需求
面向对象数据库管理系统:对于扩展性关系型数据库使用与关系型数据库相同的方法;对于扩展性面向对象程序设计语言,不需要规范化属性的步骤
设计相应的服务
文件系统:打开文件、记录定位、检索记录、更新
关系型数据库管理系统:访问那些数据库表、怎样访问所需要的行、怎样检索出旧值、怎样用现有值更新
面向对象数据库管理系统:对于扩展性关系型数据库使用与关系型数据库相同的方法;对于扩展性面向对象程序设计语言,不需要增加服务设计数据管理子系统
3、示例:ATM系统
永久性数据存储在分行计算机
保持数据一致性、完整性、满足并发性
用商品化关系数据库管理系统
每个事务不可分割,事务封锁账户
ATM系统中需存储对象主要是账户类对象,两种方法
每个对象自己保存自己
账户类对象接到“存储自己”通知,把自身存储起来
由数据管理子系统负责存储对象
账户类对象接到“存储自己”通知,向数据管理子系统发消息,由数据管理子系统将状态保存起来
第十一章 面向对象实现
导学目标
了解面向对象实现的任务、目标以及注意事项
了解面向对象语言的技术和特点
了解程序设计风格的准则
了解面向对象设计的测试策略
了解如何去设计面向对象测试用例
第一节 面向对象实现概述
1、面向对象实现的任务
将面向对象设计结果翻译成面向对象程序
测试并调试面向对象程序
2、面向对象测试的目标
用尽可能低的测试成本发现尽可能多的软件错误
3、注意事项
面向对象独有的封装、继承和多态机制增加了测试的难度
第二节 程序设计语言
面向对象设计的结果既可以支持面向对象语言,也可以用非面向对象语言实现
使用面向对象的语言时,由于语言本身的特点,编译程序可以自动地将面向对象的概念映射到目标程序中
使用非面向对象语言编写面向对象程序时,须由程序员自身把面向对象的概念映射到目标程序中
1、选择编程语言的关键因素
一致的表示方法
可重用性
可维护性
2、面向对象语言的技术特点
支持类与对象概念的机制
实现整体-部分结构的机制
实现一般-特殊结构的机制
实现属性和服务机制
类型检查
类库
效率
持久保存对象
参数化类
开发环境
3、面向对象语言的优点
将来能够占主导地位
可重用性
类库和开发环境
其他因素
第三节 程序设计风格
1、程序设计准则
提高重用性
提高方法的内聚
方法只完成单个功能。涉及多个不相关功能,分解
减小方法的规模
方法规模过大,分解
保持方法的一致性
功能相似方法有一致名字、参数特征(包括参数个数、类型和次序)、返回值类型、使用条件及出错条件等
把策略和实现分开
负责做出决策,提供变元,管理全局资源,称策略方法
负责完成具体操作,称实现方法
编程时不要把策略和实现放在同一个方法中
全面覆盖
应针对所有组合写方法
尽量不使用全局信息
可以降低方法和外界的耦合程度
利用继承机制
实现共享和提高重用程度的主要途径
调用子过程:把公共代码分离出来,构成一个公用方法
分解因子:
从不同类相似方法分解出不同的代码,余下作为公用方法中公共代码。把分解出的因子作为名字相同算法不同的方法,在不同类中定义
使用委托
把代码封装在类中:把被重用的代码封装在类中
提高可扩充性
封装实现策略
应把类的实现策略(包括数据结构、算法等)封装起来,对外提供公有接口
不要用一个方法关联多条关联链
一个方法应只包含对象模型中有限内容。否则导致方法过分复杂,不易理解和修改扩充
避免使用多分支语句
不要根据对象类型选择应有的行为,否则增添新类时不得不修改原有的代码,要合理的利用多态机制 精心确定公有方法
公有方法是向公众公布的接口
提高健壮性
预防用户的操作错误
任何输入(错误),必须接受检查,给出提示信息,再次接收用户输入
检查参数的合法性
不要预先确定限制条件
先测试后优化
第四节 测试策略
1、测试策略 单元测试——> 集成测试——> 验收测试 2、面向对象的单元测试
最小的可测试单元是对象与类
测试面向对象软件时,不能孤立地测试单个操作
传统的测试方法都可使用,等价类划分、边值分析、逻辑覆盖法、基本路径法
3、面向对象的集成测试
在面向对象的软件中不存在层次的控制结构,传统的自顶向下或自底向上的集成策略就没有意义了
此外,由于构成类的各个成分彼此间存在直接或间接的交互,一次集成一个操作到类中(传统的渐增式集成方法)通常是不现实的
面向对象软件的集成测试主要有下述两种不同的策略
基于线程的集成测试
把响应系统的一个输入或一个事件所需类集成起来
基于使用的集成测试
先测独立类,测完后测独立类下一层类(依赖类),到测完
4、面向对象确认测试
不再考虑类之间相互连接的细节,集中检查用户可见的动作和用户可识别的输出
传统黑盒测试方法也可以使用,但是主要还是根据动态模型和描述系统行为的脚本设计确认测试用例
第五节 测试用例的设计
与传统软件测试(测试用例的设计由软件的输入-> 处理-> 输出视图或单个模块的算法细节驱动)不同,面向对象测试关注于设计适当的操作序列以检查类的状态
1、测试类的方法
随机测试
在类的多个操作排列中,随机选择
示例:
在银行应用系统的account(账户)类操作:
open(打开)、setup、deposit(存款)、withdraw(取款)、balance(余额)、summarize(清单)、creditLimit(透支限额)和close(关闭)
一个ccount类实例的最小行为历史包括下列操作:
这就是对account类的最小测试序列
在下面的序列中可能发生许多其他行为:
随机产生不同的操作序列
执行上述这些及另外一些随机产生的测试用例,可以测试类实例的不同生存历史
划分测试
基于状态的划分
根据改变类状态能力划分:改变类状态、不改变类状态
以account类为例:
改变类状态:deposit、withdraw
不改变类状态:balance、summarize、creditLimit
设计测试用例,以分别测试改变状态的操作和不改变状态的操作
测试用例(最小测试序列除外)
基于属性的划分
根据类操作属性:使用该属性、修改属性、不操作该属性
示例:account类可根据balance属性把操作定义划分三个类别
使用balance的操作
修改balance的操作
不使用也不修改balance的操作
为每个类别设计测试序列
基于功能的划分
根据类操作完成的功能进行划分
示例:acount类
初始化操作(open、setup)
计算操作(deposit、withdraw)
查询操作(balance、summarize、creditLimit)
终止操作(close)
为每一个类别设计测试用例
基于故障的测试
与传统的错误推断法类似,首先推断出软件可能有的错误,然后设计出最可能发现这些错误的测试用例
很大程度上依赖程序员的直觉和经验,如果推断准确,效果较好;如果不准确,效果也会不好
2、集成测试的方法
多类测试
测类间协作,同样可采用随机测试和划分测试
随机测试
生成多类随机测试的步骤
对于每个客户类,使用类操作符列表来生成一系列随机测试用例,这些操作符向服务器类实例发送消息
对所生成的每个消息,确定协作类在服务器对象中的对应操作符
对服务器对象中的每个操作符,确定传递的消息
对每个消息,确定下一层被调用的操作符,并把这些操作符结合进测试序列中
示例:
Bank类对ATM类的操作序列
对Bank类的随机测试用例可能是
为了考虑测试涉及协作者,考虑与测试用例1每个操作相关联消息,Bank必须和ValidationInfo协作以执行verifyAcct和verifyPIN,Bank还必须和Account协作以执行deposit测试这些协作的新的测试用例
划分测试
根据特定类的接口来划分,如bank类的方法分服务于ATM或服务于cashier
从动态模型导出测试用例
测试用例涵盖所有状态。如下图,操作系列使account类实例遍历所有允许的状态转换
Account类的状态转换图
测试用例1:
open · setupAccount · deposit(initial) · withdraw(final) · close
测试用例2:
open · setupAccount · deposit(initial) · deposit · balance · credit · withdraw(final) · close
测试用例3:
open · setupAccount · deposit(initial) · deposit · withdraw · accntInfo · withdraw(final) · close
导出更多的测试用例以保证该类的所有行为都被适当地测试
在类的行为导致与一个或多个类协作的情况下,应该使用多个状态图去跟踪系统的行为流
第十二章 软件项目管理
导学目标
掌握软件规模估算的两种方法
掌握工作量估算的两种模型
掌握开发时间的估计以及甘特图
掌握三种人员组织结构
掌握质量保证的定义及保证措施
掌握软件配置管理项以及过程
第一节 软件规模估算
1、软件项目管理的概念
通过计划、组织、控制一系列活动,合理配置使用资源,达到既定目标的活动
2、软件项目管理过程
从一组项目计划活动开始,而制定计划的基础是工作量和完成期限估算;为了估算项目的工作量和完成期限,首先需要估算软件的规模
3、软件规模的估算 常用方法是代码行技术和功能点技术
4、代码行技术
依据开发类似产品的经验和历史数据,估计实现一个功能所需要的源代码行数,把实现每个功能所需要的源代码行数进行累加,就可以得到实现整个软件所需要的源代码行数
当程序较小时,我们常用的单位是代码行数(LOC)
当程序较大时,我们常用的单位是千行代码数(KLOC)
具体方法
找 n 名有经验的工程师估计
a:程序的最小规模
b:程序的最大规模
m:程序的最可能的规模
求三种规模的平均值
求程序的规模
优点:
代码是所有软件中都有的产品,很容易计算代码行数
缺点
源程序规模不等于软件规模,源程序只是软件配置的一个成分
用不同语言实现同一个软件的代码行数不同
不适用于非过程语言
5、功能点技术
定义
依据软件信息域特性和软件复杂性评估结果估算软件规模
信息域特性
输入项数:用户向软件输入的项数,这些输入给软件提供面向应用的数据
输出项数:软件向用户输出的项数,他们向用户提供面向应用的信息
查询数:即是一种联机输入,以输出方式产生某种即时响应
主文件数:每一个逻辑主文件都应计数
外部接口数:机器可读的全部接口的数量,用这些接口把信息传递给另一个系统
估算功能点的步骤
计算未调整功能点UFP
计算复杂度因子TCF
计算功能点数FP
FP=UFP×TCF
功能点数所用编程语言无关,看起来功能点技术比代码行技术更合理一些,但是在判断信息域特性复杂级别和技术因素的影响程度时,存在着相当大的主观因素
第二节 工作量估算
工作量是软件规模的函数,单位是人月
支持大多数估算模型的经验数据,都是从有限个项目的样本集中总结出来的,因此,没有一个估算模型可以适用于所有类型的软件和开发环境
1、静态单变量模型
面向KLOC的估算模型
面向FP的估算模型
从上面的模型中可以看出,相同的KLOC或FP值,用不同模型估算得出不同的结果
主要原因是:这些模型多数都是仅根据若干应用领域中有限个项目的经验数据推导出来的,适用范围有限。因此,必须根据当前项目的特点选择适用的估算模型,并且根据需要适当地调整(例如,修改模型常数)估算模型
2、动态多变量模型
工作量是软件规模和开发时间两个变量的函数。是根据从4000多个当代软件项目中收集的生产率数据推导出来的
t:以月或年为单位的项目持续时间;
B:特殊技术因子,随着需求增加缓慢增加。小程序0.16(5~10KLOC),大程序(超70KLOC)0.39。
P:生产率参数,反应过程管理、使用语言、系统的复杂程度等对工作量的影响实时嵌入软件2000;系统软件10000;商业系统28000等
第三节 进度计划
软件项目的进度安排是这样的一组活动:它通过把工作量分配给特定的软件工程任务并规定完成各项任务的起止时间,从而估算出项目的工作量分布于计划好的项目持续期内。进度计划将随着时间的流逝而不断演化
1、估算开发时间
工作量估算完,估算开发时间。如工作量为20人月项目,可能是下列几种进度表:
1个人用20个月
4个人用5个月
20个人用1个月等
产生问题:进度表不符合实际,软件开发的时间和从事开发的工作人数不是简单的反比关系
2、估算开发时间模型
3、甘特图(Gantt 图)
示例
矩形木板房需重新油漆。
三步:刮旧漆,刷新漆,清除溅在窗上油漆。
15 名工人,5把刮旧漆刮板,5 把刷漆刷子,5 把清除溅在窗上油漆小刮刀
图
第四节 人员组织
必须把多名开发人员合理的组织起来,使他们分工协作完成开发工作
3 种典型的组织方式
民主制程序员组
组内成员完全平等,通过协商作出技术决策
优点:积极性比较高、学习气氛比较浓郁
缺点:没有权威指导,缺乏必要的协调
适用领域:开发时间长,开发难度大的项目
通信链路多,组内成员要少而精
如果有n个成员,通信信道共n*(n-1)/2
主程序员组
组织形式
两个重要特征
专业化:每名成员完成受过专业训练的工作
层次化:主程序员有绝对权威
现代程序员组
主程序员由两个人担任:技术负责人;行政负责人。分工明确。明确划分技术负责人和行政负责人权限
现代程序员组结构
软件规模较大时,程序员组分成若干个小组
将民主制程序员组与主程序员组的优点结合进来,形成包含分散决策组织形式
第五节 质量保证
1、软件质量的定义
与软件产品满足规定的和隐含的需求能力有关的特征或特性全体
2、软件质量的三个要点
软件需求是度量软件质量的基础
按规范化标准定义开发准则,不遵守软件质量不能保证
不能忽略隐含需求
3、软件质量的影响因素
4、软件质量保证的措施
基于非执行的测试:复审或评审
基于执行的测试:软件测试
程序正确性证明
5、技术审查的必要性
保证编码前各阶段文档质量,及早纠正大部分缺陷
包括走查和审查
6、走查
是开发者的一次友好的会议,需要仔细规划,有明确的目的、日程、持续时间和参与人员,许多小组以星期为单位走查
会后将问题分发给相应人员进行解决
7、审查
最系统化严密的评审技术
审查范围比走查广泛、步骤较多
基本步骤
综述
准备
审查
返工
跟踪
审查组成员:
组长(同时是技术负责人);
负责开发工作的项目组代表(当前阶段和下一阶段)
SQA小组代表
8、程序正确性证明
用数学方法验证程序与说明一致。对评价小程序适用
第六节 配置管理
1、配置管理的定义
软件配置管理是软件的整个生命周期内管理变化的一组活动
软件配置管理不同于软件维护
软件配置的目标是使变化更正确且更容易被适应,在必须变化时减少所需花费的工作量
2、软件配置项
计算机程序:(源程序及目标程序)
文档:(包括技术文档和用户文档)
数据:(程序内包含的或在程序外的)
3、基线
IEEE定义:已经通过正式复审的规格说明或中间产品,可作为进一步开发基础,并且只有通过正式的变化控制才能改变它
简而言之,基线就是通过了正式复审的软件配置项
基线标志着软件开发过程的各个阶段的里程碑
4、软件配置管理过程
主要有5个任务:配置标识、版本管理、变更控制、配置审计和配置报告
配置标识
标识两类对象:基本对象和复合对象
基本对象:软件工程师分析、设计、编码和测试时建立“文本单元”。
如:需求规格说明一节,源程序清单、一组测试用例
复合对象:是基本对象或其它复合对象的集合
对象标识:(名字、描述、资源表、“实现”)
版本控制
版本控制是对配置对象不同版本标识和跟踪过程。保证软件技术的一致性
变化控制
变化控制是建立一套组织结构和控制规程,有意识地控制软件的变更过程变化控制的过程
配置审计
确保所有文档内容变动不超出当初确定软件要求范围
状态报告
对开发过程做系统记录,反映开发活动历史情况
主要回答发生了什么事?谁做的这件事?这件事是什么时候发生的?它将影响哪些其他事物?
第十三章 UML简介
导学目标
了解UML的基础知识
了解UML的视图和构成
掌握常见的有关面向对象的考题
第一节 UML概述
UML是一个通用的可视化建模语言,是用于对软件进行描述、可视化处理、构造和建立软件系统支配的文档
制品:软件开发过程中的各种产物,如模型、源代码、测试用例等
UML适用于各种软件开发方法、软件生命周期的各个阶段、各种应用领域以及各种开发工具
UML目的是为了支持面向对象开发过程而设计的
1、什么是UML
UML是一种语言,定义了一系列的图形符号来描述软件系统
图形符号有清晰的语义和严格的语法
图形符号及背后的语义语法组成了一个标准
UML描述了一个系统的静态结构(属性)和动态行为(服务、方法)
通过静态属性定义系统中的对象的属性和操作以及这些对象之间的相互关系
动态行为定义了对象的时间特性和对象为完成目标而相互进行通信的机制
UML标准没有定义一个标准的开发过程,它是为了支持面向对象开发过程而设计的
UML不是一门程序设计语言,但可以利用代码生成器工具将UML模型转换为多种程序设计语言代码
2、UML的特点
统一的标准(被OMG所认定的建模语言标准)
面向对象(UML是支持面向对象软件开发的建模语言)
可视化,表现能力强(图形符号)
独立开发过程,UML不依赖与特定的软件开发过程
概念明确,建模简洁,推行结构清晰,容易掌握和使用
第二节 UML视图
1、视图组成
设计视图:设计词汇、功能描述
实现视图:系统组装、配置管理
进程视图:性能、稳定性和吞吐率
部属视图:系统拓扑、分布、安装
注意:不同的视图突出特定的参与群体所关心的系统的不同方面,通过合并所有五个视图中得到的信息就可以形成系统的完整描述
2、用例视图(核心)
用于支持软件系统的需求分析、定义边界、关注功能
从系统参与者的角度去描述系统的外部行为和动态功能
用例视图的使用者是客户、开发人员及测试人员
用例图是核心,该视图定义了系统的需求,因此约束了描述系统设计和构造的某些方面的所有其他视图
通过用例图可以校验最终系统
3、设计视图
定义了系统的实现逻辑
设计视图的使用者主要是开发人员和设计者
它由程序组件的定义,主要是类、类所包含的数据、类的行为以及类之间交互的说明组成
它的图形模型包括:类图、对象图、状态图、顺序图、协作图及活动图
4、进程视图
描述系统的实现模块及它们之间的依赖关系
进程视图的使用者主要是开发人员
进程视图包括形成并发和同步机制的进程和线程
5、实现视图
描述构造系统的物理组件,这些组件包括如何执行文件、代码库和数据库等内容
实现视图的使用者主要是开发人员和系统集成人员
该视图由动态图(状态图、协作图、活动图)和实现图(组件图和部署图)组成
6、部署视图
描述物理组件如何在系统运行的实际环境(如计算机网路)中分布
部署视图的使用者是开发人员、系统集成人员和测试人员
该视图由部署图表示
第三节 UML构成
1、UML构成
2、UML的模型元素
UML把在图中使用的概念统称为模型元素
图形符号隐含表示了模型元素的语法和语义
模型元素描述了系统结构(静态特征)和行为(动态特征)
UML中定义了两类模型元素:
用于表示模型中的某个概念(类、对象、接口)
用于表示模型之间的相互关系(依赖、关联、泛化、实现)
示例图
3、UML模型规则
名字:任何一个UML成员都必须包含一个名字
作用域:UML成员所定义的内容起作用的上下文环境
可见性:UML成员被其他成员引用的方式
完整性:UML成员之间相互连接的合法性和一致性
运行属性:UML成员在运行时的特性
注意:一个完整的UML模型必须对上述内容给出完整的解释