本书笔记目录链接
上篇
第2章 软件工程基础知识
“软件工程”概念在1968年的“软件危机”会议中提出。
IEEE 在1983年对软件工程的定义:软件工程是开发、运行、维护和修复软件的系统方法。
电气和电子工程师协会,IEEE,Institute of Electrical and Electronics Engineers
软件工程专家 Boehm 在1983年提出了软件工程的7条基本原理:
- 用分阶段的生命周期计划严格管理
- 坚持进行阶段评审
- 实行严格的产品控制
- 采用现代程序设计技术
- 结果应能清楚地审查
- 开发小组的人员应该少而精
- 承认不断改进软件工程实践的必要性
软件工程方法学包含3个要素:方法、工具和过程。
- 方法:完成软件开发的各项任务的技术方法。
- 工具:为运用方法而提供的软件工程支撑环境。
- 过程:为获得高质量的软件所需要完成的一系列任务的框架。
本章要点:
- 软件需求分析与定义;
- 软件设计、测试与维护;
- 软件复用;
- 软件质量保证及质量评价;
- 软件配置管理;
- 软件开发环境;
- 软件过程管理。
2.1 软件需求分析与定义
根据 Standish Group 对23000个项目进行的研究结果表明,28%的项目彻底失败,46%的项目超出经费预算或者超出工期,有26%的项目获得成功。而在这些高达74%的不成功项目中,有60%的失败是源于需求问题。
2.1.1 软件需求与需求过程
1. 什么是软件需求
软件需求就是系统必须完成的事,以及必须具备的品质。包括:
功能需求
产品必须执行的动作。非功能需求
必须具备的属性或品质,如可靠性、性能、扩展性等。设计约束
限制条件、补充规约,通常是对解决方案的约束说明。
其他相关概念:
业务需求(Business Requirement)
反映组织机构或客户对系统、产品高层次的目标要求。用户需求(User Requirement)
指描述用户使用产品必须要完成什么任务,怎么完成的需求。系统需求(System Requirement)
指从系统的角度来说明软件的需求。
2. 需求工程
需求工程是一个包括创建和维护系统需求文档所必需的一切活动的过程,通常包括:
-
需求开发
需求开发对于需求工程而言重于需求管理。包括四个阶段:
- 需求捕获
- 需求分析
- 编写规格说明书
- 需求验证
-
需求管理
包括:
- 定义需求基线
- 处理需求变更
- 需求跟踪
2.1.2 需求调查与问题定义
想做好需求调查必须清楚了解的3个问题:
- What:搜集什么信息
- Where:从什么地方搜集这些信息
- How:用什么机制或技术搜集这些信息
1. 要捕获的信息
从宏观的角度看,要捕获的信息包括三类:
- 与问题域相关的信息(如业务资料、业务处理流程等)
- 与要求解决的问题相关的信息
- 用户对系统的特别期望与施加的任何约束信息
2. 信息的来源
对涉众(风险承担人、项目干系人)进行分类,从每一类涉众中找1~2名代表。
3. 需求捕获技术
常用技术:
-
用户访谈
- 准备问题:围绕想要获取的信息展开,设计一系列的问题,按顺序组织起来。预先准备好记录方式。
- 访谈时的技巧:注意措辞,保持气氛轻松,语言通俗化,访谈前对相关知识的准备保证理解与认识。
- 应该询问的问题:自己的问题是否相关?回答是否正式?对方是否为回答此问题合适人选?是否问了过多的问题?询问被访者还希望自己问他什么问题,应该见哪些人?
用户调查
缺点:缺乏灵活性,反馈的信息不全面。
补足:用户调查与用户访谈结合使用。先调查,整理分析后进行小范围的用户访谈。现场观摩
文档考古
-
联合讨论会
参与人数6~18人,召开时间1 ~ 5小时。
会议流程:- 提前将与讨论主题相关的资料分发给会议参与人
- 与会者互相认识
- 针对所列举的问题进行逐项专题讨论
- 对原有系统、类似系统的不足进行开放性交流
- 大家对新的解决方案的设想
记录会议中讨论的想法、问题、不足,形成要点清单,针对清单进行整理,明确优先级并进行评审。
4. 需求捕获的策略
需求过程4阶段(需求捕获、需求分析、需求规格 、需求验证)采用迭代的方式进行。
2.1.3 可行性研究
1. 可行性研究工作的基础
问题定义的关键是清晰地界定出问题内容、性质,以及系统的目标、规模等内容,并形成完整的书面报告。
2. 可行性研究工作的任务
- 技术可行性
- 经济可行性
- 社会可行性
3. 可行性研究工作的步骤
核实问题定义与目标
具体来说,就是仔细阅读问题定义的相关材料,对该问题所涉及的领域知识进行学习、考证,然后通过走访相关人员进行验证与核实。
这一步骤的关键目标是:使问题定义更加清晰、明确、没有歧义性,并且对系统的目标、规模,以及相关约束与限制条件做出更加细致的定义,确保可行性研究小组的所有成员达成共识。研究分析现有系统
“现有系统” 不仅包括旧的软件系统,还包括旧的非计算机系统。为新系统建模
建模的目的是为了获得一个对新系统的框架认识、概念性认识。
常见技术:
- 系统上下文关系范围图
- 实体-关系图
- 用例模型
- 域模型
- IPO 表
客户复核
提出并评价解决方案
应该尽量列举出各种可行的解决方案,并且对这些解决方案的优点、缺点做一个综合性的评价,以便下一步决策。确定最终推荐的解决方案
成本效益分析:
成本估计
估算工作量的工具:历史数据与经验模型(功能点分析,COCOMO 分析等)。效益分析
在效益分析之前应该首先对该系统应用之后,将会来的直接、间接收益,以及成本降低的具体数额进行量化,并且通过经济学的相关模型来进行分析。
常见概念:
- 货币的时间价值
- 投资回收期
- 纯收入
- 投资回报率
- 草拟开发计划
- 以书面的形式提交《可行性分析报告》并进行审查。
2.1.4 需求分析
定义:所谓分析就是通过对问题域的研究,获得对该领域特性及存在于其中(需要解决)的问题特性的透彻理解并用文档说明。
1. 需求分析的工作任务
Karl E.Wiegers 在《软件需求》中指出需求分析工作通常包括7个方面。
绘制系统上下文范围关系图
用于定义系统与系统外部实体间的界限和接口的简单模型,它可以为需求确定一个范围。DFD 的0层图。创建用户接口原型
分析需求的可行性
确定需求的优先级
需求的优先级可以采用满意度/非满意度指标进行说明(取值均为1 ~ 5)。为需求建立模型
创建数据字典
使用质量功能配置(QFD)
质量功能配置,QFD,Quality Function Deployment
2. 需求建模
Alan Davis 主张需要把用文字表示的需求和用图形表示的需求结合起来。用图形表示的需求,就是需求建模,获得分析模型。分析模型有助于检测需求的不一致性、模糊性、错误及遗漏。
2.1.5 流行的需求分析方法论
按照分解方式不同分类:
- 结构化分析方法
结构化分析方法,SA,Structured Analysis
- 软系统方法
过渡性的方法论,并未真正流行过。以 Checkland 方法为代表。
- 面向对象分析方法
面向对象分析方法,OOA,Object Oriented Analysis
- 面向问题域的分析
面向问题域的分析,PDOA,Problem Domain Oriented Analysis
尚处在研究阶段,并未广泛应用。
小镭:今天情况怎样?
1. 结构化分析
把系统看作一个过程的集合体。
工具:
- 数据流图
- 数据字典
- 结构化语言
- 判定表
- 判定树
结构化系统分析方法从总体上看是一种强烈依赖数据流图的自顶向下的建模方法。
如何进行结构化分析:
研究“物质环境”
画出当前系统的数据流图。建立系统逻辑模型
在第一步的基础上,画出相对真实系统的等价逻辑数据流图。划清人机界限
2. 数据流图
数据流图,DFD,Data Flow Diagram
基本元素:
- 过程
- 外部实体
- 数据存储
- 数据流
- 实时连接
过程执行时,外部实体与过程之间的来回通信。
对过程0的分解,称之为 DFD 0层图。
3. 细化记录 DFD 部件
- 结构化语言
- 决策表和决策树
- 数据字典
每条目包含信息:- 名称
- 何处使用/如何使用
- 内容描述
- 补充信息
数据字典实例:
客户基本信息=客户编号+客户名称+身份证号码+手机
客户编号={0···9}8
客户名称={字}4
身份证号码=[{0···9}15|{0···9}18]
手机=[{0···9}11|{0···9}12]
4. 实体-关系图
实体-关系图,E-R 图,Entity Relationship Diagram
实体是一个概念,用来抽象地表示一组相类似的事物的所有实例。
结构化分析的不足:
- 对问题域的研究力度不够大
- 分析与设计之间缺乏清晰界限
- 没有一个真正的功能规格说明
- 需求实质上是根据满足该需求的某一特定系统的内部设计来加以说明的
- 内部设计的开发使用的则是不可靠的内部设计技术-功能分解
- 不适用于很多类型的应用
小镭:或许结构化分析过于抽象,这加大了分析阶段的复杂性,使问题的描述、阅读、沟通变得困难。
5. 面向问题域的分析
面向问题域的分析更多地强调描述,而较少强调建模。
分析过程:
- 搜集基本的信息并开发问题框架,以建立问题域的类型
- 在问题框架类型的指导下,进一步搜集详细信息并给出一个问题域相关特性的描述
2.2 软件设计
2.2.1 软件设计基本原则
1. 信息隐蔽
Parnas 指出:每个模块的实现细节对于其他模块来说是隐蔽的,模块中所包含的信息(包括数据和过程)不允许其他不需要这些信息的模块使用。这提高了软件的可维护性和可靠性。
2. 模块独立性
指软件系统中每个模块只涉及软件要求的具体子功能,而和软件系统中其他的模块接口是简单的。
度量模块独立性的两个准则:
耦合
模块之间联系紧密程度的度量。内聚
模块内部各个元素彼此结合的紧密程度的度量。
高内聚,低耦合的模块独立性较强。
高 《--- 内聚性 --- 低
功能内聚 | 信息内聚 | 通信内聚 | 过程内聚 | 时间内聚 | 逻辑内聚 | 巧合内聚
强 《--- 模块独立性 --- 弱
功能单一 ------ 功能分散
各类聚合性:
功能内聚
模块中所有部分都是为了完成一项具体功能而协同工作,紧密联系,不可分割的。信息内聚
模块所完成的各个功能都在同一数据结构上操作,每一项功能有一个唯一的入口点。通信内聚
如果一个模块内各功能部分都使用了相同的输入数据,或产生了相同的输出数据,则称之为通信内聚模块。过程内聚
把流程图中的某一部分划出组成模块,就得到过程内聚模块。时间内聚
模块的各个功能的执行与时间有关。逻辑内聚
组合相关功能。巧合内聚
又称偶然内聚,指模块内部的联系很松散。
低 --- 耦合性 ---》 高
非直接耦合 | 数据耦合 | 标记耦合 | 控制耦合 | 外部耦合 | 公共耦合 | 内容耦合
强 《--- 模块独立性 --- 弱
各类耦合性:
非直接耦合
模块之间没有直接联系。数据耦合
模块之间通过简单数据参数来交换输入、输出信息。标记耦合
一组模块通过参数表传递记录信息。控制耦合
一个模块通过传送开关、标志、名字等控制信息控制选择另一模块的功能。外部耦合
一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息。公共耦合
一组模块都访问同一个公共数据环境。内容耦合
一个模块直接访问另一个模块的内部数据;一个模块不通过正常入口转到另一模块内部;一个模块有多个入口。
2.2.2 结构化设计方法
实施过程:
- 总结出系统应有的功能,对一个功能,从功能完成的过程考虑,将各个过程列出,标志出过程转向和传递的数据。
- 细化数据流。
- 分析过程之间的耦合关系,合理地划分模块,提高内聚性。
1. 系统结构图中的模块
在系统结构图中,不能再分解的底层模块称为原子模块。
完全因子分解的系统:系统的全部实际加工都由原子模块来完成,而其他所有非原子模块仅仅执行控制协调功能。这是理想中的系统。
结构图中的常见模块:
- 传入模块
- 传出模块
- 变换模块
- 协调模块
区别系统结构图与程序流程图
结构图,SC,Structured Charts
结构图反映模块间的隶属关系(调用与层次),着眼软件系统的总体结构,并不涉及模块内部的细节。
流程图反映的是程序的执行顺序及其依赖条件,表达执行程序的具体算法。
有太多项目失败就是因为它们没有明确的目标就开始了。
2. 系统结构图中的主要成分
- 模块
- 模块间的调用关系
- 模块间的通信
- 辅助控制符号
3. 常用的系统结构图
变换型系统结构图
从外部取得数据,处理后再传回外部。事务型系统结构图
数据被转换成一个事务项并加以计算,然后根据结果选择数据流。混合型系统结构图
现实中多为此类。
2.2.3 用户界面设计
一个好的用户界面应具有的特点:
- 可使用性
- 灵活性
- 复杂性和可靠性
2.2.4 设计评审
评审采用评审会议的形式进行。
角色:
设计负责人
- 承担项目的全部设计管理任务
- 对设计质量、进度及各项设计间的组织协调等全面负责
- 对各单项工程之间的衔接、协调和总体方案质量负主要责任
- 负责编写总说明,汇编总概算。
高级管理人员
- 定主审员
- 审批评审记录
主审员
- 在评审会前提出项目的书面评审意见
- 确定评审组、确定评审结果并填写评审记录
评审组
- 专业评审组评委表决通过项目初评结论并报综合评审会议,通过设计报告。
2.3 软件测试
应当把“尽早地和不断地进行软件测试”作为软件开发者的座右铭。
软件测试并不等于程序测试。软件测试应贯穿于软件定义与开发的整个期间。各阶段所得到的文档以及源程序,都应成为软件测试的对象。
2.3.1 软件测试
测试用例是为特定目标开发的测试输入、执行条件和预期结果的集合。
1. 黑盒测试
又称为功能测试或数据驱动测试。把测试对象看做一个空盒子,不考虑程序的内部逻辑结构和内部特性,依据程序的需求规格说明书,检查程序的功能是否符合它的功能说明。
主要检查内容:
- 功能的正确性与完整性
- 输入输出接口的正确性
- 数据结构的正确性
- 性能极其稳定性
测试用例设计方法:
-
等价类划分
步骤:- 划分等价类
- 选取测试用例
等价类是指某个输入域的子集合。在该子集合中,各个输入数据对揭露程序中的错误是等效的。
有效等价类与无效等价类。
边界值分析
经验表明,大量的错误是发生在输入或输出范围的边界上。错误推测法
靠经验和直觉推测程序中可能存在的各种错误,有针对性地编写测试用例。-
因果图
此方法最终生成的就是判定表,适于检查程序输入条件的各种组合情况。步骤:
- 分析原因(输入)与结果(输出)并标示编号因果组
- 分析原因与结果之间的关系,画出因果图
- 在因果图上标明不可能的情况并指出约束或限制条件
- 把因果图转换成判定表
- 利用判定表的每一列设计测试用例
2. 白盒测试
又称为结构测试或逻辑驱动测试。白盒测试把测试对象看做一个透明的盒子,它允许测试人员利用程序内部的逻辑结构和有关信息,设计或选择测试用例,对程序所有逻辑路径进行测试。
主要检查内容:
- 对程序模块的所有独立执行路径至少测试一次
- 对所有的逻辑判定,取 “真” 与取 “假” 两种情况都至少测试一次
- 在循环的边界和运行界限内执行循环体
- 测试内部数据结构的有效性
3. 逻辑覆盖
此技术属于白盒测试,是以程序内部的逻辑结构为基础的设计用例的技术。
包括:
语句覆盖
设计若干个测试用例,使每一可执行语句至少执行一次。判定覆盖
设计若干个测试用例,使程序中每个判断的取真分支和取假分支至少经历一次,又称为分支覆盖。条件覆盖
设计若干个测试用例,使程序中每个判断的每个条件的可能取值至少执行一次。判定-条件覆盖
设计足够的测试用例,使判断中每个条件的所有可能取值至少执行一次,每个判断中的每个条件的可能取值至少执行一次。条件组合覆盖
设计足够的测试用例,使判断的所有可能的条件取值组合至少执行一次。路径覆盖
设计足够的测试用例,覆盖程序中所有可能的路径。
2.3.2 软件测试策略
1. 单元测试
也称为模块测试,是针对每个模块进行的测试。
驱动模块是指在单元测试和集成测试中,协调输入和输出的测试程序。
桩模块指模拟被调用单元的程序。
单元测试可测试内容:
- 模块接口
-
局域数据结构
数据类型,变量,初始值与默认值。 -
独立路径
路径错误。 - 错误处理测试
- 边界条件测试
2. 集成测试
把模块组装为系统的方式有两种:
- 一次性组装方式
- 增殖式组装方式
对关键模块及早进行测试。
3. 确认测试
验证软件的功能、性能及其他特性是否与用户的要求一致。
主要步骤:
- 有效性测试
- 软件配置复査
- 验收测试
应交付文档:
- 确认测试分析报告
- 最终的用户手册和操作手册
- 项目开发总结报告
4. 系统测试
在实际运行环境下进行一系列的测试。
5. α 测试和 β 测试
α 测试
α 测试是由一个用户在开发环境或模拟实际操作环境下进行的测试。
α 测试目的:评价软件产品的 FLURPS(功能、局域化、可使用性、可靠性、性能、支持)
α 测试注重产品的界面和特色。
β 测试
β 测试是由多个用户在实际使用环境下进行的测试。
β 测试处在整个测试的最后阶段。产品的所有手册文本也应该在此阶段完全定稿。
2.3.3 软件测试类型
1. 功能测试
2. 可靠性测试
主要评价指标:
平均故障间隔时间,MTBF,Mean Time Between Failure
平均故障修复时间,MTTR,Mean Time To Repair
3. 强度测试
4. 性能测试
5. 恢复测试
6. 启动/停止测试
7. 配置测试
8. 安全性测试
9. 可使用性测试
10. 安装测试
11. 过程测试
检查由人工完成的、为了配合计算机的工作,就是过程测试。
12. 容量测试
13. 文档测试
14. 兼容性测试
2.3.4 面向对象的软件测试
面向对象的开发阶段:
- 面向对象分析,OOA,Object Oriented Analysis
- 面向对象设计,OOD,Object Oriented Design
- 面向对象编程,OOP,Object Oriented Programming
面向对象测试:
1. OOA Test
OOA 直接映射问题空间,全面地将问题空间中实现功能的现实抽象化。将问题空间中的实例抽象为对象,用对象的结构反映问题空间的复杂实例和复杂关系,用属性和服务表示实例的特性和行为。行为相对稳定,结构则相对不稳定。
2. OOD Test
OOD 以 OOA 为基础归纳类,并建立类结构或进一步构造成类库,实现分析结果对问题空间的抽象。
因 OOD 是 OOA 的进一步细化和更高层的抽象。所以,OOD 与 OOA 的界限通常难以区分。
3. OOP Test
考虑:
- 数据成员是否满足数据封装的要求
- 类是否实现了要求的功能
4. 面向对象的单元测试
考虑:
- 继承的成员函数是否都不需要测试
- 对父类的测试是否能照搬到子类
5. 面向对象的集成测试
基于单元测试对成员函数行为正确性的保证,集成测试只关注系统的结构和内部的相互作用。
静态测试:检测程序结构是否符合设计要求。
动态测试:优化测试用例,减少测试工作量,使测试达到一定的覆盖标准。
6. 面向对象的系统测试
2.4 软件维护
2.4.1 软件的可维护性
1. 软件具有可维护性
决定软件可维护性的三个因素:
- 可理解性
- 可测试性
- 可修改性
2. 采用软件工程提高软件的可维护性
软件系统的文档可分为用户文档和系统文档两大类。
用户文档主要是描述软件功能和使用方法,至少包括:
- 功能说明
- 安装文档
- 用户使用手册
- 参考手册
- 管理员指南
系统文档关心实现细节,描述系统需求、设计、实现和测试等各个方面。
3. 注重可维护性的开发过程
在开发过程中提高软件的可维护性:
- 在需求分析阶段,应该对将来要改进的和可能会修改的部分加以明确说明
- 在设计阶段,应该尽量遵循“ 高内聚低耦合” 的模块设计原则
- 在编码阶段,应该采用科学的代码规范
- 在测试阶段,应重视测试相关文档的的编写
- 在维护阶段,要有严格的配置管理,同步更新相关系统文档
4. 可维护性的度量
1. 外部度量
MTTR 指标度量。
可维护指标 M = 1 / (1 + MTTR)
2. 内部度量
软件复杂性相关因素:
- 环路数
- 软件规模
- 其他因素
建立经验模型的步骤:
- 确定影响可维护性的若干主要因素,并为其制订尺度
- 用大量的现有案例,计算出一个包含影响因素及其权值的多项式模型
- 利用这个经验模型分析新的软件,再根据实际的维护活动的感受进行校准
- 重复校准过程
2.4.2 软件维护的分类
从性质上分为:
- 纠错型维护(21%)
- 适应型维护(25%)
- 预防型维护(4%)
- 完善型维护(50%)
注:括号中为 Lientz 和 Swanson 在1980年的调查结果。
2.4.3 软件维护的工作量
维护活动分类:
- 生产类
- 非生产类(熟悉代码,理解结构等)
M = P + K^(c-d)
M - 维护总工作量
P - 生产类活动工作量
K - 经验常数
c - 软件复杂程度
d - 维护人员对软件的熟悉程度
影响维护工作的其他因素:
- 维护工作本身是否规范,是否按软件工程的正确方法进行
- 软件系统的类型不同,维护工作量也有区别
按照对真实世界的依赖程度,软件系统可以分为:抽象系统、近似系统和模拟系统。
- 硬件因素
2.4.4 软件维护作业的实施和管理
1. 建立维护组织
2. 提出维护需求
3. 实施维护作业
每次维护活动的实施都要经历如下的步骤:
- 确认维护需求
- 制订维护计划
- 编码
- 测试
- 交付用户
4. 记录维护要素
5. 评价维护活动
2.4.5 软件再生工程
软件的再生工程通常包括以下六类活动:
- 筛选
- 文档重构
- 逆向工程
- 代码重构
- 数据重构
- 重新开发
2.5 软件开发环境
2.5.1 软件开发环境概述
软件开发环境,SDE,Software Development Environment
集成式项目支援环境,IPSE,Integrated Project Support Environment
集成型开发环境通常可由工具集和环境集成机制两部分组成。
环境集成机制:
- 数据集成机制
数据集成机制提供统一的数据模式和数据接口规范,需要相互协作的工具通过这种统一的模式与规范交换数据。
- 控制集成机制
控制集成机制支持各工具或各开发活动之间的通信、切换、调度和协同工作,并支持软件开发过程的描述、执行和转接。
- 界面集成机制
2.5.2 软件开发环境的功能与分类
按软件开发模型及开发方法分类:
- 瀑布模型
- 演化模型
- 螺旋模型
- 喷泉模型
- 结构化方法
- 信息模型方法
- 面向对象方法
按功能及结构特点分类:
- 单体型
- 协同型
- 分散型
- 并发型
按应用范围分类:
- 通用型
- 专用型
按开发阶段分类:
- 前端开发环境
- 后端开发环境
- 软件维护环境
- 逆向工程环境
2.5.3 软件开发环境的结构
开发环境组成:
- 工具集
- 集成机制
- 环境信息库
环境信息库是软件开发环境的核心,用以储存与系统开发有关的信息并支持信息的交流与共享。
- 过程控制和消息服务器
- 环境用户界面
开发环境的结构可分为四层:
- 宿主层
包括基本宿主硬件和基本宿主软件。
- 核心层
包括工具组、环境数据库和会话系统。
- 基本层
包括至少一组工具,如编译工具、调试工具等。
- 应用层
以基本层为基础补充某些工具,以适应应用软件的要求。
2.5.4 软件开发环境的发展
集成计算机辅助软件设计,ICASE,Integrated Computer-Aided Software Engineering
ICASE 信息中心库功能:
- 数据完整性
- 信息共享
- 数据-工具集成
- 数据-数据集成
- 方法学实施
- 文档标准化
ICASE 的最终目标是实现应用软件的全自动开发,即开发人员只要写好软件的需求规格说明书,软件开发环境就自动完成从需求分析开始的所有的软件开发工作,自动生成供用户直接使用的软件及有关文档。