概述

软件工程是指应用计算机科学、数学及管理科学等原理,以工程化的原则和方法来解决软件问题的工程,其目的是提高软件生产率、提高软件质量、降低软件成本。

软件工程学:

  • 软件开发技术
    • 软件开发方法学
    • 软件工具
    • 软件工程环境
  • 软件工程管理
    • 软件工程管理学
    • 软件经济学

软件工程涉及软件开发、维护、管理等多方面的原理、方法、工具与环境。

计算机软件

计算机软件是指:

  • 计算机系统中的程序:计算任务的处理对象和处理规则的描述。

    • 计算任务:任何以计算机为处理工具的任务。

    • 处理对象:

      • 数据:如数字、文字、图形、图像、声音等,它们只是表示,而无含义。
      • 信息:数据及有关的含义。
    • 处理规则:一般指处理的动作和步骤。

  • 程序文档:文档是为了便于了解程序所需的阐述性资料。

按照软件的应用领域,可以将计算机软件分为十大类:

  • 系统软件
  • 应用软件
  • 工程/科学软件
  • 嵌入式软件
  • 产品线软件
  • Web应用
  • 人工智能软件
  • 开放计算
  • 网络资源
  • 开源软件

软件生存周期

  1. 可行性分析与项目开发计划
  2. 需求分析
  3. 概要设计
  4. 详细设计
  5. 编码
  6. 测试
  7. 维护

软件过程模型

软件开发中所遵循的路线图(一系列可预测的步骤)称为“软件过程”。过程是活动的集合,活动是任务的集合。

软件过程有3层含义:

  • 个体含义
  • 整体含义
  • 工程含义

能力成熟度模型

软件过程能力成熟度模型(Capability Maturity Model of Software,CMM)是对软件组织进化阶段的描述,随着软件组织定义、实施、测量、控制和改进其软件过程,软件组织的能力经过这些阶段逐步提高。

能力成熟度模型使软件组织能够较容易地确定其当前过程的成熟度并识别其软件过程执行中的薄弱环节,确定对软件质量和过程改进最为关键的几个问题,从而形成对其过程的改进策略。

CMM将软件过程改进分为以下5个成熟度级别:

  1. 初始级(Initial):

    软件过程的特点是杂乱无章,有时甚至很混乱,几乎没有明确定义的步骤。

  2. 可重复级(Repeatable):

    建立了基本的项目管理过程和实践来跟踪项目费用、进度和功能特性;建立了有必要的过程准则来重复以前在同类项目中的成功。

  3. 已定义级(Defined):

    管理和工程两方面的软件过程已经文档化、标准化,并综合成整个软件开发组织的标准软件过程。所有项目都采用根据实际情况修改后得到的标准软件过程来开发和维护软件。

  4. 己管理级(Managed):

    制定了软件过程和产品质量的详细度量标准。软件过程和产品质量都被开发组织的成员所理解和控制。

  5. 优化级(Optimized):

    加强了定量分析,通过来自过程质量反馈和来自新观念、新技术的反馈使过程能不断持续地改进。

能力成熟度模型集成

能力成熟度模型集成(CMMI)是若干过程模型的综合和改进,是支持多个工程学科和领域的、系统的、一致的过程改进框架,能适应现代工程的特点和需要,能提高过程的质量和工作效率

CMMI提供了两种表示方法:

  • 阶段式模型

    结构类以于CMM,它关注组织的成熟度

    CMMI-SE/SW/IPPD 1.1版中有5个成熟度等级:

    1. 初始的:过程不可预测且缺乏控制。
    2. 己管理的:过程为项目服务。
    3. 已定义的:过程为组织服务。
    4. 定量管理的:过程已度量和控制。
    5. 优化的:集中于过程改进。
  • 连续式模型

    关注每个过程域的能力,一个组织对不同的过程域可以达到不同的过程域能力等级(Capability Level,CL)。

    能力等级包括:

    • 共性目标
    • 共性实践

    能力等级可以独立地应用于任何单独的过程域,任何一个能力等级都必须满足比它等级低的能力等级的所有准则。

    CMMI中包括6个过程域能力等级(0~5):

    1. $CL_0$(未完成的):
      过程域未执行未得到$CL_1$中定义的所有目标
    2. $CL_1$(已执行的):
      其共性目标是过程将可标识的输入工作产品转换成可标识的输出工作产品,以实现支持过程域的特定目标
      关注于过程域的特定目标的完成
    3. $CL_2$(已管理的):
      其共性目标集中于已管理的过程的制度化
      关注于针对单个过程实例的能力
    4. $CL_3$(已定义级的):
      其共性目标集中于已定义的过程的制度化
      关注于过程的组织级标准化和部署
    5. $CL_4$(定量管理的):
      其共性目标集中于可定量管理的过程的制度化
      使用测量和质量保证来控制和改进过程域,建立和使用关于质量和过程执行的定量目标作为管理准则。
    6. $CL_5$(优化的):
      使用量化(统计学)手段改变和优化过程域,以满足客户要求的改变和持续改进计划中的过程域的功效。
      其共性目标集中于优化的过程的制度化,表明过程得到很好地执行持续得到改进

瀑布模型

瀑布模型(Waterfall Model)是将软件生存周期中的各个活动规定为依线性顺序连接的若干阶段的模型(它规定了由前至后、相互衔接的固定次序,如同瀑布流水逐级下落),包括:

  • 需求分析
  • 设计
  • 编码
  • 测试
  • 运行与维护(运维)

瀑布模型

瀑布模型假设:一个待开发的系统需求是完整的、简明的、一致的,而且可以先于设计和实现完成之前产生

瀑布模型:

  • 优点:

    • 容易理解;
    • 管理成本低;
    • 每个阶段都有对应的成果产物;
    • 各个阶段有明显的界限划分和顺序需求;
    • 强调开发的阶段性早期计划及需求调查和产品测试。
  • 缺点:

    • 需要客户能够完整、正确和清晰地表达自己的需要;

    • 在开始的两个或3个阶段中,很难评估真正的进度状态;

    • 当接近项目结束时,出现了大量的集成和测试工作;

    • 直到项目结束之前,都不能演示系统的能力;

    • 一旦发生错误,整个项目要推到重新开始。

      需求或设计中的错误往往只有到了项目后期才能够被发现,对于项目风险的控制能力较弱,从而导致项目常常延期完成,开发费用超出预算。

瀑布模式适合用于:

  • 开发需求明确的,需求大致固定且不会随意变更的系统;
  • 开发人员对软件的应用领域很熟悉;
  • 开发工作对用户参与的要求很低。

V模型

V模型式是瀑布模型的一个变体,描述了质量保证活动和沟通、建模相关活动以及早期构建相关的活动之间的关系

V模型

  • 设计、开发:随着软件团队工作沿着V模型左侧步骤向下推进基本问题需求逐步细化,形成问题及解决方案的技术描述。

  • 测试:一旦编码结束,团队沿着V模型右侧的步骤向上推进

    其实际上是执行了一系列测试(质量保证活动),这些测试验证了团队沿着V模型左侧步骤向下推进过程中所生成的每个模型

V模型是一种测试的开发模型,强调测试贯穿项目的始终,而不是集中在测试阶段。V模型提供了一种将验证确认活动(测试)应用于早期软件工程工作中的方法。

增量模型

增量模型(Incremental Model)融合了瀑布模型的基本成分原型实现的迭代特征

增量模型假设可以将需求分段为一系列增量产品,每一增量可以分别开发。该模型采用随着日程时间的进展而交错的线性序列,每一个线性序列产生软件的一个可发布的“增量”:

增量模型

  • 从分析到测试为一个增量的过程(线性序列)。
  • 核心功能先完成:第1个增量往往是核心的产品。
  • 客户对每个增量的使用和评估都作为下一个增量发布的新特征和功能,这个过程在每一个增量发布后不断重复,直到产生了最终的完善产品。
  • 增量模型强调每一个增量均发布一个可操作的产品

增量模型作为瀑布模型的一个变体,具有瀑布模型的所有优点。此外,它还有以下优点:

  • 第一个可交付版本所需要的成本和时间很少
  • 开发由增量表示的小系统所承担的风险不大
  • 由于很快发布了第一个版本,因此可以减少用户需求的变更
  • 优先级高的功能先交付,使得重要的功能经历更多的测试。
  • 运行增量投资,即在项目开始时,可以仅对一个或两个增量投资。

缺点:

  • 如果没有对用户的变更要求进行规划,那么产生的初始增量可能会造成后来增量的不稳定
  • 如果需求不像早期思考的那样稳定和完整,那么一些增量就可能需要重新开发、重新发布
  • 管理发生的成本、进度和配置的复杂性可能会超出组织的能力

量模型适合用于:

  • 需要快速构造可运行的产品的项目(对完成期限严格要求的产品);
  • 进行已有产品升级或新版本开发;
  • 对所开发的领域比较熟悉而且已有原型系统。

演化模型

演化模型(Evolutionary Model)演化模型是迭代的过程模型,使得软件开发人员能够逐步开发出更完整的软件版本。演化模型特别适用于对软件需求缺乏准确认识的情况

典型的演化模型有原型模型和螺旋模型等。

原型模型

并非所有的需求都能够预先定义。大量的实践表明,在开发初期很难得到一个完整的、准确的需求规格说明。原因有:

  • 客户往往不能准确地表达对未来系统的全面要求,导致形成的需求规格说明不完整、不准确,甚至是有歧义。
  • 在整个开发过程中,用户可能会产生新的要求,导致需求的变更

瀑布模型难以适应这种需求的不确定性和变化,于是出现了快速原型(Rapid Prototype)这种新的开发方法。

原型模型(Prototype Model):

  • 适合于用户需求不清需求经常变化的情况;
  • 不适合大规模系统的开发

原型的目的是能快速、低成本地构建原型系统

能够采用原型方法是因为开发工具的快速发展,使得能够迅速地开发出一个让用户看得见、摸得着的系统框架。这样,对于计算机不是很熟悉的用户就可以根据这个框架提出自己的需求。

开发原型系统首先确定用户需求,开发初始原型,然后征求用户对初始原型的改进意见,并根据意见修改原型:

原型模型

  1. 交流:目的是定义软件的总体目标,标识需求,然后
  2. 快速计划:快速制订原型开发的计划,确定原型的目标和范围。
  3. 采用快速设计方式进行建模。
  4. 构建原型。
  5. 部署交付和反馈:被开发的原型应交付给客户使用,并收集客户的反馈意见,这些反馈意见可在下一轮中对原型进行改进。
  6. 下一轮迭代:在前一个原型需要改进,或者需要扩展其范围的时候,进入下一轮原型的迭代开发。

根据使用原型的目的不同,原型可以分为:

  • 探索型原型:目的是要弄清目标的要求,确定所希望的特性,并探讨多种方案的可行性。
  • 实验型原型:目的是验证方案或算法的合理性,是在大规模开发和实现前,用于考查方案是否合适、规格说明是否可靠等。
  • 演化型原型:目的是将原型作为目标系统的一部分,通过对原型的多次改进,逐步将原型演化成最终的目标系统。

螺旋模型

对于复杂的大型软件,开发一个原型往往达不到要求。

螺旋模型将瀑布模型和演化模型结合起来加入了两种模型均忽略的风险分析(以风险为驱动),弥补了这两种模型的不足。

螺旋模型将开发过程分为几个螺旋周期,每个螺旋周期大致和瀑布模型相符合:

螺旋模型

每个螺旋周期分为如下4个工作步骤:

  1. 制订计划
    1. 确定软件的目标
    2. 选定实施方案
    3. 明确项目开发的限制条件
  2. 风险分析
    1. 分析所选的方案
    2. 识别风险
    3. 消除风险
  3. 实施工程
    1. 实施软件开发
    2. 验证阶段性产品
  4. 用户评估
    1. 评价开发工作,提出修正建议
    2. 建立下一个周期的开发计划

螺旋模型属于面向对象开发模型。

螺旋模型适用于:

  • 庞大、复杂并且具有高风险的系统
  • 新近开发,需求不明的情况。

优点:

  • 螺旋模型强调风险分析,使得开发人员和用户对每个演化层出现的风险有所了解,从而做出应有的反应。
  • 螺旋模型支持用户需求的动态变化,有助于提高软件的适应能力,降低了软件开发的风险。

缺点:

  • 需要开发人员具有相当丰富的风险评估经验和专门知识。
  • 过多的迭代次数会增加开发成本,延迟提交时间。

喷泉模型

喷泉模型(Water Fountain Model):

  • 以用户需求为动力;
  • 以对象作为驱动;
  • 适合于面向对象。

喷泉模型克服了瀑布模型不支持软件重用和多项开发活动集成的局限性。其开发过程如下:

喷泉模型

喷泉模型使开发过程具有以下性质或特点:

  • 迭代性:意味着模型中的开发活动常常需要重复多次,在迭代过程中不断地完善软件系统。

  • 无间隙性:指在开发活动(如分析、设计、编码)之间不存在明显的边界。

    喷泉不像瀑布模型那样,在需求分析活动结束后才开始设计活动,在设计活动结束后才开始编码活动,而是允许各开发活动交叉、迭代地进行

    喷泉模型的各个阶段没有明显的界线,开发人员可以同步进行

  • 支持软件重用。

优点:可以提高软件项目的开发效率,节省开发时间。

缺点:

  • 由于喷泉模型在各个开发阶段是重叠的,在开发过程中需要大量的开发人员,不利于项目的管理
  • 喷泉模型要求严格管理文档,使得审核的难度加大

统一过程模型

统一过程(UP)模型开发过程的特征:

  • 用例和风险驱动。

  • 以架构为中心。

  • 迭代并且增量:

    迭代:将整个软件开发项目划分为许多个小的“袖珍项目”。

    每个“袖珍项目”都包含正常软件项目的所有元素:

    • 计划
    • 分析和设计
    • 构造
    • 集成和测试
    • 内部和外部发布

统一过程模型由UML(统一建模语言,Unified Modeling Language)方法和工具支持。

统一过程定义了4个技术阶段及其制品,这4个技术阶段由主要里程碑所终止:

  1. 起始阶段(Inception Phase):

    确定项目的风险及其优先次序,并对精化阶段进行详细规划和对整个项目进行粗略计算。

    专注于:项目的初创活动。

    产生的主要工作产品有:

    • 构想文档(Vision Document)
    • 初始用例模型
    • 初始项目术语表
    • 初始业务用例
    • 初始风险评估
    • 项目计划(阶段及迭代)业务模型
    • 一个或多个原型(需要时)

    里程碑:生命周期目标。

  2. 精化阶段(Elaboration Phase):

    根据主要的用例描述设计出详细的系统构架。主要是解决用例、构架和计划是否足够稳定可靠,风险释放得到充分控制,以便能够按照合同的规定完成整个开发任务。

    精华阶段在理解了最初的领域范围之后进行:

    • 需求分析
    • 架构演进

    关注于:需求分析和架构演进。

    产生的主要工作产品有:

    • 用例模型

    • 补充需求:包括非功能需求

    • 分析模型

    • 软件体系结构描述

    • 可执行的软件体系结构原型

    • 初步的设计模型

    • 修订的风险列表

    • 项目计划:

      包括:

      • 迭代计划
      • 调整的工作流
      • 里程碑
      • 技术工作产品
    • 初始用户手册

    里程碑:生命周期目标。

  3. 构建阶段(Construction Phase):

    将设计实现,并进行测试。

    关注系统的构建,产生实现模型。

    产生的主要工作产品有:

    • 设计模型
    • 软件构件
    • 集成的软件增量
    • 测试计划及步骤
    • 测试用例
    • 支持文档:
      • 用户手册
      • 安装手册
      • 对于并发增量的描述

    里程碑:初始运作功能。即一个准备交到最终用户手中的产品。

  4. 移交阶段(Transition Phase):

    交由用户测试并迭代完善。

    关注于软件提交方面的工作,产生软件增量。

    产生的主要工作产品有:

    • 提交的软件增量
    • $\beta$测试报告
    • 综合用户反馈

    里程碑:产品发布。

    其实还有一个产生阶段,这里包含在了移交阶段中。

在每次迭代中有5个核心工作流:

  • 需求工作流:捕获系统应该做什么;
  • 分析工作流:精华和结构化需求;
  • 设计工作流:在系统架构内实现需求;
  • 实现工作流:构造软件;
  • 测试工作流:验证实现是否如期望那样工作。

统一过程的典型代表是RUP(Rational Unified Process)。

RUP是UP的商业扩展,完全兼容UP,但比UP更完整、更详细。

敏捷开发

敏捷开发(Agile Development)的总体目标是通过“尽可能早地、持续地对有价值的软件的交付”使客户满意。通过在软件开发过程中加入灵活性,敏捷方法使用户能够在开发周期的后期增加或改变需求

敏捷过程的典型方法有很多,每一种方法基于一套原则,这些原则实现了敏捷方法所宣称的理念(敏捷宣言)

敏捷开发有以下几种方法:

  • 极限编程(XP)
  • 水晶法(Crystal)
  • 并列争求法(Scrum)
  • 自适应软件开发(ASD)
  • 敏捷统一过程(AUP)

极限编程

极限编程是为了降低需求变更所带来的成本,旨在提高软件质量和对客户需求变化的适应性,期望能够让软件开发达到低成本、低缺陷、高产出、高回报(最小投入得到最大结果)的效果。

极限编程(XP)软件开发方式有以下性质:

  • 轻量级(敏捷)
  • 高效
  • 低风险
  • 柔性
  • 可预测的
  • 科学的

XP由价值观、原则、实践和行为4个部分组成,他们之间彼此相互依赖、关联,并通过行为贯穿于整个生存周期:

  • 4大价值观:
    • 沟通
    • 简单性
    • 反馈
    • 勇气
  • 5个原则:
    • 快速反馈
    • 简单性假设
    • 逐步修改
    • 提倡更改
    • 优质工作
  • 12个最佳实践:
    • 计划游戏:快速制定计划、随着细节的不断变化而完善
    • 小型发布:系统的设计要能够尽可能早地交付
    • 隐喻:找到合适的比喻传达信息
    • 简单设计:只处理当前的需求,使设计保持简单
    • 测试先行:先写测试代码,然后再编写程序
    • 重构:重新审视需求和设计,重新明确地描述它们以符合新的和现有的需求
    • 结对编程
    • 集体代码所有制
    • 持续集成:可以按日甚至按小时为客户提供可运行的版本
    • 每周工作40个小时
    • 现场客户
    • 编码标准

敏捷统一过程

敏捷统一过程(Agile Unified Process,AUP)采用以下原理来构建软件系统:

  • “在大型上连续”
  • “在小型上迭代”。

采用经典的UP阶段性活动(初始、精化、构建和转换),提供了一系列活动,能够使团队为软件项目构想出一个全面的过程流。

在每个活动里,一个团队迭代使用敏捷,并将有意义的软件增量尽可能快地交付给最终用户。每个AUP迭代执行以下活动:

  • 建模:建立对商业和问题域的模型表述,这些模型“足够好”即可,以便团队继续前进。
  • 实现:将模型翻译成源代码。
  • 测试:像XP一样,团队设计和执行一系列的测试来发现错误以保证源代码满足需求。
  • 部署:对软件增量的交付以及获取最终用户的反馈。
  • 配置及项目管理:着眼于变更管理、风险管理以及对团队的任一制品的控制。项目管理追踪和控制开发团队的工作进展并协调团队活动。
  • 环境管理:协调标准、工具以及适用于开发团队的支持技术等过程基础设施。

其他敏捷开发方法

水晶法

  • 认为每一个不同的项目都需要一套不同的策略、约定和方法论。

  • 认为人对软件质量有重要的影响。

    随着项目质量和开发人员素质的提高,项目和过程的质量也随之提高。

  • 通过更好地交流和经常性的交付,软件生产力得到提高。

并列争求法

使用迭代的方法。

  • 把每30天一次的迭代称为一个“冲刺”。
  • 按需求的优先级别来实现产品。
  • 多个自组织和自治的小组并行地递增实现产品。
  • 协调是通过简短的日常情况会议来进行,就像橄榄球中的“并列争球”。

自适应软件开发

有6个基本原则:

  • 有一个使命作为指导;
  • 特征被视为客户价值的关键点;
  • 过程中的等待是很重要的,因此“重做”与“做”同样关键;
  • 变化不被视为改正,而是被视为对软件开发实际情况的调整;
  • 确定的交付时间迫使开发人员认真考虑每一个生产的版本的关键需求;
  • 风险也包含其中。

总结

能力成熟度模型(从1开始):

级别 说明
初始级 杂乱无章,几乎没有明确定义的步骤。
可重复级 建立基本的项目管理过程和实践来跟踪项目费用、进度和功能特性。
已定义级 将管理和工程文档化、标准化并综合成标准软件过程;
使用标准开发过程(或方法论)构建(或集成)系统。
己管理级 对软件过程和产品质量制定了的详细度量标准,且有定量的理解和控制。
优化级 加强了定量分析,通过过程质量和新观念、新技术使过程不断地改进。

能力成熟度集成连续式模型(从0开始):

能力等级 目标
未完成的 未执行或未得到等级1中的所有目标。
已执行的 可标识的输入工作产品到输出工作产品的转换,实现特定目标。

关注:特定目标的完成。

已管理的 已管理的过程的制度化。

关注:针对单个过程实例的能力。

已定义级的 已定义的过程的制度化。

关注:过程的组织级标准化和部署。

定量管理的 可定量管理的过程的制度化。

说明:使用测量和质量保证来控制和改进。

优化的 优化的过程的制度化。

说明:使用量化手段改变和优化。

各开发模型的适用范围:

模型 说明 适用范围
瀑布模型 将软件生存周期中的活动定为线性顺序链接的阶段模型 需求明确、大致固定且变更少
V模型 瀑布模型的变体,强调测试贯穿项目的始终,是一种测试的开发模型 需求明确、低风险
增量模型 融合瀑布模型和原型迭代,核心功能先完成,每轮迭代都会有新的增量,核心功能得到充分测试,强调每个增量均发布一个可操作的产品 快速构造可运行的产品,产品升级,领域熟悉或已有原型
演化模型 迭代的过程模型,需求无法被完整定义,功能在使用过程中不断完善 对软件需求缺乏准确认识的情况
原型模型 原型开发方法模型,目的是快速、低成本地构建原型系统 需求不清或多变、领域陌生;不适合大规模系统
螺旋模型 结合瀑布和演化模型,强调引入风险分析,属于面向对象开发模型 庞大、复杂、高风险的系统,开发人员有丰富的风险评估经验和知识
喷泉模型 面向对象模型,特点是迭代、无间隙和支持重用,各阶段无明显界限,可迭代交叉 面向对象的开发过程
统一过程 用例驱动、以架构为中心、迭代和增量

统一过程模型阶段里程碑和关注点总结:

阶段 里程碑 关注
初始阶段 生命周期目标 项目的初创活动
精化阶段 生命周期架构 需求分析和架构演进
构建阶段 初始运作功能 系统的构建
移交阶段 产品发布 软件提交方面的工作

需求分析

软件需求

需求分析主要的目的是弄清楚系统需要“做什么”,并将其阐述出来。

软件需求是指用户对目标软件系统在功能、行为、性能、设计约束等方面的期望。通常,这些需求包括:

  • 功能需求:考虑系统要做什么,在何时做,在何时以及如何修改或升级。

  • 性能需求:考虑软件开发的技术性指标。

    例如:

    • 存储容量限制;
    • 执行速度;
    • 响应时间;
    • 吞吐量。
  • 用户或人的因素:考虑用户的类型。

    例如:

    • 各种用户对使用计算机的熟练程度,需要接受的训练;
    • 用户理解、使用系统的难度;
    • 用户错误操作系统的可能性。
  • 环境需求:考虑未来软件应用的环境,包括硬件和软件。

    • 对硬件设备的需求包括:机型、外设、接口、地点、分布、湿度、磁场干扰等;
    • 对软件的需求包括:操作系统、网络、数据库等。
  • 界面需求

    考虑以下方面:

    • 来自其他系统的输入;
    • 到其他系统的输出;
    • 对数据格式的特殊规定;
    • 对数据存储介质的规定。
  • 文档需求:考虑需要哪些文档,文档针对哪些读者。

  • 数据需求

    考虑以下方面:

    • 输入、输出数据的格式;
    • 接收、发送数据的频率;
    • 数据的准确性和精度;
    • 数据流量;
    • 数据需保持的时间。
  • 资源使用需求

    考虑以下方面:

    • 软件运行时所需要的数据、其他软件、内存空间等资源;
    • 软件开发、维护时,所需的人力、支撑软件、开发设备。
  • 安全保密要求

    考虑以下方面:

    • 是否需要对访问系统或系统信息加以控制;
    • 隔离用户数据的方法;
    • 用户程序如何与其他程序和操作系统隔离
    • 系统备份要求。
  • 可靠性要求

    考虑以下方面:

    • 系统的可靠性要求;
    • 系统是否必须检测和隔离错误;
    • 出错后,重启系统允许的时间。
  • 软件成本消耗与开发进度需求

    考虑以下方面:

    • 开发是否有规定的时间表;
    • 软/硬件投资有无限制。
  • 其他非功能性要求

    如采用某种开发模式,需要确定:

    • 质量控制标准;
    • 里程碑和评审;
    • 验收标准;
    • 各种质量要求的优先级;
    • 可维护性方面的要求。

软件需求的出处:

  • 可以来自于用户(实际的和潜在的)、用户的规约、应用领域的专家、相关的技术标准和法规;
  • 可以来自于原有的系统、原有系统的用户、新系统的潜在用户;
  • 可以来自于竞争对手的产品。

需求工程

需求工程可以细分为6个阶段:

  1. 需求获取
  2. 需求分析与协商
  3. 系统建模
  4. 需求规约
  5. 需求验证
  6. 需求管理

系统设计

进入设计阶段,需要把软件系统“做什么”的逻辑模型转换成“怎么做”的物理模型。即着手实现软件系统需求。

系统设计的主要目的就是为系统制定蓝图,在各种技术和实施方法中权衡利弊,精心设计,合理地使用各种资源,最终勾画出新系统的详细设计方案。

系统设计的主要内容包括:

  • 新系统总体结构设计
  • 代码设计
  • 输出、输入设计
  • 处理过程设计
  • 数据存储设计
  • 用户界面设计
  • 安全控制设计

常用的设计方法有以下两种:

  • 面向数据流的结构化设计方法(SD)。
  • 面向对象的分析方法(OOD)。

系统设计的基本任务大体上可以分为两个步骤:

  1. 概要设计
  2. 详细设计

概要设计

  1. 设计软件系统总体结构总体结构设计):

    其基本任务是:

    • 采用某种设计方法,将一个复杂的系统按功能划分成模块;
    • 确定每个模块的功能;
    • 确定模块之间的调用关系;
    • 确定模块之间的接口,即模块之间传递的信息;
    • 评价模块结构的质量。

    软件系统总体结构的设计是概要设计关键的一步,直接影响到下一个阶段详细设计与编码的工作。

    软件系统的质量及一些整体特性都在软件系统总体结构的设计中决定。

  2. 数据结构及数据库设计

    1. 数据结构设计:逐步细化的方法也适用于数据结构的设计。

      在需求分析阶段,己经通过数据字典对数据的组成、操作约束和数据之间的关系等方面进行了描述,确定了数据的结构特性。在概要设计阶段要加以细化,详细设计阶段则规定具体的实现细节。在概要设计阶段,宜使用抽象的数据类型。

    2. 数据库设计

      数据库的设计是指数据存储文件的设计,主要进行以下几方面设计:

      1. 概念设计:在数据分析的基础上,采用自底向上的方法从用户角度进行视图设计,一般用E-R模型来表述数据模型。

        E-R模型既是设计数据库的基础,也是设计数据结构的基础。

      2. 逻辑设计:E-R模型是独立于数据库管理系统(DBMS)的,要结合具体的DBMS特征来建立数据库的逻辑结构。

      3. 物理设计:对于不同的DBMS,物理环境不同,提供的存储结构与存取方法各不相同。

        物理设计就是设计数据模式的一些物理细节,如数据项存储要求、存取方法和索引的建立等。

  3. 编写概要设计文档

    文档主要有:

    • 概要设计说明书
    • 数据库设计说明书
    • 用户手册
    • 修订测试计划
  4. 评审

    对设计部分是否完整地实现了需求中规定的功能、性能等要求,设计方法的可行性,关键的处理及内外部接口定义的正确性、有效性、各部分之间的一致性等都一一进行评审。

详细设计

  1. 算法设计对每个模块进行详细的算法设计,用某种图形、表格和语言等工具将每个模块处理过程的详细算法描述出来

  2. 数据结构设计:对模块内的数据结构进行设计。

  3. 数据库设计:对数据库进行物理设计,即确定数据库的物理结构。

  4. 其他设计:

    根据软件系统的类型,还可能要进行以下设计:

    • 代码设计:为了提高数据的输入、分类、存储和检索等操作,节约内存空间,对数据库中某些数据项的值要进行代码设计。
    • 输入/输出格式设计
    • 用户界面设计
  5. 编写详细设计说明书

  6. 评审对处理过程的算法和数据库的物理结构都要评审。

系统设计的结果是一系列的系统设计文件,这些文件是物理实现一个信息系统(包括硬件设备和编制软件程序)的重要基础。


系统测试

系统测试是为了发现错误而执行程序的过程

  • 成功的测试:发现了至今尚未发现的错误的测试。
  • 测试的目的:希望能以最少的人力和时间发现潜在的各种错误和缺陷。

信息系统测试应包括:

  • 软件测试
  • 硬件测试
  • 网络测试

系统测试是保证系统质量和可靠性的关键步骤,是对系统开发过程的最后复查。

根据测试的概念和目的,在进行信息系统测试时应遵循以下基本原则:

  1. 应尽早并不断地进行测试。测试应贯穿在开发的各个阶段,应尽早纠正错误,消除隐患。

  2. 测试工作应该避免由原开发软件的人或小组承担。

    • 开发人员往往不愿否认自己的工作,总认为自己开发的软件没有错误;
    • 开发人员的错误很难由本人测试出来,很容易根据自己编程的思路来制定测试思路,具有局限性。
  3. 在设计测试方案时,不仅要确定输入数据,而且要根据系统功能确定预期输出结果。将实际输出结果与预期结果相比较就能发现测试对象是否正确。

  4. 在设计测试用例时,不仅要设计有效、合理的输入条件,也要包含不合理、失效的输入条件。

  5. 在测试程序时,不仅要检验程序是否做了该做的事,还要检验程序是否做了不该做的事。

  6. 严格按照测试计划来进行,避免测试的随意性。

    测试计划应包括:

    • 测试内容
    • 进度安排
    • 人员安排
    • 测试环境
    • 测试工具
    • 测试资料
  7. 妥善保存测试计划、测试用例,作为软件文档的组成部分,为维护提供方便。

  8. 测试例子都是精心设计出来的,可以为重新测试或追加测试提供方便。

系统测试阶段的测试目标来自于需求分析阶段

测试过程:

  1. 制定测试计划
  2. 编制测试大纲
  3. 根据测试大纲设计和生成测试用例
  4. 实施测试
  5. 生成测试报告

单元测试

单元测试也称为模块测试在模块编写完成且无编译错误后就可以进行。单元测试侧重于模块中的内部处理逻辑和数据结构。如果选用机器测试,一般用白盒测试法。这类测试可以对多个模块同时进行

单元测试主要检查模块的以下5个特征:

  • 模块接口:模块的接口保证了测试模块的数据流可以正确地流入、流出。

    在测试中应检查以下要点:

    • 测试模块的输入参数形式参数个数、属性、单位是否一致
    • 调用其他模块时,所给出的实际参数被调用模块的形式参数个数、属性、单位是否一致
    • 调用标准函数时,所用的参数在属性、数目和顺序上是否正确
    • 全局变量在各模块中的定义和用法是否一致。
    • 输入是否仅改变了形式参数。
    • 开/关的语句是否正确。
    • 规定的I/O格式是否与输入/输出语句一致。
    • 在使用文件之前是否已经打开文件或使用文件之后是否己经关闭文件。
  • 局部数据结构

  • 重要的执行路径

  • 出错处理

  • 边界条件

模块间存在调用与被调关系,对每个模块进行测试时,需要开发两种模块:

  • 驱动模块:接收测试例子的数据,将这些数据送到测试模块,输出结果。

    即模拟被测试模块的上一级模块,相当于被测模块的主程序。

  • 桩模块(存根模块):代替测试模块中所调用的子模块,其内部可进行少量的数据处理。目的是为了检验入口、输出调用和返回的信息。

    即模拟被测模块的子模块(所调用的模块),但不是软件产品的组成部分。

单元测试环境

提高模块的内聚度可以简化单元测试。

集成测试

集成测试就是把模块按系统设计说明书的要求组合起来进行测试。即使所有的模块都通过了测试,在集成之后,仍然可能出现问题:

  • 穿过模块的数据丢失;
  • 一个模块的功能对其他模块造成有害的影响;
  • 各个模块集成起来没有达到预期的功能;
  • 全局数据结构出现问题;
  • 单个模块的误差可以接受,但模块组合后,可能会出现误差累积,最后累积到不能接受的程度。

集成测试是进行一些旨在发现与接口相关的错误的测试,其目标是利用已通过单元测试的构件建立设计中描述的程序结构

通常,集成测试有两种方法:

  • 非增量集成:分别测试各个模块,再把这些模块组合起来进行整体测试。

    • 优点:可以对模块进行并行测试,能充分利用人力,并加快工程进度。
    • 缺点:容易混乱,出现错误不容易查找和定位。
  • 增量集成:以小增量的方式逐步进行构造和测试。

    增量式测试的范围一步步扩大,错误容易定位,更易于对接口进行彻底测试,并且可以运用系统化的测试方法。

增量集成策略有:

  • 自项向下集成测试:一种构造软件体系结构的增量方法。

    模块的集成顺序为从主控模块(主程序)开始,沿着控制层次逐步向下,以深度优先或广度优先的方式将从属于(或间接从属于)主控模块的模块集成到结构中。

    自顶向下集成

    深度优先集成首先集成位于程序结构中主控路径上的所有构件,也可以根据特定应用系统的特征进行选择。

    例如上图中,选择最左边的路径:

    1. 首先,集成构建$M_1$、$M_2$和$M_5$;
    2. 其次,集成$M_8$或$M_6$(若$M_2$的正常运行是必须的);
    3. 然后,集成中间和右边控制路径上的构建。

    广度优先集成首先沿着水平方向,将属于同一层的构建集成起来

    例如上图中:

    1. 首先,将构建$M_2$、$M_3$和$M_4$集成起来;
    2. 其次是$M_5$、$M_6$、$M_7$,依次类推。

    集成过程可以通过下列5个步骤完成:

    1. 主控模块用作测试驱动模块,用这些从属于主控模块的所有模块代替桩模块。
    2. 依靠所选择的集成方法(即深度优先或广度优先),每次用实际模块替换一个从属桩模块。
    3. 在集成每个模块后都进行测试。
    4. 在完成每个测试集之后,用实际模块替换另一个桩模块。
    5. 可以执行回归测试,以确保没有引入新的错误。

    回到第2步继续执行此过程,直到完成了整个程序结构的构造。

    自顶向下集成不需要驱动模块。

  • 自底向上集成测试:自底向上集成测试就是从原子模块(程序结构的最底层构件)开始进行构造和测试。

    由于构件是自底向上集成的,在处理时所需要的从属于给定层次的模块总是存在的,因此,没有必要使用桩模块。自底向上集成策略可以利用以下步骤来实现:

    1. 连接低层构件以构成完成特定子功能的簇。
    2. 编写驱动模块(测试的控制程序)以协调测试用例的输入和输出。
    3. 测试簇。
    4. 去掉驱动程序,沿着程序结构向上逐步连接簇。

    簇:一系列相关低层构建的集合。

    自底向上集成不需要桩模块。

    例如:

    自底向上

    1. 连接相应的构建形成簇1、簇2和簇3;
    2. 编写驱动模块(图中虚线上方连接的框,即$D_1$、$D_2$和$D_3$)
    3. 利用驱动模块对每个簇进行测试。
    4. 簇1和簇2中的构建从属于模块$M_a$,去掉驱动模块$D_1$和$D_2$,将这两个簇直接与$M_a$相连;簇3去掉驱动模块$D_3$,然后与$M_b$相连。
    5. 最后将$M_a$和$M_b$与构建$M_c$连接在一起。
  • 回归测试:重新执行己测试过的某些子集,以确保变更没有传播不期望的副作用。

    每当加入一个新模块作为集成测试的一部分时,软件发生变更,建立了新的数据流路径,可能出现新的/O,以及调用新的控制逻辑。这些变更可能会使原来可以正常工作的功能产生问题。

    回归测试有助于保证变更不引入无意识行为或额外的错误。回归测试的方法有:

    • 手工执行,重新执行所有测试用例的子集
    • 利用捕捉/回放工具自动执行

    回归测试要执行的测试子集包含以下3种测试用例:

    • 能够测试软件所有功能的具有代表性的测试样本
    • 额外测试,侧重于可能会受变更影响的软件功能
    • 侧重于已发生变更的软件构件测试

    随着集成测试的进行,回归测试的数量可能变得相当庞大,因此,应将回归测试用例设计成只包括每个主要程序功能的一个或多个错误类的测试

  • 冒烟测试:一种常用的集成测试方法,是时间关键项目的决定性机制,它让软件团队频繁地对项目进行评估。

    冒烟测试方法包括下列活动:

    1. 将已经转换为代码的软件构件集成到构建中。

      一个构建包括所有的:

      • 数据文件
      • 可复用的模块
      • 实现一个或多个产品功能所需的工程化构件
    2. 设计一系列测试以暴露影响构建正确地完成它的功能的错误,其目的是为了发现极有可能造成项目延迟的业务阻塞错误

    3. 每天将该构建与其他构建及整个软件产品(以其当前形势)集成起来进行冒烟测试。

      这种集成方法可以自顶向下,也可以自底向上。

测试方法

测试方法分为:

  • 静态测试

    指被测试程序不在机器上运行,而是采用以下手段对程序进行检测:

    • 人工检测:不依靠计算机而是依靠人工审查程序或评审软件。

      人工检测包括:

      • 代码检查
      • 静态结构分析
      • 代码质量度量
    • 计算机辅助静态分析

  • 动态测试:指通过运行程序发现错误。

    在对软件产品进行动态测试时可以采用以下两种测试方法:

    • 黑盒测试法
    • 白盒测试法

    测试用例由以下组成:

    • 测试输入数据
    • 预期输出结果:与测试输入数据对应的预期输出结果

    在设计测试用例时,应当包括:

    • 合理的输入条件
    • 不合理的输入条件

黑盒测试

黑盒测试也称为功能测试,在完全不考虑软件的内部结构和特性的情况下,测试软件的外部特性。

进行黑盒测试主要是为了发现以下几类错误:

  1. 是否有错误的功能或遗漏的功能?
  2. 界面是否有误?输入是否正确接收?输出是否正确?
  3. 是否有数据结构或外部数据库访问错误?
  4. 性能是否能多接受?
  5. 是否有初始化或终止性错误?

常用的黑盒测试技术有:

  • 等价类划分:将程序的输入域划分为若干等价类,然后从每个等价类中选取一个代表性数据作为测试用例。

    每一类的代表性数据在测试中的作用等价于这一类中的其他值,这样就可以用少量代表性的测试用例取得较好的测试效果

    等价类划分有两种不同的情况:

    • 有效等价类
    • 无效等价类

    在设计测试用例时,要同时考虑这两种等价类。

    定义等价类的原则如下。

    1. 输入条件规定了取值范围或值的个数的情况下,可以定义1个有效等价类和2个无效等价类
    2. 输入条件规定了输入值的集合或规定了“必须如何”的条件的情况下,可以定义1个有效等价类和一个无效等价类
    3. 在输入条件是一个布尔量的情况下,可以定义一个有效等价类和一个无效等价类
    4. 规定了输入数据的一组值(假定$n$个),并且程序要对每一个输入值分别处理的情况下,可以定义$n$个有效等价类和1个无效等价类
    5. 规定了输入数据必须遵守的规则的情况下,可以定义一个有效等价类(符合规则)和若干个无效等价类(从不同角度违反规则)
    6. 在确知己划分的等价类中,各元素在程序处理中的方式不同的情况下,则应将该等价类进一步划分为更小的等价类。

    例如,输入$x$的取值范围是$0 \sim 10$,输入$y$的取值范围是$-10 \sim -1$,那么可以定义三个等价类:

    • 有效等价类1:$x$的取值范围是$0 \sim 10$,输入$x$的取值范围是$-10 \sim -1$;
    • 无效等价类2:$x$的取值范围是$x < 0 \ \ OR \ \ x > 10$,输入$y$的取值范围是$-10 \sim -1$;
    • 无效等价类3:$x$的取值范围是$0 \sim 10$,输入$x$的取值范围是$x < -10 \ \ OR \ \ x > -1$。

    无效等价类的划分:每个无效等价类的测试用例,只违反一个输入的取值范围。如果违反了多个输入的取值范围,那便是不好的测试用例。

  • 边界值分析:输入的边界比中间更加容易发生错误,因此用边界值分析来补充等价类划分的测试用例设计技术

    边界值划分选择等价类边界的测试用例,既注重于输入条件边界,又适用于输出域测试用例

    对边界值设计测试用例应遵循的原则如下:

    1. 如果输入条件规定了值的范围,则应取刚达到这个范围的边界的值,以及刚刚超越这个范围边界的值作为测试输入数据。
    2. 如果输入条件规定了值的个数,则用最大个数、最小个数、比最小个数少1、比最大个数多1的数据作为测试数据。
    3. 根据规格说明的每个输出条件使用上述两条原则。
    4. 如果程序的规格说明给出的输入域或输出域是有序集合,则应选取集合的第一个元素和最后一个元素作为测试用例。
    5. 如果程序中使用了一个内部数据结构,则应当选择这个内部数据结构边界上的值作为测试用例。
    6. 分析规格说明,找出其他可能的边界条件。
  • 错误推测

  • 因果图

白盒测试

白盒测试也称为结构测试根据程序的内部结构和逻辑来设计测试用例,对程序的路径和过程进行测试,检查是否满足设计的需要。

白盒测试常用的技术有:

  • 逻辑覆盖:考察用测试数据运行被测程序时,对程序逻辑的覆盖程度。

    主要的逻辑覆盖标准有6种,它们的覆盖程度从低到高为:

    1. 语句覆盖:指选择足够的测试数据,使被测试程序中的每条语句至少执行一次

      语句覆盖对程序执行逻辑的覆盖很低,因此一般认为它是很弱的逻辑覆盖。

    2. 判定覆盖(分支覆盖):指设计足够的测试用例,使得被测程序中的每个判定表达式至少获得一次“真”/“假”值

      判定覆盖的判定表达式是指判定表达式整体。

      判定覆盖要比语句覆盖更强一些。

    3. 条件覆盖:指构造一组测试用例,使得每一判定语句中每个逻辑条件的各种可能的值至少满足一次

      条件覆盖的判定语句是指判定表达式下的判定语句(如果有),即用ANDOR等逻辑运算符连接起来的语句(不包含逻辑运算符的语句)。

    4. 判定/条件覆盖:指设计足够的测试用例,使得判定中每个条件的所有可能取值(真/假)至少出现一次,并使每个判定本身的判定结果(真/假)也至少出现一次

      判定/条件覆盖同时满足:

      • 判定覆盖
      • 条件覆盖
    5. 条件组合覆盖:指设计足够的测试用例,使得每个判定中条件的各种可能值的组合都至少出现一次

      满足条件组合覆盖的测试用例一定满足:

      • 判定覆盖
      • 条件覆盖
      • 判定/条件覆盖
    6. 路径覆盖:指覆盖被测试程序中所有可能的路径

  • 循环覆盖

  • 基本路径测试

总结

白盒测试逻辑覆盖技术总结(覆盖程度从低到高):

逻辑覆盖 说明
语句覆盖 每条语句执行一次
分支(判定)覆盖 每个分支获得一次True/False
条件覆盖 每个分支中的每个逻辑条件的所有可能取值满足一次
判定/条件覆盖 分支覆盖 + 条件覆盖
条件组合覆盖 每个判定中条件的各种可能值的组合都出现一次
路径覆盖 覆盖被测试程序中所有可能的路径

运行和维护

软件维护是软件生命周期中的最后一个阶段,处于系统投入生产性运行以后的时期中,因此不属于系统开发过程软件维护是在软件已经交付使用之后为了改正错误或满足新的需求而修改软件的过程,即软件在交付使用后对软件所做的一切改动。

系统可维护性

系统的可维护性可以定义为维护人员理解、改正、改动和改进这个软件的难易程度。提高可维护性是开发软件系统所有步骤的关键目的。系统的可维护性可以衡量系统是否能被很好地维护。

系统可维护性的评价指标:

  1. 可理解性:指别人能理解系统的结构、界面、功能和内部过程的难易程度。

    模块化、详细设计文档、结构化设计和良好的高级程序设计语言等都有助于提高可理解性。

  2. 可测试性诊断和测试的容易程度取决于易理解的程度。

    好的文档资料有利于诊断和测试;程序的结构、高性能的测试工具以及周密计划的测试工序也是至关重要的。

    在进行系统维护时,应该充分利用在系统测试阶段保存下来的测试用例。

  3. 可修改性:诊断和测试的容易程度与系统设计所制定的设计原则有直接关系。

    模块的耦合、内聚、作用范围与控制范围的关系等都对可修改性有影响。

软件文档与软件维护

软件文档是软件可维护性的决定因素。文档是软件产品的一部分,并且编写高质量的文档可以提高软件开发的质量。

软件系统的文档分为:

  • 用户文档:主要描述系统功能使用方法,并不关心这些功能是怎样实现的
  • 系统文档:描述系统设计、实现和测试等各方面的内容。

可维护性是所有软件都应具有的基本特点,必须在开发阶段保证软件具有可维护的特点。在软件工程的每一个阶段都应考虑并提高软件的可维护性,在每个阶段结束前的技术审查和管理复查中应该着重对可维护性进行复审(如将来要改进的部分和可能会修改的部分)。

维护应该针对整个软件配置,不应该只修改源程序代码。

软件维护

软件维护主要是指根据需求变化或硬件环境的变化对应用程序进行部分或全部修改。修改时应充分利用源程序,修改后要填写程序修改登记表,并在程序变更通知书上写明新旧程序的不同之处

软件维护的内容一般有以下几个方面:

  1. 正确性维护:指改正在系统开发阶段已发生而系统测试阶段尚未发现的错误

    所发现的错误有:

    • 不太重要、不影响系统正常运行的错误,其维护工作可随时进行;
    • 非常重要的错误,甚至会影响整个系统的正常运行,其维护工作必须制定计划,进行修改,并且要进行复查和控制。
  2. 适应性维护使应用软件适应信息技术变化和管理需求变化而进行的修改。

  3. 完善性维护为扩充功能和改善性能而进行的修改

    • 主要是指对已有的软件系统增加一些在系统分析和设计阶段中没有规定的功能与性能特征。
    • 还包括对处理效率和编写程序的改进,关系到系统开发质量的重要方面。

    这方面的维护还要注意将相关的文档资料加入到前面相应的文档中。

  4. 预防性维护为了改进应用软件的可靠性和可维护性,为了适应未来的软/硬件环境的变化,应主动增加预防性的新的功能,以使应用系统适应各类变化而不被淘汰。

    例如将专用报表功能改成通用报表生成功能,以适应将来报表格式的变化。

这4个维护内容中,正确性和完善性维护是针对来自系统内部的维护,适应性和预防性是针对来自系统外部的维护。

  • 正确性维护针对的是系统内部的错误。

    来自系统内部的,与错误有关的都是属于正确性维护。

  • 完善性维护针对的是系统内部与功能、性能等方面有关的维护。

    来自系统内部的,与系统功能、性能等方面有关的改善都是完善性维护。完善性维护的需求可以来自外部,例如功能的扩展。

  • 适应性维护是针对来自系统外部的技术、管理需求等方面的变化。

    针对来自系统外部的变化,系统功能等方面没有缺失,仅仅只是适应当前环境变化所做的更改,都是属于适应性维护。

  • 预防性维护针对的是未来的环境变化。


项目管理

沟通路径

沟通图是指项目中人员或部门之间的沟通用一条无向边连接起来,所构成图即为沟通图。沟通图中的路径称为沟通路径。

软件项目中沟通路径$m$的计算公式(人数$n$):

  • 沟通图中无主程序员时:

    $$ m = \sum_{i=1}^{n} i-1 = \cfrac{(n-1)n}{2} $$

  • 沟通图中有主程序员时:

    $$ m = n - 1 $$

软件项目估算

软件项目估算涉及人、技术、环境等多种因素,很难在项目完成前准确地估算出开发软件所需的成本、持续时间和工作量。所以需要一些方法和技术来支持项目的估算,常用的估算方法有下列3种:

  1. 基于已经完成的类似项目进行估算(常用)。

  2. 基于分解技术进行估算。

  3. 基于经验估算模型的估算。

    典型的经验估算模型有:

    • IBM估算模型
    • CoCoMo模型
    • Putnam模型

上述方法可以组合使用,以提高估算的精度。

COCOMO 估算模型

COCOMO模型是一种精确的、易于使用的成本估算模型。COCOMO模型按其详细程度分为:

  1. 基本COCOMO模型:是一个静态单变量模型,用于对整个软件系统进行估算。

    公式如下:

    $$ E = a(L)^b \\ D=cE^d $$

    • $E$:工作量,单位是人月;
    • $D$:开发时间,单位是月;
    • $L$:项目的源代码行估计值,不包括程序中的注释及文档,其单位是千行代码;
    • $a$、$b$、$c$、$d$:常数。

    基本COCOMO模型可通过估算代码行的值$L$,然后计算开发工作量$E$和开发时间$D$的估算值。

  2. 中级COCOMO模型:是一个静态多变量模型,它将软件系统模型分为系统和部件两个层次,系统由部件构成,它把软件开发所需的人力(成本)看作是程序大小和一系列“成本驱动属性”的函数。

    中级COCOMO模型以基本COCOMO模型为基础,并考虑了15种影响软件工作量的因素,通过工作量调节因子(EAF)修正对工作量的估算,从而使估算更合理。其公式如下:

    $$ E = a(L)^b \cdot EAF $$

    • $L$:软件产品的目标代码行数,单位是千行代码数;
    • $EAF$:工作量调节因子;
    • $a$、$b$:常数。
  3. 详细COCOMO模型将软件系统模型分为系统、子系统和模块3个层次,除包括中级模型所考虑的因素外,还考虑了在需求分析、软件设计等每一步的成本驱动属性的影响。

COCOMOII模型

和其前身COCOMO一样,COCOMOII也是一种层次结构的估算模型,被分为3个阶段性模型,分别对应三种不同的规模估算选择:

  1. 应用组装模型:在软件工程的前期阶段使用,这时用户界面的原型开发、对软件和系统交互的考虑、性能的评估以及技术成熟度的评价是最重要的。

    规模估算选择:对象点

  2. 早期设计阶段模型:在需求己经稳定并且基本的软件体系结构己经建立时使用。

    规模估算选择:功能点。功能点可转换为代码行。

  3. 体系结构阶段模型:在软件的构造过程中使用。

    规模估算选择:代码行

总结

COCOMO模型:

模型分类 类型或说明
基本COCOMO模型 静态单变量模型,对整个软件系统进行估算
中级COCOMO模型 静态多变量模型,将系统模型分为系统和部件2个层次
详细COCOMO模型 将系统模型分为系统、子系统和模块3个层次
COCOMOII 层次结构,分为应用组装模型、早期设计阶段模型和体系结构阶段模型

COCOMOII的使用时期及规模估算选择:

阶段性模型 规模估算选择
应用组装模型 对象点
早期设计阶段模型 功能点
体系结构阶段模型 代码行

进度管理

进度安排:

为监控软件项目的进度计划和工作的实际进展情况,表示各项任务之间进度的相互依赖关系,需要采用图示的方法。在图中明确标明如下内容:

  1. 各个任务的计划开始时间计划完成时间
  2. 各个任务的完成标志
  3. 各个任务与参与工作的人数,各个任务与工作量之间的衔接情况
  4. 完成各个任务所需的物理资源数据资源

进度安排的常用图形描述方法有:

  • Gantt图(甘特图);
  • 项目计划评审技术(Program Evaluation&Review Technique,PERT)图。

Gantt图

Gantt图:一种简单的水平条形图,它以日历为基准描述项目任务。

  • 垂直轴:表示多个不同的任务,每个任务按照左侧任务名称垂直排列。

  • 水平轴:表示日历时间线(如时、天、周、月和年等)。

    每个水平条表示一个任务:

    • 每一水平条的起点:表示该任务的开始时间
    • 每一水平条的终点:表示该任务的结束时间
    • 每一水平条的长度:表示完成该任务的持续时间

    当日历中同一时段存在多个水平条时,表示任务之间的并发。

如图:

Gantt图示例

  • Gantt图优点:

    能清晰地描述:

    • 每个任务的开始时间;
    • 每个任务的结束时间;
    • 任务的进展情况;
    • 各个任务之间的并行性。
  • Gantt图缺点:

    • 不能清晰地反映各任务之间的依赖关系
    • 难以确定整个项目的关键所在,即不能清晰地确定影响进度的关键任务
    • 不能反映计划中有潜力的部分

PERT图

PERT图是一个有向图

  • :表示任务

    任务包含以下成分:

    • 完成该任务所需的时间(任务持续时间)。

    • 松弛时间(Slack Time):表示在不影响整个工期的前提下完成该任务有多少机动余地

      即松弛时间指当前任务的工期可以推迟的时间。

    空任务:用虚线箭头表示,表示任务间的关系所添加。完成空任务的所需时间为0。

  • 结点:表示事件

    事件是流入结点的任务的结束,或流出结点的任务的开始。事件表示某个时间点,本身不消耗时间和资源。

    事件包含以下成分:

    • 事件号。
    • 出现该事件的最早时刻:表示在此时刻之前从该事件出发的任务不可能开始。
    • 出现该事件的最迟时刻:表示从该事件出发的任务最迟在此时刻开始,否则整个工程就不能如期完成。

    只有当流入该结点的所有任务都结束时,结点所表示的事件才出现,流出结点的任务才可以开始。

    特殊的事件:

    • 开始事件:没有任何任务流向该事件;
    • 结束事件:没有任务任务从该事件流出。

    一个项目是从开始事件开始到结束事件结束。

PERT图示例

设:

  • $T(e)$:完成任务$e$的所需时间;
  • $T_s(e)$:完成任务$e$的松弛时间;
  • $T_e(v)$:事件$v$的最早时刻;
  • $T_l(v)$:事件$v$的最迟时刻。

PERT图各成分取值(不一定需要满足下面的关系,但是可以用下面的式子推出):

  • 事件$V_{in}$的最早时刻$T_e(V_{in})$:

    • 只有一个任务流入时,设该任务的流出事件为$V_{out}$,则该任务为$<V_{out}, V_{in}>$:

      $$ T_e(V_{in}) = T_e(V_{out}) + T $$

      这里将$T(<V_{out}, V_{in}>)$简写为了$T$。

      即:该流入任务的流出事件的最早时刻 + 完成该流入任务的所需时间

    • 多个任务流入时,设与每个任务相对应的流出事件为$V_{out}[ \ i \ ]$,则这些任务为$<V_{out}[ \ i \ ], V_{in}>$:

      $$ T_e(V_{in}) = Max(T_e(V_{out}[ \ i \ ]) + T_i) $$

      这里将$T(<V_{out}[ \ i \ ], V_{in}>)$简写为了$T_i$。

      流入该事件的每个任务计算出的最早时刻的最大值

    • 开始事件$V_{start}$:

      $$ T_e(V_{start}) = 0 $$

  • 事件$V_{out}$的最迟时刻$T_l(V_{out})$:

    • 只有一个任务流出时,设该任务的流入事件为$V_{in}$,则该任务为$<V_{out}, V_{in}>$:

      $$ T_l(V_{out}) = T_l(V_{in}) - (T + T_s) $$

      这里将$T_s(<V_{out}, V_{in}>)$简写为$T_s$。

      即:该流出任务的流入事件的最迟时刻 -(该流出任务的所需时间 + 松弛时间)。

      如果松弛时间未知或为0:

      $$ T_l(V_{out}) = T_l(V_{in}) - T $$

    • 多个任务流出时,设与每个任务相对应的流入事件为$V_{in}[ \ i \ ]$,则这些任务为$<V_{out}, V_{in}[ \ i \ ]>$:

      $$ T_l(V_{out}) = Min(T_l(V_{in}[ \ i \ ]) - (T_i + S_i)) $$

      这里把$T(<V_{out}, V_{in}[ \ i \ ]>)$简写为$T_i$,把$T_s(<V_{out}, V_{in}[ \ i \ ]>)$简写为$S_i$。

      流出该事件的每个任务计算出的最晚时刻的最大值

      如果松弛时间未知或为0:

      $$ T_l(V_{out}) = Min(T_l(V_{in}[ \ i \ ]) - T_i) $$

    • 结束事件$V_{end}$:

      $$ T_l(V_{end}) = T_e(V_{end}) $$

      结束事件的最早时刻与最迟时刻相等

  • 设某任务的流入事件为$V_{in}$,流出事件为$V_{out}$,则该任务$<V_{out}, V_{in}>$的松弛时间$T_s(<V_{out}, V_{in}>)$。

    $$ T_s = T_l(V_{in}) - T - T_e(V_{out}) $$

    即,该任务的流入事件的最迟时刻 - 该任务的所需时间 - 该任务的流出事件的最早时刻

PERT图公式参照图

PERT图的路径:从开始事件到结束事件的一条通路。

PERT图的关键路径:指所有的任务的松弛时间都为0的路径

关键路径的长度:指结束事件的最早(或最晚)时刻。

PERT图的关键路径示例

关键路径的特点:

  • 所有任务的松弛时间都为0。

  • 每个事件的最早时刻和最迟时刻都是相等的。

  • 所有任务持续时间的和,是PERT图所有路径中最大的,并且与结束事件的最早时刻(或最晚时刻)相等。

    设关键路径中所有事件为$V_i$($i = 1, 2, \cdots, n$),且该路径下的任务为$<V_j, V_{j+1}>$($1 \le j \le n-1$)(表示$V_1$是开始事件,$V_2$是$V_1$往下的一个事件,以此类推,$V_n$是结束事件),那么该关键路径结束事件的最早时刻(或最晚时刻)为:

    $$ T_e(V_n) = \sum_{i = 1}^{n-1} T_i $$

    这里$T_i$代表$T(<V_i, V_{i+1}>)$。

最迟时刻的另一种求法(PERT图存在关键路径的情况下):

已知某PERT图结束事件的最晚时刻(最早时刻),该PERT图中某一条路径(假设该路径没有分支)中所有事件为$V_j$($j = 1, 2, \cdots, n$),且该路径下的任务为$<V_k, V_{k+1}>$($1 \le k \le n-1$),该路径下任务的持续时间$T(<V_{k-1}, V_k>)$已知,(即$V_1$是开始事件,按照次序往下,$V_n$是结束事件),计算某一事件的最迟时刻$T_l(V_i)$($1 \le i < n$):

$$ T_l(V_i) = T_l(V_n) - \sum_{j = i}^{n - 1} T_j $$

这里$T_j$代表$T(<V_j, V_{j + 1}>)$。

即:结束事件的最晚时刻 - 该事件到结束事件之间所有的任务的持续时间总和

注意:如果事件$V_i$到结束事件之间存在多条路径,应该选择那条任务持续时间总和最大的路径。

PERT图的优点:

  • 给出了每个任务的开始时间、结束时间和完成该任务所需的时间;
  • 给出了任务之间的关系(依赖关系)。即任务之间的执行顺序。

PERT图不能清晰地描述任务之间的并行情况。

项目活动图

项目活动图是一种有向图(与PERT图十分类似):

  • 弧:表示活动。弧的权值表示活动的持续时间。

  • 顶点:表示项目里程碑。

    特殊的里程碑:

    • 开始里程碑:没有任何活动指向该里程碑;
    • 结束里程碑:没有任何活动从该里程碑指出。

项目活动图的关键路径:按照PERT图的方法求出松弛时间为0的、从开始里程碑到结束里程碑的路径。

关键路径的长度:为结束里程碑的最早时刻(或最晚时刻)。它可以用来表示项目完成的最少时间。

软件配置管理

在软件开发过程中变更是不可避免的,而变更时由于没有进行变更控制,可能加剧了项目中的混乱。为了协调软件开发使得混乱减到最小,使用配置管理技术,使变更所产生的错误达到最小并最有效地提高生产率。

软件配置管理(Software Configure Management,SCM)用于整个软件工程过程,它是一组管理整个软件生存周期中各阶段变更的活动。

软件配置管理的主要目标包括:

  • 标识变更
  • 控制变更
  • 版本控制
  • 确保变更正确地实现
  • 报告有关变更

主要内容有两种版本:

    • 版本管理
    • 配置支持
    • 变更支持
    • 过程支持
    • 团队支持
    • 变化报告
    • 审计支持
    • 软件配置标识
    • 变更管理
    • 版本控制
    • 系统建立
    • 配置审核
    • 配置状态报告

变更控制

变更控制是一项最重要的软件配置任务。为了有效地实现变更控制,需借助于配置数据库和基线的概念。

基线:是软件生存周期中各开发阶段的一个特定点,它的作用是使各开发阶段的工作划分更加明确,使本来连续的工作在这些点上断开,以便于检查与肯定阶段成果。

基线可以作为一个检查点。在开发过程中,当采用的基线发生错误时可以知道所处的位置,返回到最近和最恰当的基线上。

配置数据库可分为以下3类:

  • 开发库:专供开发人员使用,其中的信息可能做频繁修改,对其控制相当宽松。

  • 受控库:在生存期某一阶段工作结束时发布的阶段产品,这些是与软件开发工作相关的计算机可读信息和人工可读信息。

    软件配置管理正是对受控库中的各个软件项进行管理,受控库也称为软件配置库

  • 产品库:在开发的软件产品完成系统测试后,作为最终产品存入产品库,等待交付用户或现场安装。

风险管理

一般认为软件风险包含两个特性:

  • 不确定性:指风险可能发生也可能不发生;
  • 损失:指如果风险发生,就会产生恶性后果。

在进行风险分析时,重要的是量化每个风险的:

  • 不确定程度
  • 损失程度

项目风险威胁到项目计划。项目风险是指以下各方面的潜在问题以及它们对软件项目的影响:

  • 预算
  • 进度
  • 人员:聘用职员及组织
  • 资源
  • 利益相关者
  • 需求

以下方面的不确定性也属于项目风险因素:

  • 项目复杂度
  • 项目规模
  • 项目结构

技术风险威胁到要开发软件的质量及交付时间。技术风险是指以下方面的潜在问题:

  • 设计
  • 实现
  • 接口
  • 验证
  • 维护

以下方面也属于技术风险因素:

  • 规格说明的歧义性
  • 技术的不确定性
  • 技术陈旧
  • “前沿”技术

商业风险威肋到要开发软件的生存能力,且常常会危害到项目或产品。5个主要的商业风险如下:

  • 市场风险:开发了一个没有人真正需要的优良产品或系统。
  • 策略风险:开发的产品不再符合公司的整体商业策略。
  • 销售风险:开发了一个销售部门不知道如何去销售的产品。
  • 管理风险:由于重点的转移或人员的变动而失去了高级管理层的支持。
  • 预算风险:没有得到预算或人员的保证。

Charette提出的风险分类方式:

  • 己知风险

    通过仔细评估以下内容可以发现的风险:

    • 项目计划
    • 开发项目的商业和技术环境
    • 其他可靠的信息来源,如:
      • 不现实的交付时间
      • 没有文档化需求或文档化软件范围
      • 恶劣的开发环境
  • 可预测风险

    能够从过去项目的经验中推断出来的风险,如:

    • 人员变动
    • 与客户缺乏沟通
    • 由于正在进行维护而使开发人员精力分散
  • 不可预测风险:可能会真的出现,但很难事先识别

风险识别

风险识别试图系统化地指出对项目计划(估算、进度、资源分配等)的威胁识别出已知风险和可预测风险后,项目管理者首先要做的是:

  • 在可能时回避这些风险
  • 在必要时控制这些风险

识别风险的一种方法是建立风险条目检查表,主要用来识别下列几种类型中的一些已知风险和可预测风险:

  • 产品规模:与要开发或要修改的软件的总体规模相关的风险。
  • 商业影响:与管理者或市场所施加的约束相关的风险。
  • 客户特性:与客户的素质以及开发者和客户定期沟通的能力相关的风险。
  • 过程定义:与软件过程定义的程度以及该过程被开发组织遵守的程度相关的风险。
  • 开发环境:与用来开发产品的工具的可得性及质量相关的风险。
  • 开发技术:与待开发软件的复杂性及系统所包含技术的“新奇性”相关的风险。
  • 人员才干及经验:与软件工程师的总体技术水平及项目经验相关的风险。

与上述每个主题相关的问题可以针对每一个软件项目来回答。根据这些问题的答案,项目管理者就可以估计风险产生的影响。

另一种风险条目检查表格式:仅仅列出与每一种类型有关的特性,最终给出一组风险因素和驱动因子以及它们发生的概率

风险因素包括:

  • 性能:性能风险是指产品能够满足需求且符合其使用目的的不确定程度。
  • 成本:成本风险是指能够维持项目预算的不确定程度。
  • 支持:支特风险是指开发出的软件易于纠错、修改及升级的不确定程度。
  • 进度:进度风险是指能够维持项目进度且按时交付产品的不确定程度。

风险预测

风险预测又称风险估计,它试图从两个方面评估一个风险:

  • 风险发生的可能性或概率;
  • 发生风险所产生的后果。

通常,项日计划人员与管理人员、技术人员一起进行以下4步风险预测活动

  1. 建立一个尺度或标准,以反映风险发生的可能性。
  2. 描述风险产生的后果。
  3. 估算风险对项目和产品的影响。
  4. 标注风险预测的整体精确度,以免产生误解。

一种简单的风险预测技术是建立风险表:

  • 第1列:列出所有的风险(由风险识别活动得到);

  • 第2~4列:列出每个风险的:

    • 种类
    • 发生的概率
    • 所产生的影响

    风险所产生的影响可用一个数字来表示:

    • “1”:表示灾难性的;
    • “2”:表示严重的;
    • “3”:表示轻微的;
    • “4”:表示可忽略的。

评估风险影响:

发生风险时,有3个因素可能会影响风险所产生的后果:

  • 风险的本质:指当风险发生时可能带来的问题。

  • 风险的范围

    包括:

    • 风险的严重性;
    • 风险的整体分布情况:项目中有多少部分受到影响或有多少客户受到损害。
  • 风险的时间

    • 何时能够感受到风险的影响;
    • 风险的影响会持续多长时间。

风险优先级

在进行项目风险管理时,根据风险的优先级来确定风险控制策略。

  • 风险优先级是根据风险暴露来确定的。
  • 风险暴露:是一种量化风险影响的指标。

整体的风险显露度(Risk Exposure,RE)可由下面的关系确定:

$$ RE = P \times C $$

  • $P$:风险发生的概率;
  • $C$:风险发生时带来的项目成本。

即风险暴露等于风险影响乘以风险概率。风险影响是指当风险发生时造成的损失。

风险评估

在进行风险评估时,建立了如下形式的三元组:

$$ (r_i, l_i, x_i) $$

  • $r_i$:表示风险;
  • $l_i$:表示风险发生的概率;
  • $x_i$:表示风险产生的影响。

一种对风险评估很有用的技术就是定义风险参照水准。对于大多数软件项目来说,有3种典型的风险参照水准

  • 成本:成本是否超支
  • 进度:进程是否延期
  • 性能:性能是否下降

在风险评估过程中,需要执行以下4个步骤:

  1. 定义项目的风险参考水平值。
  2. 建立每一组$(r_i, l_i, x_i)$与每一个参考水平值之间的关系。
  3. 预测一组临界点以定义项目终止区域,该区域由一条曲线或不确定区域所界定。
  4. 预测什么样的风险组合会影响参考水平值。

风险控制

风险控制的目的是辅助项目组建立处理风险的策略。一个有效的策略必须考虑以下3个问题:

  • 风险避免

    应对风险的最好办法是主动地避免风险,即在风险发生前分析引起风险的原因,然后采取措施,以避免风险的发生

  • 风险监控

    项目管理者应监控某些因素,这些因素可以提供风险是否正在变高或变低的指示。

  • RMMM计划:

    风险管理策略可以包含在软件项目计划中,或者风险管理步骤也可以组织成一个独立的风险缓解、监控和管理计划(RMMM计划)。

    RMMM计划将所有风险分析工作文档化,并由项目管理者作为整个项目计划中的一部分来使用。

    建立了RMMM计划,而且项目己经启动之后,风险缓解及监测步骤也就开始了:

    • 风险缓解:一种问题规避活动。

    • 风险监测:一种项目跟踪活动。

      这种监测活动有3个主要目的:

      • 评估所预测的风险是否真的发生了;
      • 保证正确地实施了各风险的缓解步骤;
      • 收集能够用于今后风险缝隙的信息。

    风险监测的另一个任务就是试图找到“起源”(在整个项目中是哪些风险引起了哪些问题)。

总结

风险分类总结:

分类 说明
项目风险 威胁到项目计划。

风险因素:
预算、进度、人员、资源和利益相关者,项目复杂度、规模和结构的不确定性

技术风险 威胁到软件的质量及交付时间。

风险因素:
设计、实现、接口、验证和维护,规格说明的歧义性、技术的不确定性、技术陈旧和使用“前沿”技术

市场风险 开发了一个没有人真正需要的产品或系统。
策略风险 开发的产品不再符合公司的整体商业策略。
销售风险 开发了一个销售部门不知道如何去销售的产品。
管理风险 由于重点的转移或人员的变动而失去了高级管理层的支持。
预算风险 没有得到预算或人员的保证。

风险管理总结:

风险管理 说明
风险识别 指出对项目计划的威胁。可通过建立风险条目检查表识别。
风险预测 从风险发生的可能性或概率、风险产生的后果评估可能发生的风险。
风险评估 从风险发生的概率和产生的影响评估风险。可用定义风险参照水准技术评估。
风险控制 目的是辅助项目建立处理风险的策略。策略是风险避免、风险监控和RMMM计划。
风险避免 应对风险的最好办法是主动地避免风险。
风险监控 项目管理者应监控某些可以提供风险高低变化指示的因素。

软件质量

软件质量特性的度量

可靠性、可用性和可维护性是软件的质量属性,软件工程中,用$0 \sim 1$之间的数来度量。

  • 可靠性:指一个系统对于给定的时间间隔内、在给定条件下无失效运作的概率

    可以用$\cfrac{MTTF}{1+MTTF}$来度量。

    $MTTF$为平均无故障时间

  • 可用性:指在给定的时间点上,一个系统能够按照规格说明正确运作的概率

    可以用$\cfrac{MTBF}{1+MTBF}$来度量。

    $MTBF$为平均失效间隔时间

  • 可维护性:在给定的使用条件下,在规定的时间间隔内,使用规定的过程和资源完成维护活动的概率

    可以用$\cfrac{1}{1+MTTR}$来度量。

    $MTTR$为平均修复时间

总结:

质量属性 度量公式 说明
可靠性

$\cfrac{MTTF}{1+MTTF}$

$MTTF$:平均无故障时间

给定时间间隔内、给定条件下,无失效运作的概率
可用性

$\cfrac{MTBF}{1+MTBF}$

$MTBF$:平均失效间隔时间

给定时间点上、能按照规格说明正确运作的概率
可维护性

$\cfrac{1}{1+MTTR}$

$MTTR$:平均修复时间

给定使用条件下、规定时间间隔内,使用规定过程和资源完成维护的概率

软件质量模型

讨论软件质量首先要了解软件的质量特性,目前己经有多种软件质量模型来描述软件质量特性,如:

  • ISO/IEC 9126 软件质量模型
  • Me Call 软件质量模型。

ISO/IEC 9126 软件质量模型

ISO/IEC 9126软件质量模型由3个层次组成:

  1. 第一层:质量特性
  2. 第二层:质量子特性
  3. 第三层:度量指标

该模型的质量特性和质量子特性:

质量特性 质量子特性
功能性(Functionality)
适合性(Suitability)
准确性(Accurateness)
互用性(Interoperability)
依从性(Compliance)
安全性(Security)
可靠性(Reliability)
成熟性(Maturity)
容错性(Fault tolerance)
易恢复性(Recoverability)
易使用性(Usability)
易理解性(Understandability)
易学性(Learnability)
易操作性(Operability)
效率(Efficiency)
时间特性(Time behavior)
资源特性(Resource behavior)
可维护性(Maintainability)
易分析性(Analyzability)
易改变性(Changeability)
稳定性(Stability)
易测试性(Testability)
可移植性(Portability)
适应性(Adaptability)
易安装性(Installability)
一致性(Conformance)
易替换性(Replaceability)

质量子特性的含义:

  • 功能性:
    • 适合性:与对规定任务能否提供一组功能以及这组功能是否适合有关的软件属性。
    • 准确性:与能够得到正确或相符的结果或效果有关的软件属性。
    • 互用性与其他指定系统进行交互操作的能力相关的软件属性。
    • 依从性使软件服从有关的标准、约定、法规及类似规定的软件属性。
    • 安全性:与避免对程序及数据的非授权故意或意外访问的能力有关的软件属性。
  • 可靠性:
    • 成熟性:与由软件故障引起失效的频度有关的软件属性。
    • 容错性:与在软件错误或违反指定接口的情况下维持指定的性能水平的能力有关的软件属性。
    • 易恢复性:与在故障发生后,重新建立其性能水平并恢复直接受影响数据的能力,以及为达到此目的所需的时间和努力有关的软件属性。
  • 易使用性:
    • 易理解性:与用户为理解逻辑概念及其应用所付出的劳动有关的软件属性。
    • 易学性:与用户为学习其应用(例如操作控制、输入、输出)所付出的努力相关的软件属性。
    • 易操作性:与用户为进行操作和操作控制所付出的努力有关的软件属性。
  • 效率:
    • 时间特性:与响应和处理时间以及软件执行其功能时的吞吐量有关的软件属性。
    • 资源特性:与软件执行其功能时,所使用的资源量以及使用资源的持续时间有关的软件属性。
  • 可维护性:
    • 易分析性:与为诊断缺陷或失效原因,或为判定待修改的部分所需努力有关的软件属性。
    • 易改变性:与进行修改、排错或适应环境变换所需努力有关的软件属性。
    • 稳定性:与修改造成未预料效果的风险有关的软件属性。
    • 易测试性:为确认经修改软件所需努力有关的软件属性。
  • 可移植性:
    • 适应性:与软件转移到不同环境时的处理或手段有关的软件属性。
    • 易安装性:与在指定环境下安装软件所需努力有关的软件属性。
    • 一致性:使软件服从与可移植性有关的标准或约定的软件属性。
    • 易替换性:与一软件在该软件环境中用来替代指定的其他软件的可能和努力有关的软件属性。

Mc Call 软件质量模型

Mc Call软件质量模型从以下3个方面确定了11个质量特性:

  • 软件产品的运行
  • 软件产品的修正
  • 软件产品的转移

Mc Call也给出了一个三层模型框架:

  1. 第一层:质量特性
  2. 第二层:评价准则
  3. 第三层:度量指标

Mc Call 软件质量模型

软件质量保证

软件质量保证是指为保证软件系统或软件产品充分满足用户要求的质量而进行的有计划、有组织的活动,其目的是生产高质量的软件。

软件质量保证包括了与以下7个主要活动相关的各种任务:

  • 应用技术方法
  • 进行正式的技术评审
  • 测试软件
  • 标准的实施
  • 控制变更
  • 度量(Metrics)
  • 记录保存和报告

软件评审

通常,把“质量”理解为“用户满意程度”。为了使得用户满意,有以下两个必要条件:

  • 设计质量:设计的规格说明书符合用户的要求。

    设计质量的评审对象:

    • 软件需求规格说明
    • 数据需求规格说明
    • 软件概要设计说明
  • 程序质量:程序按照设计规格说明所规定的情况正确执行。

    程序质量的评审通常是从开发者的角度进行,与开发技术直接相关。程序质量的评审对象:

    • 软件结构:
      • 功能结构:
        • 数据结构
        • 功能结构
        • 数据结构和功能结构之间的对应关系
      • 功能的通用性
      • 模块的层次
      • 模块结构:
        • 控制流结构
        • 数据流结构
        • 模块结构与功能结构之间的对应关系
      • 处理过程的结构
    • 与运行环境的接口:
      • 与硬件的接口
      • 与用户的接口
    • 变更带来的影响

软件的规格说明分为:

  • 外部规格说明:从用户角度来看的规格,包括硬件/软件系统设计、功能设计;

    设计质量是由外部规格说明决定的

  • 内部规格说明:为了实现外部规格的更详细的规格,即软件模块结构与模块处理过程的设计。

    内部规格说明是从开发者角度来看的规格说明。

    程序是由内部规格说明决定的。

软件容错技术

提高软件质量和可靠性的技术大致可分为两类:

  • 避开错误:在开发的过程中不让差错潜入软件的技术;
  • 容错技术:对某些无法避开的差错,使其影响减至最小的技术。

实现容错的主要手段是冗余。冗余是指对于实现系统规定功能是多余的那部分资源,包括:

  • 硬件
  • 软件
  • 信息
  • 时间

由于加入了这些资源,有可能使系统的可靠性得到较大的提高。通常,冗余技术分为4类:

  • 结构冗余:结构冗余是通常采用的冗余技术,按其工作方法可以分为:

    • 静态冗余

      常用的有:

      • 三模冗余(Triple Module Redundancy,TR)
      • 多模冗余

      静态冗余通过表决和比较来屏蔽系统中出现的错误。

    • 动态冗余:动态冗余的主要方式是多重模块待机储备。

      当系统测试到某工作模块出现错误时,就用一个备用模块来顶替它并重新运行。这里包括以下过程:

      • 检测
      • 切换
      • 恢复

      动态冗余有以下两种方式:

      • 热备份系统:每当一个出错模块被其他备用模块顶替后,冗余系统相当于进行了一次重构。

        在热备份系统中,备用模块在待机过程中的失效率为0。

      • 冷备份系统:各备用模块在其待机时可与主模块一同工作,也可不工作。

    • 混合冗余:兼有静态元余和动态冗余的长处。

  • 信息冗余:指为检测或纠正信息在运算或传输中的错误需外附加的一部分信息。

  • 时间冗余:指以重复执行指令或程序来消除瞬时错误带来的影响。

  • 冗余附加技术:指为实现上述冗余技术所需的资源和技术,包括:

    程序、指令、数据、存放和调动它们的空间和通道等。

    在屏蔽硬件错误的容错技术中,冗余附加技术包括:

    1. 关键程序和数据的冗余存储及调用。
    2. 检测、表决、切换、重构、纠错和复算的实现。

    在屏蔽软件错误的容错系统中,冗余附加技术的构成包括:

    1. 冗余备份程序的存储及调用。
    2. 实现错误检测和错误恢复的程序。
    3. 实现容错软件所需的固化程序。

McCabe 软件复杂性度量法

McCabe度量法又称环路度量法,是通过定义环路复杂度,建立程序复杂性的度量,它是一种基于程序控制流的复杂性度量方法,它反映了程序(或模块)的控制结构的复杂性。

MaCabe度量法认为程序的复杂性在很大程度上取决于控制的复杂性。单一的顺序程序结构最为简单,循环和选择构成的环路越多,程序就越复杂。

MaCabe度量法以图论为工具,先画出程序图,然后用该图的环路数作为程序复杂性的度量值。

程序图是退化的程序流程图。把程序流程图中的每个处理符号都退化成一个结点,原来连接不同处理符号的流线变成连接不同点的有向弧,这样得到的有向图称为程序图。

程序图示例

程序图仅描述程序内部的控制流程,完全不表现对数据的具体操作以及分支和循环的具体条件。

根据图论,在一个强连通的有向图$G$中,计算环的个数$V(G)$的公式为:

$$ V(G) = m - n + 2p $$

  • $V(G)$:$G$中的环路个数
  • $m$:$G$中的弧的个数
  • $n$:$G$中的结点数
  • $p$:$G$中的强连通分量个数

在一个程序中,从程序图的入口点总能到达图中的任何一个结点,因此,程序总是连通的,但不是强连通的。

为了使程序图成为强连通图,从图的入口点到出口点加一条用虚线表示的有向边(如上图所示),使图成为强连通图(但虚线弧并不算入实际的弧数)。这样就可以使用上式计算环路复杂性了,也因此程序图的$p$通常为1。

即,计算有向图$G$的环路复杂性的公式为:

$$ V(G) = m - n + 2 $$

例如上图中,结点数$n=6$,弧数$m=9$,则:

$$ V(G) = 9 - 6 + 2 = 5 $$

即上图McCabe环路复杂的度量值为5。

$V(G) = 10$是一个实际模块的上限。当$V(G) > 10$时,很难充分测试这个模块。

在白盒测试中,计算环路复杂度可以简单地使用判定条件的数量 + 1。


软件工具

软件开发工具

对应于软件开发过程的各种活动,软件开发工具通常有:

  • 需求分析工具
  • 设计工具
  • 编码与排错工具
  • 测试工具

软件维护工具

辅助软件维护过程中活动的软件称为软件维护工具,它辅助维护人员对软件代码及其文档进行各种维护活动。软件维护工具主要有:

  • 版本控制工具
  • 文档分析工具
  • 开发信息库工具
  • 逆向工程工具
  • 再工程工具

软件管理和软件支持工具

软件管理和软件支持工具用来辅助管理人员和软件支持人员的管理活动和支持活动,以确保软件高质量地完成。

常用的铺助软件管理和软件支持的工具有:

  • 项目管理工具
  • 配置管理工具
  • 软件评价工具