软件工程基础

1.软件工程概述

1.1软件危机

指计算机软件开发、使用和维护过程所遇到的一系列问题。

1.1.1软件危机的典型表现

1.对软件开发成本和进度估计不准确。

2.用户对软件系统实际需求存在不满意

3.软件产品质量不过关

4.软件常常不具有维护性

5.软件缺乏适当的文档资料

6.软件成本逐年上升

7.软件开发速度跟不上计算机应用速度

1.1.2软件危机出现的原因

1.来自软件自身的特点:是逻辑部件,缺乏可见性;规模庞大、复杂,修改、维护困难。

2.软件开发与维护的方法不当:忽视需求分析;认为软件开发等于程序编写;轻视软件维护。

3.供求矛盾将是一个永恒的主题:面对日益增长的软件需求,开发人员显得力不从心。

1.2软件工程

软件工程是把系统的、规范的、可度量的途径应用于软件开发、运行和维护过程,也就是把工程应用于软件。

1.2.1软件工程的介绍

软件工程本质特性

1.软件工程关注于大型程序的构造(软件≠程序)

2.软件工程的中心课题是控制复杂性

3.软件经常变化

4.开发软件的效率非常重要

5.和谐地合作是开发软件的基础

6.软件必须有效地支持它的用户

7.在软件工程领域中通常是由具有一种文化背景的人替具有另一种文化背景的人创作产品

1.2.2软件工程的基本原则

1.用分阶段的生命周期计划严格管理

2.坚持进行阶段评审

3.实行严格的产品控制

4.采用现代程序设计技术

5.结果应能清楚地审查

6.开发小组的人员应该小而精

7.承认不断改进软件工程实践的必要性

1.2.3软件工程方法学

1.传统方法学

也称之为生命周期法或结构化规范,把软件生命周期飞卫若干个阶段,每个阶段的任务相对独立,而且比较简单,要么面向行为,要么面向数据,不存在既面向数据又面向行为的技术。

2.面向对象方法学

降低了软件产品的复杂性,提高软件可理解性,促进了软件的重用,当软件规模庞大,或者对软件需求模糊的或随时间变化而变化,传统方法学不适用。

1.3软件生命周期

由软件定义、软件开发、软件维护三个时期组成。

1.问题定义-解决什么问题?

2.可行性研究-问题有没有解决方案?

3.需求分析-产品需要有哪些功能?

4.总体设计5.详细设计6.编码和单元测试7.综合测试8.软件维护

1.4软件过程

软件过程是为了开发出高质量的软件产品所需完成的一系列任务框架,它规定了完成各项任务的工作步骤。

软件工程方法学:通常把在软件生命周期全过程中使用的一整套技术方法的集合作为方法学。

1.4.1瀑布模型

基本上是一种文档驱动的模型。

特点:

1.阶段间具有顺序性和依赖性

2.推迟实现的特点

3.质量保证的观点

优点:它提供了一个模板,分析、设计、编码、测试提供指导

缺点:1.实际的项目大部分情况难以按照该模型给出的严格顺序进行 2.这种模型不欢迎具有二义性问题的模糊需求 3.客户只有等到开发周期的晚期才能看到程序运行的测试版本

1.4.2快速原型模型

快速建立一个能反映用户主要需求的原型模型

优点:使用户能够感受到实际的系统,使开发者能够快速地构造出系统的框架

缺点:可能采用不合适的操作系统或程序语言,以使原型能够尽快工作

1.4.3增量模型

整个软件产品分为许多个增量构建

优点:1.人员分配灵活,初期不需要投入大量人力资源,当核心产品受到欢迎时,再增加人力实现下一个增量 2.当配备的人员不能在预期的期限内完成产品时,它提供了一种先推出核心产品的途径,就可以先发布部分功能给用户,提高用户信心

缺点:1.至始至终开发者和客户纠缠在一起,直到完全版本完成 2.适合于软件需求不明确、设计方案有一定风险的软件项目

1.4.4螺旋模型

风险驱动,适用于内部开发的大型软件项目

优点:对于大型系统及软件的开发,开发者和客户能够较好地理解和对待每一个演化级别上的风险

缺点:1.需要相当的风险分析评估的专门技术,且依赖于这种技术 2.很明显一个大的没有被发现的风险问题,将会导致问题的发生,可能导致演化的方法失去控制

1.4.5喷泉模型

体现了面向对象软件开发过程迭代和无缝的特性。

因为使用面向对象方法学开发软件时,各个阶段都使用统一的概念和表示符号。

1.4.6Rational统一过程(RUP)

软件开发经验

1.最佳实践

迭代式开发,管理需求,使用基于构件的体系结构,可视化建模,验证软件质量,控制软件变更

2.RUP软件开发生命周期

核心工作流:业务建模,需求,分析与设计,实现,测试,部署,配置与变更管理,项目管理,环境

工作阶段:初始阶段,精化阶段,构建阶段,移交阶段

3.RUP迭代式开发

优点:提高了团队的生产力,在迭代的开发过程、需求管理、基于组件的体系结构、可视化软件建模、验证软件质量及控制软件变更等方面,它建立了简洁和清晰的过程结构,未开发过程提供较大的通用性。

缺点:RUP只是一个开发过程,并没有涵盖软件过程的全部内容,例如它缺失关于软件运行和支持等方面的内容,并没有支持多项目的开发结构,一定程度上降低了在开发组织内大范围实现重用的可能性。

主要适用于:大型的需求不断变化的复杂软件系统项目

1.4.7敏捷过程与极限编程

1.敏捷过程

四个价值观:1.个体和交互胜过过程和工具 2.可以工作的软件胜过面面俱到的文档 3.客户合作胜过合同谈判 4.响应变化胜过遵循计划

2.极限编程

已经成为一种典型的开发方法,广泛应用于需求模糊且经常改变的场合

极限编程的有效实践:1.客户作为开发团队的成员 2.使用用户素材 3.短交付周期 4.验收测试 5.结对编程 6.测试驱动开发 7.集体所有 8.持续集成 9.可持续的开发速度 10.开放的工作空间 11.及时调整计划 12.简单的设计 13.重构 14.使用隐喻

适用范围:商业竞争环境下对小型项目提出的有限资源和有限开发时间的约束

1.4.8微软过程

适用范围:适用于商业环境下具有有限资源和有限开发时间约束的项目的软件过程模式

2.可行性研究

目的:用最小的代价在最短的时间内确定问题是否能够解决

Why?可行性分析是要进行一次大大压缩简化系统分析和设计过程,避免时间、资源、人力和金钱的浪费。

2.1可行性研究的任务

1.技术可行性 使用这个现有的技术能实现这个系统吗?

2.经济可行性 这个系统的经济效益能超过它的开发成本吗?

3.操作可行性 系统的这个操作方式在用户组织内行的通吗?

2.2可行性研究过程

1.复查系统规模和目标

2.研究目前正在使用的系统

3.导出新系统的高层逻辑模型

4.进一步定义问题

5.导出和评价供选择的解法

6.推荐行动方针

7.草拟开发计划

8.书写文档提交审查

2.3系统流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2uC5Jkwz-1666617584364)(./系统流程图符号.png)]

2.4数据流图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqjxkrmU-1666617584366)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\数据流图符号.png)]

2.5数据字典

2.6成本/效益分析

2.6.1成本估计

1.代码行技术

2.任务分解技术

3.自动估计成本技术

2.6.2成本/效益分析的方法

1.货币的时间价值

2.投资回收期

3.纯收入

4.投资回收率

公式:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eq6LyDiz-1666617584367)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\投资回收率.png)]

3.需求分析

为了开发出真正满足用户需求的软件产品。

1.必须理解并描述问题的信息域,根据这条准则应该建立数据模型

2.必须定义软件应完成的功能,这条准则要求建立功能模型

3.必须描述作为外部事件结果的软件行为,这条准则要求建立行为模型

4.必须对描述信息、功能和行为的模型进行分解,用层次的方式展示细节

3.1需求分析的任务

3.1.1确定对系统的综合要求

即软件规格说明书描述了以下信息:

1.功能需求

2.性能需求

3.可靠性和可用性需求

4.出错处理需求

5.接口需求

6.约束

常见的约束有精度,工具或语言等。

7.逆向需求

说明软件不应该做什么。

8.将来可能提出的需求

3.2与用户沟通获得需求的方法

情景分析主要用处:1.他能在某种程度上演示目标系统的行为,从而便于用户理解,而且还可能进一步揭示出一些分析员目前还不知道的需求 2.由于情景分析较易为用户理解,使用这种技术能保证用户在需求分析中始终扮演一个积极主动的角色

3.2.1访谈

3.2.2面向数据流自顶向下求精

3.2.3简易的应用规格说明技术

3.2.4快速建立软件原型

通常使用方法和工具:1.第四代技术 2.可重用的软件构件 3.形式化规格说明和原型环境

3.3分析建模与规格说明

需求分析过程应该建立三种模型:数据模型,功能模型,行为模型

实体-联系图是用于建立数据模型,数据流图是建立功能模型的基础,状态转换图用于建立行为模型

3.4实体-联系图

依赖关系。

3.4.1数据对象

数据对象是对软件必须理解的复合信息的抽象。

3.4.2属性

属性定义了数据对象的性质。

3.4.3联系

客观世界中的事物彼此间往往是有联系的。

3.5数据规范化

1.第一范式:每个属性值都必须是原子值,即仅仅是一个简单值而不包含内部结构

2.第二范式:满足第一范式条件,而且每个非关键字属性都由整个关键字决定(而不是由关键字的一部分来决定)

3.第三范式:符合第二范式的条件,每个非关键字属性都仅由关键字决定,而且一个非关键字不能仅仅是对另一个非关键字属性的进一步描述(即一个非关键字属性值不依赖于另一个非关键字属性值)

3.6状态转换图

3.6.1状态

状态是任何可以被观察到的系统行为模式,一个状态代表系统的一种行为模式。

状态主要有:初态,终态和中间状态,一个状态图中只有一个初态,而终态则可以有0(系统循环)个或多个。

3.6.2事件

事件是在某个特定时刻发生的事情,它是对引起系统做动作或从一个状态转换为另一个状态的外界事件的抽象。

3.6.3符号

初态:实心圆,终态:同心圆,中间状态:圆角矩形

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kS5dx03V-1666617584368)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\状态图符号.png)]

活动表语法格式:

事件名(entry/exit/do)/动作表达式

事件表达式语法格式:

事件说明[守卫条件]/动作表达式

事件说明的语法为:事件名(参数表),守卫条件是一个布尔表达式,如果同时使用事件说明和守卫条件,则当且仅当守卫条件的波尔值为真时,状态转换才发生。如果只有守卫条件没有事件说明,则只要守卫条件为真,状态转换就发生。

举例:拨打电话
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nEzS9Bql-1666617584369)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\状态图举例.png)]

3.7其他图形工具

3.7.1层次方框图

层次方框图用树形结构的一系列多层次的矩形框描绘数据的层次结构。

3.7.2Warnier图

另一种表示信息层次结构图形。

在一个花括号内的所有名字都属于同一类信息;异或符号表明一类信息或一个数据元素在一定条件下才出现,而且在这个符号上、下方的两个名字所代表的数据只能出现一个;在一个名字下面(或右边)的圆括号中的数字指明了这个名字代表的信息类在这个数据结构中重复的次数。

IPO图

描述算法的有效工具。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zw9jNOJC-1666617584370)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\IPO图.png)]

3.8验证软件需求

3.8.1从哪些方面验证软件需求的正确性

1.一致性:所有需求必须是一致的,任何一条需求不能和其他需求互相矛盾

2.完整性:需求必须是完整的,规格说明书应该包括用户需要的每一个功能或性能

3.现实性:指定的需求应该是用现有的硬件技术和软件技术基本可以实现的。

4.有效性:必须证明需求是正确有效的,确实能解决用户面对的问题。

3.8.2验证需求的方法

1.验证需求的一致性

2.验证需求的现实性

3.验证需求的完整性和有效性

使用原型系统的目的,通常显示目标系统的主要功能而不是性能。

4.形式化说明技术

4.1概述

4.1.1非形式化方法的缺点

利用自然语言书写的系统规格说明书,啰嗦繁杂,可能存在矛盾、二义性、模糊性,不完整性及抽象层次混乱的问题。

抽象层次混乱是指在非常抽象的陈述中混进了一些关于细节的低层次陈述。

4.1.2形式化方法的优点

数学最有用的一个性质是,它能简洁准确地描述物理现象、对象或动作的结果。因此是理想的建模工具。

1.简洁准确的描述物理现象,对象或动作的结果。

2.可以在不同软件工程活动之间平滑地过渡。

3.它提供了高层确认的手段。

4.1.3应用形式化方法的准则

应用场景:开发大型软件系统的过程中应该使用形式化说明技术。

1.应该选用适当的表示方法。

2.应该形式化,但不要过分形式化。

3.应该估算成本。

4.应该有形式化方法顾问随时提供咨询。

5.不应该放弃传统的开发方法。

6.应该建立详尽的文档。

7.不应该放弃质量标准。

8.不应该盲目依赖形式化方法。

9.应该测试、测试再测试。

10.应该重用。即使采用了形式化方法,软件重用仍然是降低软件成本和提高软件质量的唯一合理的方法。而且形式化方法说明的软件构件具有清晰定义的功能和接口,使得它们有更好的可重用性。

4.2有穷状态机

有穷状态机包括六个部分:状态集、输入集、由当前状态和当前输入确定下一个状态的转换函数、初始态、终态集、谓词集

当前状态+事件+谓词=>下个状态

4.3Petri网

包含四种元素:一组位置P、一组转换T、输入函数I以及输出函数O

举例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6KALmwcS-1666617584370)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Petri网举例.png)]

既含权标也含禁止线(禁止线连接的输入线必须没含有权标):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GxCEthFd-1666617584371)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Petri网举例-1.png)]

4.3Z语言

1.给定的集合

[集合英文名称]

2.状态定义

Z格的格式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l0jMG0Dj-1666617584378)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Z格的格式.png)]

3.初始状态

4.操作

举例:

给定的集合:[Button]

初始状态:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqnwBq6V-1666617584381)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Z语言例-4.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PTbMwyan-1666617584382)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Z语言例-1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A1kp01lG-1666617584383)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Z语言例-2.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uH8uNbiL-1666617584383)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Z语言例-3.png)]

说明:符号P表示幂集,△三角符号代表一个格被用在另一个格中,?符号表示输入变量,!符号表示输出变量,引号’表示值更新,差集符号\ …

5.总体设计

站在全局的高度上,花较少成本,从较抽象的层次上分析对比多种可能的系统实现方案和软件结构,从中选出最佳方案和最合理的软件结构,从而用较低成本开发出较高质量的软件系统。

组成系统的物理元素:程序、文件、数据库、人工过程、文档等。

总体设计分为两个主要阶段:首先系统设计阶段,从数据流图出发设想,分析员和用户确定系统实现方案;然后结构设计阶段,确定软件由哪些模块组成以及这些模块之间的动态调用关系。

自顶向下逐步求精是进行软件结构设计的常用途径,但是,如果有了详细的数据流图,则同样可以采用面向数据流的设计方案,用形式化的方法由数据流图映射出软件的初步结构。

5.1设计过程

1.设想供选择的方案

2.选取合理的方案

3.推荐最佳方案

4.功能分解

5.设计软件结构

模块组织成良好的层次系统,顶层模块调用它的下层模块以实现程序的完整功能,每个下层模块再调用更下层模块,从而完成程序的一个子功能,最下层的模块完成最具体的功能。

6.设计数据库

7.制定测试计划

8.书写文档

文档有几种:系统说明、用户手册、测试计划、详细的实现计划、数据库设计结果

9.审查和复查

5.2设计原理

5.2.1模块化

只有适当的模块数和接口数才能使得系统开发成本最小。

5.2.2抽象

人类认识复杂现象的过程中使用的最强有力的思维工具是抽象,所谓的抽象就是:人们在实践中认识到在现实世界中一定事物、状态或过程之间总存在着某些相似的方面(共性),把这些相似的方面集中和概括起来,暂时忽略它们之间的差异,这就是抽象。或者说抽象就是抽出事物的本质特性而暂时不考虑它们的细节。

5.2.3逐步求精

与抽象是一对互补的概念。

5.2.4信息隐藏和局部化

局部化的概念和信息隐藏概念密切相关。所谓局部化是指一些关系密切的软件元素物理地防得彼此靠近。局部化有助于实现信息隐藏,信息隐藏的并不是有关模块的一切信息,而是模块的实现细节。

5.2.5模块独立

模块独立的概念是模块化、抽象、信息隐藏和局部化概念的直接结果。

开发具有独立功能而且和其他模块之间没有过多的相互作用的模块,就可以做到模块独立。

模块独立程度定性标准度量:

1.耦合:耦合是对一个软件结构内不同模块之间互连程度的度量。耦合强弱取决于模块间接口的复杂程度,进入或访问一个模块的点,以及通过接口的数据。

如果两个模块彼此间通过参数交换信息,而且交换的信息仅仅是数据,那么这种耦合称作为数据耦合。

如果传递的信息中有控制信息(这种控制信息可能以数据的形式出现),则这种耦合称之为控制耦合。数据耦合是低耦合,控制耦合是中等程度耦合,控制耦合往往多余,在把模块适当分解之后通常可以用数据耦合代替。

特征耦合的出现是因为将整个数据结构作为参数传递而被调用的模块只需要其中一部分数据元素,这种情况下,被调用的模块可以使用的数据多余它确实需要的数据,导致对数据的访问失去控制。

当两个或多个模块通过一个公共数据环境相互作用时,它们之间的耦合称之为公共环境耦合。

最高程度的耦合是内容耦合。一个模块访问另一个模块的内部数据,一个模块不通过正常入口而转到另一个模块的内部,两个模块有一部分程序代码重叠(只可能出现在汇编程序中),一个模块有多个入口(意味着一个模块有几种功能)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4TC3ZHhF-1666617584384)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\耦合.png)]

2.内聚

内聚标志着一个模块内各个元素彼此结合的紧密程度,它是信息隐藏和局部化概念的自然扩展。

低内聚:

如果一个模块完成一组任务,这些任务彼此之间即使有关系,关系也是很松散,则称之为偶然内聚。例如有时在编写完一个程序之后,发现一组语句在两处或多处出现,于是把这些语句作为一个模块以一个模块以节省内存,这样就出现了偶然内聚。又例如建立一个专门的"工具"模块。

如果一个模块完成的任务在逻辑上属于相同或相似的一类,则称之为逻辑内聚。例如将所有的鼠标和键盘都放在输入处理副程序中。

如果一个模块包括的任务必须在同一段时间内执行,就称之为时间内聚。例如模块完成各种初始化工作。又比如在捕捉到一个异常后调用一函数,在函数中关闭已打开的文件、产生错误日志、并告知用户的一个模块。

中内聚:

一个模块内的处理元素是相关的,而且必须以特定次序执行,则称之为过程内聚。例如一个子程序,首先开始读取学生的学号,然后是姓名,最后是读取分数,是由于特定的顺序而将这些操作组合在一起的。又比如一个函数先检查文件的权限,之后才打开文件。

模块中所有元素都使用同一个输入和(或)产生同一个输出数据,则称之为通信内聚。例如一个模块中的许多过程处理都访问同一个记录。

高内聚:

一个模块内的处理元素和同一个功能密切相关,而且这些处理必须顺序执行,则称之为顺序内聚。通常一个处理元素的输出数据作为下一个处理元素的输入数据。例如有一个子程序,只有事先给定的生日,才能计算出年龄,然后根据年龄算出退休的时间,承前启后的特征,则这个程序具有顺序内聚性。

模块内所有处理元素属于一个整体,完成一个单一的功能,则称之为功能内聚。功能内聚是最高程度的内聚。例如有一个程序中所有的操作都是为了算出一个人的年龄。

5.3启发规则

1.改进软件结构提高模块独立性

2.模块规模应该适中

3.深度、宽度、扇出和扇入都应该适当

深度表示软件结构中控制的层数,它往往能粗略地标志一个系统的大小和复杂程度。

宽度是软件结构内同一个层次上的模块总数的最大值。

扇出是一个模块直接控制(调用)的模块数目,扇出过大意味着模块过分复杂,需要控制和协调过多的下级模块。扇出过多一般是缺乏中间层次,应该适当增加中间层次的控制模块。扇出太小时可以把下级模块进一步分解成若干子功能模块,或者合并到它的上级模块中去。

扇入代表有多少个上级模块直接调用它本身,扇入越大则共享该模块的上级模块数目越多。

4.模块的作用域应该在控制域之内

模块的作用域定义为受该模块内一个判定影响的所有模块的集合。模块的控制域是这个模块本身以及所有直接或间接从属于它的模块的集合。

5.力争降低模块接口的复杂程度

6.设计单入口单出口的模块

7.模块功能应该可以预测

携带内部"存储器"的模块功能可能是不可预测的,因为它的输出可能取决于内部存储器的状态,由于内部存储器对于上级模块而言是不可见的,所以这样的模块既不容易理解又难于测试。

5.4描绘软件结构的图形工具

5.4.1层次图和HIPO图

层次图用来描绘软件的层次结构。

HIPO图是层次图加上IPO图,这里的层次图带编号,通过编号追踪IPO图内部过程。

示例图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PuGDAAYt-1666617584385)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\HIPO图示例.png)]

5.4.2结构图

尾部空心圆表示传递的为数据,实心圆表示传递的为控制信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOxq75qj-1666617584386)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\软件结构设计结构图示例.png)]

其余符号:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5x5YSkWz-1666617584387)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\软件结构设计结构图附加.png)]

5.5面向数据流的设计方法

任何软件系统都可以用数据流图表示,面向数据流的设计方法理论上可以设计任何软件的结构。结构化设计方法基于数据流的设计方法。

5.5.1概念

1.变换流

根据基本系统模型,信息通常以"外部世界"的形式进入软件系统,经过处理后再以"外部世界"的形式离开系统。

信息沿输入通路釞系统,同时由外部形式变换成内部形式,进入系统的信息通过变换中心,经加工处理以后再沿输出通路变换成外部形式离开软件系统。当数据流图具有这些特征时,这种信息流就称作为变换流。

2.事务流

数据流是"以事务为中心",即数据沿输入通路到达一个处理,处理之后根据输入数据选择一条动作序列来执行。

3.设计过程

5.5.2变换分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IHC5AJZK-1666617584388)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\面向数据流方法的设计过程.png)]

设计步骤:

第一步:复查基本系统模型

第二步:复查并精化数据流图

第三步:确定数据流图具有变换特性还是事务特性

第四步:确定输入流和输出流的边界,从而孤立出变换中心

第五步:完成"第一级分解"

软件结构代表对控制的自顶向下的分配,所谓分解就是分配控制的过程。

分解输入控制模块、变换中心控制模块、输出控制模块

第六步:完成"第二级分解"

所谓第二级分解就是把数据流图中的每个处理映射成软件结构中的一个适当的模块。

简而言之,就是映射为模块。

第七步:使用设计度量和启发式规则对第一次分割得到的软件结构进一步精华

对层次图进行合并、分解或移动等操作。

5.5.3事务分析

一般来说,如果数据流不具有显著事务特性,最好使用变换分析;反之,如果具有明显的事务中心,则应该采用事务分析技术。

5.5.4设计优化

设计优化追寻的目标应该是力求做到在有效地模块化的前提下使用最少量的模块,以及在能够满足信息要求的前提下使用最简单的数据结构。

“先使它工作,然后再使它快起来。”

6.详细设计

详细设计阶段的根本目标是确定应该怎样具体地实现所要求的系统。详细设计的目标不仅仅是逻辑上正确地实现每个模块的功能,更重要的是设计出的处理过程应该尽可能简明易懂。

保证软件的可靠性,程序具有可读性、容易理解、容易修改和维护,是详细设计的逻辑基础。

结构化的程序只有一个入口和一个出口。

6.2人机界面设计

6.2.1设计问题

1.系统响应时间

有两个重要属性:长度和易变性

系统响应时间过长,用户体验差,系统响应时间过短,用户可能操作不当。

易变性是指系统响应时间相对于平均响应时间的偏差。

2.用户帮助设施

常见的帮助设施分为集成和附加两类。

3.出错信息处理

信息应该用用户可以理解的术语描述问题;信息应该提供有助于从错误中恢复的建设性意见;信息应该指出错误可能导致哪些负面后果,以便用户检查是否出现了这些问题,并在确实出现问题时及时解决;信息应该伴随着听觉上或视觉上的提示;信息不能带有指责色彩,也就是说,不能责怪用户。

4.命令交互

6.2.2设计过程

1.系统及其界面的规格说明书的长度和复杂程度,预示了用户学习使用该系统所需要的工作量。

2.命令或动作的数量、命令的平均参数个数或动作中单个操作的个数,预示了系统的交互时间和总体效率。

3.设计模型中包含的动作、命令和系统状态的数量,预示了用户学习使用该系统需要记忆的内容的多少。

4.界面风格、帮助设施和出错处理协议,预示了界面的复杂程度及用户接受该界面的程度。

6.2.3人机界面设计指南

1.一般交互指南

保持一致性:应该为人机界面中的菜单选择、命令输入、数据显示以及众多其他功能,使用一致的格式。

提供有意义的反馈。

在执行有较大破坏性的动作之前要求用户确认。

允许取消大多数操作。

减少两次操作之间必须记忆的信息量:不应该期望用户能记住下一步操作中需要使用的一大串数字或标识符。

提高对话、移动和思考的效率。

允许犯错误。

按功能对动作分类,并根据此设计屏幕布局。

提供对用户工作内容敏感的帮助设施。

用简单动词或动词短语作为命令名。

2.信息显示指南

只显示与当前工作内容有关的信息。

不要用数据淹没用户,应该用便于用户迅速吸取信息的方式来表示数据。

使用一致的标记、标准的缩写和可预知的颜色:显示的含义应该非常明确,用户无须参照其他信息源就能理解。

允许用户保持可视化的语境。

产生有意义的出错信息。

使用大小写、缩进和文本分组以帮助理解。

使用窗口分隔不同类型的信息。

使用"模拟"显示方式表示信息,以使信息更容易被用户提取。

高效率地使用显示屏。

3.数据输入指南

尽量减少用户的输入动作。

保持信息显示和数据输入之间的一致性。

允许用户自定义输入。

交互应该是灵活的,并且可调整成用户最喜欢的输入方式。

使在当前动作语境中不适用的命令不起作用。

让用户控制交互流:用户应该能够跳过不必要的动作,改变所需做的动作的顺序,以及在不退出程序的情况下从错误状态中恢复正常。

对所有输入动作都提供帮助。

消除冗余的输入。

6.3过程设计的工具

6.3.1程序流程图

主要缺点:1.程序流程图本质上不是逐步求精的好工具,它诱使程序员过早地考虑程序的控制流程,而不去考虑程序的全局结构。2.程序流程图中用箭头代表控制流,因此程序员不受任何约束,可以完全不顾结构程序设计的精神,随意转移控制。3.程序流程图不易表示数据结构。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJdxLQag-1666617584389)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\程序流程图符号.png)]

6.3.2盒图(N-S图)

特点:1.功能域(即一个特定控制结构的作用域),2.不可能任意转移控制,3.很容易确定局部和全程数据的作用域,4.很容易表现嵌套关系,也可以表示模块的层次关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wLGw2JFC-1666617584389)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\盒图.png)]

6.3.3PAD图

问题分析图(PAD图)

PAD图基本符号:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XS5NS8nR-1666617584390)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\PAD图基本符号.png)]

主要优点:

1.使用表示结构化控制结构的PAD符号所设计出来的程序必然是结构化程序

2.PAD图描绘的程序结构十分清晰。图中最左面的竖线是程序的主线,即第一层结构。随着程序层次的增加,PAD图逐渐向右延伸,每增加一个层次,图形向右扩展一条竖线。

3.用PAD图表现程序逻辑,易读、易懂、易记。自上向下,从左向右顺序执行,遍历所有节点。

4.容易将PAD图转换成高级语言源程序,这种转换可用软件工具自动完成,从而省去人工编码的工作,有利于提高软件可靠性和软件生产率。

5.PAD图的符号支持自顶向下、逐步求精方法的使用。如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Doss1mMi-1666617584391)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\PAD图示例.png)]

6.3.4判定表

用判定表表示计算行李费的算法

左上部分列出所有条件,左下部分是所有可能需要做的动作,右上部分是表示各种条件组合的一个矩阵,右下部分是和每种条件组合相对应的动作。判定表右下部分画×表示执行对应左下部分的动作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tncSw6Pd-1666617584392)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\判定表示例.png)]

6.3.5判定树

判定树是判定表的变种,它也能清晰地表示复杂条件组合与应做的动作之间的对应关系。

判定树示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bsS16pp-1666617584393)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\判定树示例.png)]

6.3.6过程设计语言(PDL)

特点:

1.关键字的固定语法,它提供了结构化控制结构、数据说明和模块化的特点。

2.自然语言的自由语法,它描述处理特点。

3.数据说明的手段。

4.模块定义和调用的技术,应该提供各种接口描述模式。

优点:

1.可以作为注释直接插在源程序中间。

2.可以使用普通的正文编辑程序或文字处理系统,很方便地完成PDL的书写和编辑工作。

3.已经有自动化处理PDL的程序存在,而且可以自动由PDL生成程序代码。

6.4面向数据结构的设计方法

计算机软件本质上是信息处理系统,因此,可以根据软件所处理的信息的特征来设计软件。

6.4.1Jackson图

1.顺序机构

2.选择结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sWj0ZETz-1666617584393)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Jackson图示例-1.png)]

3.重复结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7XRYlLy7-1666617584394)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Jackson图示例-2.png)]

6.4.2改进的Jackson图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FnRtd0H1-1666617584394)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\改进后的Jackson图示例.png)]

6.4.3Jackson方法

步骤:

1.分析并确定输入数据和输出数据的逻辑结构,并用Jackson图描绘这些数据结构。

2.找出输入数据结构和输出数据结构中有对应关系的数据单元。所谓对应关系是指有直接的因果关系,在程序中可以同时处理的数据单元。

3.用三条规则从描绘数据结构的Jackson图导出描绘程序结构的Jackson图:为每对有对应关系的数据单元,按照它们在数据结构图中的相应层次在画一个处理框。根据输入数据结构中剩余的每个数据单元所处的层次,在程序结构图相应参差分别为它们画上对应的处理框。根据输出数据结构中剩余的每个数据单元所处的层次,在程序结构图的相应层次分别为它们画上对应的处理框

4.列出所有操作和条件,并且把它们分配到程序结构图的适当位置。

5.用伪码表示程序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YRcg4Mxe-1666617584395)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Jackson方法.png)]

Jackson示例-统计正文文件中每行字符串记录中的空格数,输出格式要求每复制一行输入字符串之后,另起一行印出这个字符串中的空格数,最后一行输出空格总数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rz9dIx85-1666617584395)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\改进的Jackson图示例.png)]

正文文件和输出表格对应,字符串与串信息对应。

6.5程序复杂程度的定量度量

6.5.1McCabe方法

1.流图

在流图中用圆表示结点,一个圆代表一条或多条语句。程序流程图中的一个顺序的处理框序列和一个菱形判定框,可以映射流程图中的一个结点(按情况可合并)。流程图中的箭头线称为边,它和程序流程图中的箭头线类似,代表控制流。在流图中一条边必须终止于一个结点,即使这个结点不代表任何语句(实际上相当于一个空语句)。由边和结点围成的面积称为区域,当计算区域数时应该包括图外部未被围起来的那个区域。

用任何方法表示的过程设计结果,都可以翻译成流图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DtTtodeO-1666617584396)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\Mccabe流图.png)]

复合条件的流图稍微复杂一些,在这种情况下,应该把复合条件分解为若干个简单条件,每个简单条件对应流图中的一个结点。如下示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BgNLgTJB-1666617584397)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\复合条件下的流图示例.png)]

2.计算环形复杂度的方法

环形复杂度定量程序的逻辑复杂度。

三种方法来计算环形复杂度:流图中线性无关的区域数等于环形复杂度。流图G的环形复杂度V(G)=E-N+2,其中E是流图中的边的条数,N是结点数。流图G的环形复杂度V(G)=P+1,P是流图中判定结点的数目。

3.环形复杂度的用途

程序的环形复杂度取决于程序控制流的复杂程度,即取决于程序结构的复杂程度。当程序内分支数或循环个数增加时,环形复杂度也随之增加,因此它是对测试难度的一种定量度量,也能对软件最终的可靠性给出某种预测。

实践表明,模块规模以V(G)≤10为宜,也就是说,V(G)=10是模块规模的一个更科学更精确的上限。

6.5.2Halstead方法

根据程序中运算符和操作数的总数来度量程序的复杂程度。

N 1 N_1 N1为程序中运算符出现的总次数, N 2 N_2 N2为操作数出现的总次数,程序长度定义为: N = N 1 + N 2 N=N_1+N_2 N=N1+N2

已知程序中使用的不同运算符(包括关键字)的个数为 n 1 n_1 n1,不同操作数(变量和常量)的 n 2 n_2 n2,预测程序长度的公式为: H = n 1 l o g 2 n 1 + n 2 l o g 2 n 2 H=n_1log_2n_1+n_2log_2n_2 H=n1log2n1+n2log2n2,其预测的H与实际长度N非常接近。

预测程序中包含错误的个数公式: E = N l o g 2 ( n 1 + n 2 ) / 3000 E=Nlog_2(n_1+n_2)/3000 E=Nlog2(n1+n2)/3000

7.实现

一般将编码和测试统称为实现。

7.1编码

7.1.1选择程序设计语言

实用标准:

1.系统用户的要求。

2.可以使用的编译程序。

3.可以得到的软件工具。

4.工程规模。

5.程序员的知识。

6.软件可移植性要求。

7.软件的应用邻域。

7.1.2编码风格

源程序代码的逻辑简明清晰、易读易懂是好程序的一个标准。

遵循的规则:

1.程序内部的文档

所谓程序内部的文档包括恰当的标识符、适当的注解和程序的视觉组织等。比如使用缩写,那么缩写规则应该一致,并且应该给每个名字加上注解。

2.数据说明

虽然在设计期间已经确定了数据结构的组织和复杂程度,然而数据说明的风格却是在写程序时确定的。为了使数据更容易理解和维护,有一些比较简单的原则应该遵循。数据说明的次序应该标准化(例如按照数据结构或数据类型确定说明的次序)。有次序就容易查阅,因此能够加速测试、调试和维护的过程。

当多个变量名在一个语句中说明时,应该按字母顺序排列这些变量。

如果设计时使用了一个复杂的数据结构,则应该用注解说明用程序设计语言实现这个数据结构的方法和特点。

3.语句构造

遵循的原则是,每个语句都应该简单而直接,不能为了提高效率而使程序变得过分复杂。

规则:不要为了节省空间而把多个语句写在一行。尽量避免复杂的条件测试。尽量减少对"非"条件的测试。避免大量使用循环嵌套和条件嵌套。利用括号使逻辑表达式或算术表达式的运算次序清晰。

4.输入输出

规则:对所有输入数据都进行检验。检查输入项重要组合的合法性。保持输入格式简单。使用数据结束标记,不要要求用户指定数据的数目。明确提示交互式输入的请求,详细说明可用的选择或边界数值。当程序设计语言对格式有严格要求时,应保持输入格式一致。设计良好的输出报表。给所有输出数据加标志。

5.效率

降低程序运行时间:写程序之前先简化算术的和逻辑的表达式。仔细研究嵌套的循环,以确定是否有语句可以从内层往外移。尽量避免使用多维数组。尽量避免使用指针和复杂的表。使用执行时间短的算术运算。不要混合使用不同的数据类型。尽量使用整数运算和布尔表达式。

存储器效率

输入和输出的效率(如果用户为了给计算机提供输入信息或为了理解计算机输出的信息,所需花费的脑力劳动是经济的,那么人和计算机之间通信的效率就高):所有输入输出都应该有缓冲,以减少用于通信的额外开销。对二级存储器(如磁盘)应该用最简单的访问方法。二级存储器的输入输出应该以信息组为单位。如果"超高效的"输入输出很难被人理解,则不应采用这种方法。

7.2软件测试基础

7.2.1软件测试的目标

1.测试是为了发现程序中的错误而执行程序的过程。

2.好的测试方案是极可能发现迄今为止尚未发现的错误的测试方案。

3.成功的测试是发现了至今为止尚未发现的错误的测试。

7.2.2软件测试准则

1.所有测试都应该能追溯到用户需求。

2.应该远在测试开始之前就制定出测试计划。

3.把Pareto同源应用到软件测试中。

4.应该从"小规模"测试开始,并逐步进行"大规模"测试。

5.穷举测试是不可能的。

6.为了达到最佳的测试效果,应该由独立的第三方从事测试工作。

7.2.3测试方法

测试任何产品都有两种方法:如果已知产品应该具有的功能,可以通过测试检验是否每个功能都能正常使用,这种方法称之为黑盒测试,这种方法又称之为功能测试;如果已知产品的内部工作过程,可以通过测试来检验产品内部动作是否按照规格说明书的规定正常进行,这种方法称之为白盒测试,又称之为结构测试。

7.2.4测试步骤

1.模块测试

设计好的软件系统中,每个模块完成一个清晰定义的子功能,而且这个子功能和同级其他模块的功能之间没有相互依赖关系。模块测试的目的是保证每个模块作为一个单元能正确运行,所以模块测试又称之为单元测试。

2.子系统测试

子系统测试是把经过单元测试的模块放在一起形成一个子系统来测试。模块间相互间的协调和通信是这个测试过程中的主要问题,所以,这个步骤着重测试模块的接口。

3.系统测试

系统测试是把经过测试的子系统装配成一个完整的系统来测试。在这个测试中发现的往往是软件设计中的错误,也可能发现需求说明中的错误。不论是子系统测试还是系统测试,都兼有检测和组装两重含义,通常称之为集成测试。

4.验收测试

验收测试把软件系统作为单一的实体进行测试,测试内容与系统测试基本相似,但是它是在用户积极参与下进行的,而且可能主要使用实际数据进行测试。测试验收也称之为确认测试。

5.平行运行

关系重大的软件产品在验收之后往往并不立即投入生产性运行,而是要再经过一段平行运行时间的考验。所谓平行运行就是同时运行新开发出来的系统和将被取代的旧系统,以便比较新旧两个系统的处理结果。具体目的:可以在准生产环境中运行新系统而又不冒风险。用户能有一段熟悉新系统的时间。可以验证用户指南和使用手册之类的文档。能够以准生产模式对新系统进行全负荷测试,可以用测试结果验证性能指标。

7.2.5测试阶段的信息流

软件配置:包括需求说明书、设计说明书和源程序清单等

测试配置:包括测试计划和测试方案。所谓测试方案不仅仅是测试使用的输入数据(称为测试用例),还应该包括每组输入数据预定要检验的功能,以及每组输入数据预期应该得到的正确输出。

调试与测试不同,通常由程序的编写者负责调试。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xg2Qyw5F-1666617584397)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\测试阶段的信息流.png)]

7.3单元测试

单元测试主要使用白盒测试技术,而且对多个模块的测试可以并行地进行。

7.3.1测试重点

1.模块接口

2.局部数据结构

3.重要的执行通路

4.出错处理通路

5.边界条件

7.3.2代码审查

审查成员的一般组成:

没有直接参与这项工程的组长,程序的设计者,程序的编写者,程序的测试者。

代码审查比计算机测试优越的是:一次审查会上可以发现许多错误;用计算级测试的方法发现错误之后,通常需要先改正这个错误才能测试,因此错误是一个一个地发现并改正的。也就是说,采用代码审查的方法可以减少系统验证的总工作量。

7.3.3计算机测试

模块并不是一个独立的程序,因此必须为每个单元测试开发驱动软件和(或)存根软件。

驱动程序通常是一个"主程序",它接收测试软件,把这些数据传送给被测试的模块,并且印出有关的结果。

存根程序代替被测试的模块所调用的模块,因此存根程序可以称之为"虚拟子程序"。它使用被它代替的模块的接口,可能做最少量的数据操作,印出对入口的检验或操作结果,并且把控制归还给调用它的模块。数据不流向上级模块。

7.4集成测试

由模块组装成程序有两种方法:一种是先分别测试每个模块,在把所有模块按设计要求在一起组合成所要的程序,这种方法称之为非渐增式测试方法;另一种是把下一个要测试的模块同已经测试好的那些模块结合起来进行测试,测试完以后再把下一个应该测试的模块结合进来测试,这种方法称之为渐增式测试。

目前,普遍采用渐增式测试方法,因为非渐增式方法需要把整个庞大的程序作为一个整体来测试,情况复杂。

7.4.1自顶向下集成

从主控制模块开始,沿着程序的控制次序向下移动,逐渐把各个模块结合起来。过程可以使用深度优先策略或者宽度优先策略。

具体步骤:

1.对主控制模块进行测试,测试时用存根程序代替所有直接附属于主控制模块的模块。

2.根据选定的结合策略,每次用一个实际模块代换一个存根程序。

3.在结合进一个模块没有引进新的错误,可能需要进行回归测试(即部分或全部地重复以前做过的测试)。

4.从2开始不断重复上述过程,直到其构造起完整的软件结构。

7.4.2自底向上集成

为了解决存根程序数据无法自下往上流。

自底向上测试从"原子"模块(即在软件结构最低层的模块)开始组装和测试。因为是最底层,所以不要存根程序。

具体步骤:

1.把低层模块组合成实现某个特定的软件子功能的族。

2.写一个驱动程序(用于测试的控制程序),协调测试数据的输入和输出。

3.对由模块组成的子功能族进行测试。

4.去掉驱动程序,沿软件结构自下向上移动,把子功能族组合起来形成更大的功能族。

5.从2开始不断重复上述过程。

7.4.3不同集成策略的比较

自顶向下集成和自底向上集成优缺点大致互补,因此,采用混合策略最佳。

1.改进的自顶向下测试方法:基本使用自顶向下的测试方法,但是早期使用自底向上的方法测试软件中的少数关键模块。

2.混合法:对软件结构中较上层使用的自顶向下方法与对软件结构较下层使用的自底向上方法相结合。

7.4.4回归测试

在集成测试过程中每当一个新模块结合进来,程序就发生了变化:建立了新的数据流路径,可能出现了新的I/O操作,激活了新的控制逻辑。这些变化有可能使得原来工作正常的模块功能出现异常。因此,需要重新执行已经做过测试的某个自己,以保证上述这些变化没有带来非预期的副作用。

回归测试集应该包括以下特点:

1.检测软件全部功能的代表性测试用例。

2.专门针对可能手修改影响的软件功能的附加测试。

3.针对被修改过的软件成分的测试。

7.5确认测试

目标:验证软件的有效性。

软件有效的一个简单定义:如果软件的功能和性能如同用户所合理期待的那样,软件就是有效地。

测试数据:正常测试数据一类(对于某功能的输入数据,其可以采用不同的对比项,并重复3~5次测试),特殊测试数据一类(对于输入数据可以采用一些边界值,比如0、负数、极大值和极小值等)

7.5.1确认测试的范围

确认测试必须用户积极参与,或者以用户为主进行。

确认测试通常采用黑盒测试法。

通过测试和调试要保证软件能满足所有功能要求,能达到每个性能要求等等。(参考需求分析阶段)

确认测试两种结果:

1.功能和性能与用户要求一致,软件是可以接受的。

2.功能和性能与用户要求有差距。

7.5.2软件配置复查

确认测试的一个重要内容是复查软件配置。复查的目的是保证软件配置的所有成分都齐全,质量符合要求,文档与程序完全一致,就有维护软件所必须的细节,而且已编好目录。

7.5.3Alpha和Beta测试

如果软件是专门为某个客户开发的,只需要进行一系列验收测试,以便用户确认所有需求都得到满足。如果软件是为许多客户开发的,那么,让每个客户都进行正式的验收测试是不现实的,此情况下,绝大多数开发商都使用Alpha测试和Beta测试的过程。

Alpha测试由用户在开发者的场所进行,并且在开发者对用户的"指导"下进行测试,是在受控的环境下进行的。

Beta测试由软件的最终用户们在一个或多个客户场所进行。与Alpha测试不同,开发者通常不在Beta测试的现场,因此,Beta测试时软件在开发者不能控制的环境中的"真实"应用。

7.6白盒测试

设计测试方案是测试阶段的关键技术问题,其中最困难的问题是设计测试用的输入数据。

7.6.1逻辑覆盖

有选择地执行程序中某些最有代表性的通路是对穷尽测试的唯一可行的替代办法。

覆盖标准:

1.语句覆盖

为了暴露程序中的错误,至少每个语句应该执行一次。

如下程序流程图,该程序的执行路径应该为:sacbed

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4E9TXFHZ-1666617584398)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\逻辑覆盖示例.png)]

2.判定覆盖

判定覆盖又叫分支覆盖,它的含义是,不仅每个语句必须至少执行一次,而且每个判定的每种可能结果(真和假)都应该至少执行一次,也就是每个判定分支都至少执行一次。

此时,根据上述程序流程图,程序的测试路径可以为:sacbed、sabd或者sacbd、sabed(分析:TT和FF情况,TF和FT情况,即判定表达式互补即可)

判定覆盖比语句覆盖强,但是对程序逻辑的覆盖程度仍然不高,上述只覆盖了程序全部路径的一半。

3.条件覆盖

不仅每个语句至少执行一次,而且使判定表达式中的每个条件都取到各种可能的结果。

此时,根据上述程序流程图,测试路径可以为:sacbed、sabd(分析:判定表达式中的所有条件全部为True和全部为False,即条件互补即可)

条件覆盖通常比判定覆盖强,满足条件覆盖不满足判定覆盖的一组测试路径:sacbed、sabed

4.判定/条件覆盖

既然判定覆盖不一定包括条件覆盖,条件覆盖也不一定包含判定覆盖,自然提出一种能同时满足两种覆盖标准的逻辑覆盖,这就是判定/条件覆盖。

此时,测试路径可以为:sacbed、sabd(分析:判定和条件全为True和False),但是并不比条件覆盖更强。

5.条件组合覆盖

条件组合覆盖是更强的逻辑覆盖标准,它要求每个判定表达式中条件的各种可能组合都至少出现一次。

针对上述程序流程图,共有8种条件组合
1. A > 1 , B = 0 2. A > 1 , B ≠ 0 3. A ≤ 1 , B = 0 4. A ≤ 1 , B ≠ 0 5. A = 2 , X > 0 6. A = 2 , X ≤ 1 7. A ≠ 2 , X > 1 8. A ≠ 2 , X ≤ 1 1.A>1,B=0\\ 2.A>1,B\ne0\\ 3.A\le1,B=0\\ 4.A\le1,B\ne0\\ 5.A=2,X>0\\ 6.A=2,X\le1\\ 7.A\ne2,X>1\\ 8.A\ne2,X\le1 1.A>1,B=02.A>1,B=03.A1,B=04.A1,B=05.A=2,X>06.A=2,X17.A=2,X>18.A=2,X1
1、5组合:sacbed,2、6组合:sabed,3、7组合sabed,4、8组合:sabd(分析:每种组合都访问到)

但是满足条件组合覆盖标准的测试数据并不一定能使程序中的每条路径都执行到,比如路径sacbd未测试到。

6.点覆盖

点覆盖和语句覆盖的标准是相同的。

7.边覆盖

通常,边覆盖和判定覆盖是一致的。

8.路径覆盖

程序的每条可能路径都至少执行一次。

7.6.2控制结构测试

1.基本路径测试

步骤:

根据过程设计结果画出相应流图。

计算流图的环形复杂度。

确定线性独立路径的基本集合(由环形复杂度决定路径个数):所谓独立路径是指至少引入程序的一个新处理语句集合或一个新条件路径。

设计可强制执行基本集合中没条路径的测试用例。

示例如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m6BLVani-1666617584399)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\控制结构测试示例.png)]

以上示例的环形复杂度为6,因此共有6条独立路径。

路径1:1-2-10-12-13

路径2:1-1-10-11-13

路径3:1-2-3-10-12-13

路径4:1-2-3-4-5-8-9-2-…

路径5:1-2-3-4-5-6-8-9-2-…

路径6:1-2-3-4-5-6-7-8-9-2-…

省略号表示可以后接通过控制结构其余部分的任意路径。

通过独立路径设计测试用例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBLP4jp4-1666617584400)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\控制结构测试示例-2.png)]

2.条件测试

尽管基本路径测试技术简单而且高效,但是仅有这种技术还不够,还需要使用其他控制结构测试技术,才能进一步提高白盒测试的质量。使用条件测试技术出的测试用例,能够检查程序模块中包含的逻辑条件。

作用,发现以下类型的错误:

布尔变量错误:例如 (x>y)写出(x>z)。

布尔括弧错误:例如 (x+y)*z少些括弧变成x+y*z。

关系操作符错误:例如 x>y写出x≥y。

算术表达式错误:例如 x*y写成xy。

3.循环测试

测试思想:关注循环体结构的正确性,对循环变量运用类似于边界值测试的方法以验证循环体结构的正确性。

1.简单循环

如果n是循环次数,那么测试用例应包括:直接跳过循环,只通过一次循环,通过循环两次,通过循环m次(m<n-1),通过循环n-1,n,n+1次。

2.嵌套循环

采用Beizer方法:

从最内层循环开始测试,把所有其他循环都设置最小值。

对最内层循环使用简单循环测试方法,而使外层循环的迭代参数取最小值(循环边界值),并为越界值或非法值增加一些额外的测试。

由内向外,对下一个循环进行测试,但保持所有其他外层循环为最小值,其他嵌套循环为"典型"值。

继续进行下去,直到测试完所有循环。

3.串接循环

如果串接循环的各个循环都彼此独立,则采用前面的简单循环测试,反之,如果两个循环串接,并且第一个循环的循环计数器值是第二个循环的初始值,则这两个循环并不是独立的,则采用测试嵌套循环的方法来测试串接循环。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXSDMRAM-1666617584401)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\循环测试.png)]

7.7黑盒测试技术

黑盒测试着重测试软件功能。白盒测试在测试的早期阶段进行,而黑盒测试主要用于测试过程的后期。

黑盒测试力图发现下述类型的错误:

1.功能不正确或遗漏了功能。

2.界面错误。

3.数据结构错误或外部数据库访问错误。

4.性能错误。

5.初始化和终止错误。

7.7.1等价划分

把程序的输入域划分成若干个数据类,据此导出测试用例。一个理想的测试用例能独立发现一类错误(例如对所有负整数的矗立都不正确)。穷尽不可取,选取少量具有代表性的输入数据。

7.7.2边界值分析

经验表明,处理边界情况时程序最容易发生错误。

7.7.3错误推测

错误推测法在很大程度上靠直觉和经验进行。它的基本思想法是列举出程序中可能有的错误和容易发生的错误的特殊情况。输入数据组合检测出程序的错误。

7.8调试

调试(也称纠错)作为成功测试的后果出现,也就是说,调试是在测试发现错误之后排除错误的过程。

7.8.1调试过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nmE9Nfp1-1666617584401)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\调试过程.png)]

7.8.2调试途径

1.蛮干法

最低效的方法,按照"让计算机自己寻找错误"的策略,仅当所有其他方法都失败了情况下,才应该使用这种方法。计算机自行print或log发现线索。

2.回溯法

从发现症状开始,人工沿程序的控制流往回追踪分析源程序代码,直到找出错误原因为止,调试小程序有效,但随着程序规模扩大,回溯路径数目增加,彻底回溯将完全不可能。

3.原因排除法

对分查找法、归纳法和演绎法都属于原因排除法。

对分查找法:如果已经知道每个变量在程序内若干关键点的正确值,则可以用赋值语句或输入语句在程序中点附近"注入"这些变量的正确值,然后运行程序并检查所得到的输出。如果输出结果是正确的,则错误原因在程序的前半部分;反之,错误原因在程序的后半部分。重复以上操作,类似于二分查找,直到范围缩小到程序容易诊断的程度为止。

归纳法:从个别现象推断出一般性结论的思维方法。使用该方法调试程序时,首先把和错误有关的数据组织起来进行分析,以便发现可能的错误原因。然后导出对错误原因的一个或多个假设,并利用已有的数据来证明或排除这些假设。如果已有的数据尚不足以证明或排除这些假设,则需要设计并执行一些新的测试用例,以获得更多的数据。

演绎法:从一般原理或前提出发,经过排除和精化的过程推导出结论。采用这种方式调试程序时,首先设想出所有可能所有出错原因,然后试图用测试来排除每个假设的原因。如果测试表明某个假设的原因可能是真的原因,则对数据进行细化以准确定位错误。

找到错误后就必须改正它,但是,改正一个错误后往往可能会引入更多的错误,所以在动手改正之前,应该思考以下三个问题:

1.是否同样的错误也在程序其他地方存在?

2.将要进行的修改可能会引入的"下一个错误"是什么?

3.为防止今后出现类似的从错误,应该做什么?

7.9软件可靠性

7.9.1基本概念

1.软件可靠性的定义

多数人认同对的一个定义:软件可靠性是程序在给定的时间间隔内,按照规格说明书的规定成功地运行的概率。

2.软件的可用性

通常用户关注软件系统对的可以使用的程度。

其定义为:软件可用性是从程序在给定时间点,按照规格说明书规定,成功地运行的概率。

如果在一段时间内,软件系统故障停机时间分别为 t d 1 , t d 2 , . . . , t_{d1},t_{d2},..., td1,td2,...,正常运行时间为 t u 1 , t u 2 , . . . , t_{u1},t_{u2},..., tu1,tu2,...,则系统的稳态可用性为:

A s s = T u p T u p + T d o w n T u p = ∑ T u i , T d o w n = ∑ T d i A_{ss}=\frac{T_{up}}{T_{up}+T_{down}} \\ T_{up}=\sum T_{ui},T_{down}=\sum T_{di} Ass=Tup+TdownTupTup=Tui,Tdown=Tdi

7.9.2估算平均无故障时间的方法

软件的平均无故障时间MTTF是一个重要的质量指标,往往作为对软件的一项要求,由用户提出来。

1.符号

KaTeX parse error: Expected 'EOF', got '&' at position 7: E_{T}-&̲测试之前程序中的错误总数;\\…

2.基本假定

1.单位长度里的错误数 E T / I T E_{T}/I_{T} ET/IT近似为常数。 0.5 ∗ 1 0 − 2 ≤ E T / I T ≤ 2 ∗ 1 0 − 2 0.5*10^{-2}\le E_{T}/I_{T}\le 2*10^{-2} 0.5102ET/IT2102,也就是说,在测试之前每1000条指令中大约有5~20个错误。

2.失败率正比于软件中剩余的(潜藏的)错误数,而平均无故障时间MTTF与剩余的错误数成反比。

3.假设发现的每一个错误都立即正确地改正了,并未引入新的错误,因此有, E c ( τ ) = E d ( τ ) E_c(\tau)=E_d(\tau) Ec(τ)=Ed(τ),剩余的错误数为 E r ( τ ) = E T − E c ( τ ) E_r(\tau)=E_T-E_c(\tau) Er(τ)=ETEc(τ),单位长度程序中剩余的错误数为 ϵ r ( τ ) = E T / I T − E c ( τ ) / I T \epsilon_r(\tau)=E_T/I_T-E_c(\tau)/I_T ϵr(τ)=ET/ITEc(τ)/IT

其余补充: M T T F MTTF MTTF与测试时间 t t t关系: M T T F = t ∗ k + b MTTF=t*k+b MTTF=tk+b

3.估算平均无故障时间

平均无故障时间与单位长度程序中剩余的错误数成反比,即 M T T F = 1 K ( E T / I T − E c ( τ ) / I T ) ( 重点公式 ! ) MTTF=\frac{1}{K(E_T/I_T-E_c(\tau)/I_T)} (重点公式!) MTTF=K(ET/ITEc(τ)/IT)1(重点公式!)

美国统计学家表明,K的典型值为200。

因此, E c = E T − I T K ∗ M T T F E_c=E_T-\frac{I_T}{K*MTTF} Ec=ETKMTTFIT

4.估计错误总数的方法

程序中潜藏的错误的数目是一个十分重要的量,它既直接标志软件的可靠程度,又是计算软件平均故障时间点的重要参数。

1.植入错误法

使用这种估计方法,在测试之前由专人在程序中随机地植入一些错误,测试之后,根据测试小组发现的错误中原有的和植入的两种错误的比例,来估计程序中原有错误的总数 E T E_T ET

假设人为地植入的错误数为 N s N_s Ns,经过一段时间的测试之后发现 n s n_s ns个植入的错误,此外还发现了 n n n个原有的错误。如果可以认为测试方案发现植入错误和发现原有错误的能力相同,则能够估计出程序中原有错误的总数为 N = n n s N s N=\frac {n}{n_s}N_s N=nsnNs,其中 N N N即是错误总数 E T E_T ET的估计值。

2.分别测试法

植入错误法的基本假定是所用的测试方案发现植入错误和发现原有错误对的概率相同。但是,人为地植入的错误和程序中原有的错误可能性质很不相同,发现它们难易程度自然也不相同,因此,上述基本假定可能有时和事实不完全一致。

如果有办法随机地把程序中一部分原有的错误加上标记,然后根据测试过程中发现的有标记错误和无标记错误的比例,估计程序中的错误总数,则这样得出的结果比用植入错误法得到的结果更可信一些。

τ = 0 时错误总数为 B 0 τ = τ 1 时测试员甲发现的错误数为 B 1 τ = τ 1 时测试员乙发现的错误数为 B 2 τ = τ 1 时两个测试员发现的相同错误数为 b c \tau=0时错误总数为B_0\\\tau=\tau_1时测试员甲发现的错误数为B_1\\\tau=\tau_1时测试员乙发现的错误数为B_2\\\tau=\tau_1时两个测试员发现的相同错误数为b_c τ=0时错误总数为B0τ=τ1时测试员甲发现的错误数为B1τ=τ1时测试员乙发现的错误数为B2τ=τ1时两个测试员发现的相同错误数为bc

则估计出的测试前的程序的错误总数为: B = B 2 b c B 1 B=\frac{B_2}{b_c}B_1 B=bcB2B1,则可用 B B B的平均值作为 E T E_T ET的估计值。

8.维护

这个阶段是软件生命周期的最后一个阶段,其最基本任务是保证软件在一个相当长的时期能够正常运行。

软件维护需要的工作量很大,平均来说,大型软件的维护成本高达开发成本的4倍左右。

软件工程学的主要目的就是提高软件的可维护性,降低维护的代价。

8.1软件维护的定义

软件在交付使用之后,为了改正错误或满足新的需要而修改软件的过程。

改正性维护:因为软件测试不可能暴露出一个大型软件系统中所有潜藏的错误,所以必然会有第一项维护活动:在任何大型程序的使用期间,用户必然会发现程序错误,并且把他们遇到对的问题报告给维护人员。

适应性维护:配合变化的硬件环境而进行的修改软件活动。

完善性维护:在使用软件过程中用户往往提出增加新功能或修改已有功能建议,还可能提出一般性的改进意见,为了满足这类要求,所需的维护称之为完善性维护。

预防性维护:为了改进未来的可维护性,或为了给未来的改进奠定更好的基础而修改软件。

8.2软件维护的特点

8.2.1结构化维护与非结构化维护差别巨大

1.非结构化维护

软件配置的唯一内容是程序代码,程序内部文档不足,没有测试方面的文档,不可能进行回归测试,将导致非结构化维护需要付出很大代价,这种维护方式是没有使用良好定义的方法学开发出来的软件的必然结果。

2.结构化维护

具有完整的软件配置存在,维护工作从评价设计文档开始,按照测试说明书包含的信息进行回归测试。

8.2.2维护的代价高昂

维护费用。修改可能引起的用户不满。潜藏错误降低软件质量。开发过程可能混乱。

8.2.3维护的问题很多

1.理解别人写的程序通常非常困难,而且困难程度随着软件配置成分的减少而迅速增加。

2.需要维护的软件往往没有合格的文档,或者文档资料显著不足。

3.当要求对软件进行维护时,不能指望由开发人员给人们仔细说明软件。

4.绝大多数软件在设计时没有考虑将来的修改。

5.软件维护不是一项吸引人的工作。

8.3软件维护过程

软件维护过程本质上修改和压缩了的软件定义和开发过程,而且事实上远在提出一项维护要求之前,与软件维护有关的工作已经开始了。

1.维护组织

虽然通常并不需要建立正式的维护组织,即使对于一个小的软件开发团队而言,非正式地委托责任也是绝对必要的。在维护活动开始之前就应该明确维护责任。

2.维护报告

应该用标准化的格式表达所有软件维护要求。

软件组织内部制定一个软件修改报告,信息如下:

1.满足维护要求表中提出的要求所需的工作量。

2.维护要求的性质。

3.这项要求的优先次序。

4.与修改有关的事后数据。

3.维护的事件流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LaycWpoi-1666617584402)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\维护阶段的事件流.png)]

4.保存维护记录

对于软件生命周期的所有阶段而言,以前记录保存都是不充分的,而软件维护则根本没有记录保存下来。由于这个原因,往往不能估价维护技术的有效性,不能确定一个产品程序的"优良"程度,而且很难确定维护的实际代价是什么。

5.评价维护活动

1.每次从程序运行平均失效的次数。

2.用于每一类维护活动的总人数。

3.平均每个程序、煤种语言、每种维护类型所做的程序变动数。

4.维护过程中增加或删除一个源语句平均花费的人时数。

5.维护每种语言平均花费的人时数。

6.一张维护要求表的平均周转时间。

7.不同维护类型所占的百分比。

8.4软件的可维护性

定义为:维护人员理解、改正、改动或改进这个软件的难易程度。

提高可维护性的措施:

1.建立明确的软件质量目标。

2.使用先进的软件开发技术和工具。

3.建立明确的质量保证。

4.选择可维护的程序设计语言。

5.改进程序文档。

8.4.1决定软件可维护性的因素

1.可理解性

2.可测试性

诊断和测试的容易程度取决于软件容易理解的程度。维护人员应该能够得到在开发阶段用过的测试方案,以便进行回归测试。

3.可修改性

耦合、内聚、信息隐藏、局部化、控制域与作用域关系等,都影响软件的可修改性。

4.可移植性

程序从一种计算机环境转移到另一种计算环境的难易程度。

5.可重用性

8.4.2文档

文档是影响软件可维护性的决定因素。软件系统的文档分为用户文档和系统文档两类。用户文档主要描述系统功能和使用方法,并不关心这些功能是怎样实现的;系统文档描述系统设计、实现和测试等各方面的内容。不管是哪一类文档都必须和程序代码同时维护,只有和程序代码完全一致的文档才是真正有价值的文档。

8.4.3可维护性复审

代码复审应该强调编码风格和内部说明文档这两个影响可维护性的因素。

8.5预防性维护

1.反复多次地做修改程序的尝试。

2.通过仔细分析程序尽可能多地掌握程序内部工作细节,以便更有效地修改它。

3.在深入理解原有设计的基础上,用软件工程方法重新设计、重新编码和测试哪些需要变更的软件部分。

4.以软件工程方法学为指导,对程序全部重新设计、重新编码和测试。这中方法也成为软件再工程。

8.6软件再工程过程

预防性维护实质上是软件再工程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xeByUc3N-1666617584403)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\软件再工程过程模型.png)]

1.库存目录分析

每个软件组织都应该保存其拥有的所有应用系统的库存目录。该目录包含关于每个应用系统的基本信息。

2.文档重构

老程序固有的特点是缺乏文档。

3.逆向工程

软件的逆向工程是分析程序以便在比源代码更高的抽象层次上创建出程序的某种表示过程,也就是说,逆向工程是一个恢复设计结果的过程,逆向工程工具从现存的程序代码中抽取有关数据、体系结构和处理过程的设计信息。

4.代码重构

代码重构是最常见的再工程活动。某些老程序具有比较完整、合理的体系结构,但是,个体模块的编码方式确实难于理解、测试和维护的。在这种情况下,可以重构可疑模块的代码。

5.数据重构

对数据体系结构差的程序很难进行适应性修改和增强,事实上,对许多应用系统来说,数据体系结构比源代码本身对程序的的长期生存力有更大影响。

6.正向工程

正向工程也称为革新或改造,这项活动不仅从现有程序中恢复设计信息,而且使用该信息去改变或重构现有系统,以提高其整体质量。

9.面向对象方法学引论

9.1面向对象方法学概述

9.1.1面向对象方法学要点

出发点和基本原则:尽可能模拟人类习惯的思维方式,使开发软件的方法与过程尽可能接近人类认识世界解决问题的方法与过程,也就是使描述问题的问题空间与实现解法的解空间在结构上尽可能一致。

从本质上说,用计算机解决客观世界的问题,是借助于某种程序设计语言对的规定,对计算机中的实体施加某种处理,并用处理结果去映射解。

9.1.2面向对象方法学的优点

1.与人类习惯的思维方法一致

2.稳定性好

3.可重用性好

4.较易开发大型软件产品

5.可维护性好

1.面向对象的软件稳定性比较好。

2.面向对象的软件比较容易修改。

3.面向对象的软件比较容易理解。

4.易于测试和调试。

9.2面向对象的概述

9.2.1对象

1.对象的形象表示

2.对象的定义

3.对象的特点

1.以数据为中心。

2.对象是主动的。

3.实现了数据封装。

4.本质上具有并行性。

5.模块独立性好。

9.2.2其他概念

1.类

类是具有相同属性和行为相似的对象的抽象。

2.实例

实例就是由某个特定的类所描述的一个具体的对象。

3.消息

消息就是要求某个对象执行在定义它的那个类中所定义的某个操作的规格说明。

4.方法

方法就是对象所能执行的操作,也就是类中所定义的具体服务。

5.属性

属性就是类中所定义的数据,它是对客观世界实体所具有的性质的抽象。

6.封装

不能从外面直接访问或修改内部数据或代码。

7.继承

继承是子类自动地共享基类的数据和方法的机制。

8.多态性

子类对象可以像父类对象那样使用,同样的消息既可以发送给父类对象也可以发送给子类对象。

9.重载

9.3面向对象建模

用面向对象方法开发软件,通常需要建立三种形式的模型,它们分别是描述系统数据结构的对象模型,描述系统控制结构的动态模型,以及描述系统功能的功能模型。

而所谓的模型则是为了理解事物而对事物做出的一种抽象,是对事物的一种无歧义的书面描述。

9.4对象模型

对象模型是模拟客观世界实体的对象以及对象彼此间的关系的映射。

9.4.1类图的基本符号

1.定义类

UML中类的图形符号为长方形,用两条横线把长方形分为上、中、下三个区域,从上至下依次为名字、属性和服务。

2.定义属性

UML描述属性的语法格式如下:

可见性 属性名:类型=初值{性质串}

属性的可见性通常有三种:共有的(public)、私有的(private)、受保护的(protected),分别用加号(+)、减号(-)和井号(#)表示。

用花括号括起来的性质明确地列出该属性所有可能的取值。也可以用性质串说明属性的其他性质,例如,约束说明{只读}表明该属性是只读属性。

类变量在类图中表示为下划线的属性。比如:- 图书馆书籍数目:Integer

3.定义服务

服务就是操作,UML描述操作的语法格式如下:

可见性 操作名(参数表):返回值类型{性质串}

参数表语法格式如下:

参数名:类型名=默认值

9.4.2表示关系的符号

1.关联

关联表示两个类的对象之间存在某种语义上联系。

1.普通关联

普通关联是最常见的关联关系,只要在类于类之间存在连接关系就可以用普通关联表示。普通关联的符号是连接两个类之间的直线。例如:作家使用计算机。一个作者使用至少一台计算机,而一台计算机可被零至多个作家使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ij8vfqf9-1666617584404)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\普通关联示例.png)]

黑三角是为了避免混淆。

重数表示该类有多少个对象与对方的一个对象连接,表示方法通常有:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sBNxShq3-1666617584404)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\普通关联重数表示方法.png)]

2.关联的角色

3.限定关联:限定关联通常用在一对多或多对多的关联关系中,可以把模型的重数从一对多变为一对一,或从多对多简化为多对一。

例如,某操作系统中一个目录下有多个文件,一个文件仅属于一个目录,在一个目录内一个文件名确定唯一一个文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VaKBzyDA-1666617584405)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\受限关联示例.png)]

4.关联类

为了说明关联的性质,可能需要一些附加信息。

2.聚集

聚集也称聚合,是关联的特例。聚集表示类与类之间的关系是整体与部分的关系。在陈述需求时使用的"包含"、“组成”、"分为……部分"等字句,往往意味着存在聚集关系。

1.共享聚集

如果在聚集关系中处于部分方的对象可同时参与与多个处于整体方对象的构成,则该聚集称之为共享聚集。

例如:一个课题组包含许多成员,每个成员又可以是另一个课题组的成员,则课题组合成员之间就是共享聚集关系。

空心菱形画在表示关联关系的支线末端紧挨着整体类的地方。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V3UzCNvM-1666617584406)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\共享聚集示例.png)]

2.组合聚集

如果部分类完全隶属于整体类,部分与整体共存,整体不存在了部分也会随之消失,则该聚集称为组合聚集(简称组成)。

实心菱形表示组合聚集关系。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SXm8obsS-1666617584406)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\组合聚集示例.png)]

3.泛化

UML中泛化关系就是通常所说的继承关系,它是通用元素和具体元素之间的一种分类关系。具体元素完全拥有通用元素的信息,并且还可以附加一些其他信息。

在UML中,用一端为空心三角形的连接表示泛化关系,三角形的顶角紧挨着通用元素。

1.普通泛化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25E8LAjc-1666617584407)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\普通泛化示例.png)]

2.受限泛化

可以给泛化关系附加约束条件,以进一步说明该泛化关系的使用方法或扩充方法,这样的泛化关系成为受限泛化。预定义的约束有四种:多重、不相交、完全和不完全。

多重继承指的是,一个子类可以同时多次继承同一个上层基类。如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YWMcj0p-1666617584408)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\多重继承示例.png)]

与多重继承相反的是不相交继承,如果图中没有指定{多重}约束,则是不相交继承,一般的继承都是不相交继承。

完全继承指的是父类的所有子类都已在类图中穷举出来了,图示符号是指定{完全}约束。

不完全继承与完全继承恰好相反,父类的子类并没有都穷举出来,随着对问题理解的深入,可不断补充和维护,这为日后系统的扩充和维护带来很大方便。不完全继承是一般情况下默认的继承关系。

4.依赖和细化

1.依赖关系

依赖关系描述两个模型元素之间的语义连接关系:其中一个模型元素是独立的,另一个模型元素不是独立的,它依赖于独立的模型元素,如果独立的模型元素改变了,将影响依赖于它的模型元素。例如,一个类使用另一个类的对象作为操作的参数,一个类用另一个类的对象作为它的数据成员,一个类向另一个类发消息等,这样的两个类之间都存在依赖关系。

在UML的类图中,用带箭头的虚线连接有依赖关系的两个类,箭头指向独立的类。

2.细化关系

当对同一个事物在不同层次上描述时,这些描述之间具有细化关系。加入两个模型元素A和B描述同一个事物,它们的区别是抽象层次不同,如果B是在A的基础上的更详细的描述,则称B细化了A,或称A细化成了B。

细化的图示符号为由元素B指向元素A的、一端为空心三角形的虚线。

示例如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RpaPv8Z2-1666617584408)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\依赖和细化关系示例.png)]

9.5动态模型

每个类的动态行为用一张状态图来描述,各个类的状态图通过共享事件合并起来,从而构成系统的动态模型。

详细参看3.6状态图。

9.6功能模型

通常,功能模型由一组数据流图组成。

9.6.1用例图

UML提供的用例图也是进行需求分析和建立功能模型的强有力工具。而一幅用例图包含的模型元素有系统、行为者、用例及用例之间的关系。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CAtjngIR-1666617584409)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\用例图示例-1.png)]

1.系统

系统被看作是一个提供用例的黑盒子,内部如何工作、用例如何实现,这些对于建立用例模型来说都是不重要的。代表系统的方框的边线表示系统的边界,描述该系统功能的用例置于方框内,代表外部实体的行为置于方框外。

2.用例

一个用例是可以被行为者感受到的、系统的一个完整的功能。

3.行为者

行为者是指与系统交互的人或其他系统,它代表外部实体。

在用例图中用直线连接行为者和用例,表示两者之间交换信息。

4.用例之间的关系

1.扩展关系

向一个用例中添加一些动作后构成了另一个用例,这两个用例之间的关系就是扩展关系,后者继承前者的一些行为,通常把后者成为扩展用例。

2.使用关系

当一个用例使用另一个用例时,这两个用例之间就构成了使用关系。

如下图展示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dMUgRDl8-1666617584410)(D:\Temp\新建文件夹\文件\软件工程导论第六版张海藩\用例图示例-2.png)]

注意扩展与使用之间的异同:这两种关系都意味着从几个用例抽取那些公共的行为并放入已单独的用例中,而这个用例被其他用例使用或扩展,但是,使用和扩展的目的是不同的。

9.6.2用例建模

几乎在任何情况下都需要使用用例,通过用例可以获取用户的需求,规划和控制项目。获取用例是需求分析阶段的主要工作之一,而且是首先要做的工作。

1.寻找行为者

谁将使用系统的主要功能(主行为者)?

谁要来维护和关系系统(副行为者)?

2.寻找用例

行为者需要系统提供哪些功能?行为者自身需要做什么?

行为者是否需要读取、创建、删除、修改或存储系统中的某类信息?

系统需要哪些输入输出?输入来自何处?输出到哪里去?

9.7三种模型之间的关系

功能模型指明了系统应该"做什么";动态模型明确规定了什么时候(即在何种状态下接收了什么事件的触发)做;对象模型则定义了做事情的实体。

扼要简要的叙述三种模型之间的关系:

1.针对每个类建立的动态模型,描述了类实例的生命周期或运行周期。

2.状态转换驱使行为发生,这些行为在数据流图中被映射成处理,在用例图中被映射成用例,它们同时与类图中的服务相对应。

3.功能模型中的处理(或用例)对应于对象模型中的类所提供的服务。有时一个处理(或用例)对应多个服务,也有一个服务(或用例)对应多个处理的时候。

4.数据流图中的数据存储,以及数据的源点/终点,通常是对象模型中的对象。

5.数据流图中的数据流,往往是对象模型中对象的属性值,也可能使整个对象。

6.用例图中的行为者,可能是对象模型中的对象。

7.功能模型中的处理(或用例)可能产生动态模型中的事件。

8.对象模型描述了数据流图中的数据流、数据存储以及数据源点/终点的结构。

10.面向对象分析

不论采用哪种方法开发软件,分析过程都是提取需求的过程。

10.1面向对象的分析过程

10.1.1概述

面向对象分析,就是抽取和整理用户需求并建立问题域精确模型的过程。

10.1.2三个子模型与五个层次

面向对象建模包含对象模型、动态模型和功能模型。

复杂问题的对象模型通常由从上之下五个层次组成:主题层、类与对象层、结构层、属性层和服务层。主题是指导读者理解大型、复杂模型的一种机制。

面向对象分析大体上按照下列顺序进行:寻找类与对象,识别结构,识别主题,定义属性,建立动态模型,建立功能模型,定义服务。

10.2需求陈述

10.2.1书写要点

需求陈述的内容包括问题范围,功能需求,性能需求,应用环境及假设条件等。书写需求陈述时,要尽力做到语法正确,而且应该慎重选用名词、动词、形容词和同义词。不能将实际需求和设计决策混为一谈。

10.3建立对象模型

10.3.1确定类与对象

1.找出候选的类与对象

对象是对问题中有意义的事物的抽象,它们既可能是物理实体,也可能是抽象概念。通常分为下述五类:

1.可感知的物理实体,例如,飞机、汽车、房屋等。

2.人或组织的角色,例如,医生、雇主、雇员、计算机系、财务部等。

3.应该记忆的事件,例如,飞行、演出、访问、交通事故等。

4.两个或多个对象的相互作用,通常具有交易或接触的性质,例如,购买、纳税等。

5.需要说明的概念,例如,政策、保险政策、版权法等。

非正式分析:以自然语言书写的需求陈述作为依据,陈述中的名词作为类与对象的候选者,用形容词作为确定属性的线索,把动词作为服务(操作)的候选者,最后详细分析筛选。

2.筛选出正确的类与对象

筛选主要依据下列标准,删除不正确或不必要的类与对象:

1.冗余:如果两个类表述了相同的信息,则应该保留在此问题域中最富于描述力的名称。

2.无关:现实世界存在许多对象,不能把它们都纳入到系统中去,仅需要把与本问题密切相关的类与对象放进目标系统中。

3.笼统:在需求陈述中常常使用一些笼统的、泛指的名词,虽然在初步分析时把它们作为候选的类与对象列出来了,但是,要么系统无需记忆有关他们的信息,要么在需求陈述中有更明确更具体的名词对应它们所暗示的事务,因此,通常把这些笼统的或模糊的类去掉。

4.属性

在需求陈述中有些名词实际上描述的是其他对象的属性,应该把这些名词从候选类与对象中去掉。除非,如果某个性质具有很强的独立性,则应把它作为类而不是作为属性。

5.操作

在需求陈述中有时可能使用一些既可作为名词,又可作为动词的词,应该慎重考虑它们在本问题中的含义,以便正确地决定把它们作为类还是作为类中定义。总之,本身具有属性需独立存在的操作,应该作为类与对象。

6.实现

在分析阶段不应该过早的考虑怎样实现目标系统。因此,应该去掉仅和实现有关的候选的类与对象。在设计和实现阶段,这些类与对象可能是重要的,但在分析阶段过早地考虑它们反而会分散人们的注意力。

10.3.2确定关联

在需求陈述中使用的描述性动词或动词词组,通常表示关联关系。

1.初步确定关联

1.直接提取动词短语得出的关联。

2.需求陈述中隐含的关联。

3.根据问题域知识得出的关联。

2.筛选

经初步分析得出的关联只能作为候选的关联, 还需经过进一步筛选,以去掉不正确或不必要的关联。筛选时遵循以下标准:

1.已删去的类之间的关联:如果在分析确定类与对象的过程中已经删掉了某个候选类,则与这个类有关的关联也应该删去,或用其他类重新表达这个关联。

2.与问题无关的或应在是现阶段考虑的关联:应该把处于在本问题域之外的关联或与实现密切的关联删去。

3.瞬时事件:关联应该描述问题域的静态结构,而不应该是一个瞬时事件。

4.三元关联:三个或三个以上对象之间的关联,大多可以分解为二元关联或用词组描述成限定的关联。

5.派生关联:应该去掉那些可以用其他关联定义的冗余关联。

3.进一步完善

1.正名:好的名字是帮助读者理解的关键因素之一。

2.分解:为了能够适用于不同的关联,必要时应该分解以前确定的类与对象。

3.补充:发现遗漏的关联应该及时补上。

4.标明重数:应该初步判定各个关联的类型,并粗略的确定关联的重数。

10.3.3划分主题

在开发大型、复杂系统的过程中,为了降低复杂程度,人们习惯于把系统在进一步划分成几个不同的主题,也就是在概念上把系统包含的内容分成若干个范畴。

在开发很小的系统时,可能根本无需引入主题层;对于含有较多对象的系统,则往往识别出类与对象和关联,然后划分主题,并用它作为指导开发者和用户观察整个模型的一种机制;对于规模极大的系统,则首先由高级分析员粗略的识别对象和关联,然后初步划分主题,经进一步分析,对系统结构有更深入的了解之后,再进一步修改和精炼主题。

10.3.4确定属性

属性是对象的性质,借助于属性人们能对类与对象和结构有更深入更具体的认识。

一般来说,确定属性的过程包括分析和选择两个步骤:

1.分析

通常,在需求陈述中用名词词组表示属性,例如,“汽车的颜色"或"光标的位置”。往往用形容词表示可枚举的具体属性,例如,“红色的"或"打开的”。但是,不可能在需求陈述中找到所有属性,分析员还必须借助于邻域知识和常识才能分析得出需要的属性。

2.属性

认证考察经过初步分析而确定下来的那些属性,从中删掉不正确的或不必要的属性。

1.误把对象当做属性:如果某个实体的独立存在比它的值更重要,则应把它作为一个对象而不是对象的属性。

2.误把关联类的属性当做一般对象的属性:如果某个性质依赖于某个关联链的存在,则该性质是关联类的属性,在分析阶段不应该把它作为一般对象的属性。

3.把限定误当成属性

4.误把内部状态当成了属性:如果某个性质是对象的非公开的内部状态,则应该从对象模型中删掉这个属性。

5.过于细化:在分析阶段应该忽略那些对大多数操作都没有影响的属性。

6.存在不一致的属性:类应该是简单而且一致的。

参考《软件工程导论》第六版 张海藩

图片参考

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值