软件工程

This is my blog.

之前没有好好学习软件工程呢!

所以打算好好开始看看呢!

概念

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

软件工程学:

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

软件工程过程是指为获得软件产品,在软件工具的支持下由软件工程师完成的一系列软件工程活动

包括以下四个方面

  1. P(Plan)软件规格说明,规定软件的功能及其运行时的限制
  2. D(DO)软件开发,开发出满足规格说明的软件
  3. C(Check)软件确认,确认开发的软件能够满足用户的需求
  4. A(Action)软件演进,软件在运行过程中不断改进以满足客户新的需求

原则:

  1. 抽象;
  2. 信息隐蔽;
  3. 模块化;
  4. 局部化;
  5. 确定性;
  6. 一致性;
  7. 完备性;
  8. 可验证性

计算机软件:

  • 系统软件:一整套服务于其他程序的程序
  • 应用软件:解决特定业务需要的独立应用程序
  • 工程/科学软件:通常带有”数值计算”算法的特征
  • 嵌入式软件:面向最终使用者和系统本身的特性
  • 产品线软件:为多个不同用户的使用提供特定功能
  • Web应用软件:以网络为中心
  • 人工智能软件:利用非数值算法解决计算和直接分析无法解决的复杂问题
  • 开放计算:普适计算、分布式计算
  • 网络资源
  • 开源软件

描述Bug的术语:

当人们在进行软件开发活动的过程中出错时(称为错误error),就会出现故障(fault);是从开发人员的角度来看待系统;单个错误可能会产生多个故障

失效(failure)是指系统违背了它应有的行为;是从用户角度看到的问题;并非每一个故障对应于一个失效,如果不执行故障代码,则不会使代码失效

基本原理

这七条原理被认为是确保软件产品质量和开发效率的原理的最小集合

  1. 用分阶段的生命周期计划严格管理
    在软件的这个生存周期中应该制定并严格执行六类计划:
    • 项目概要计划
    • 里程碑计划
    • 项目控制计划
    • 产品控制计划
    • 验证计划
    • 运行维护计划
  2. 坚持进行阶段评审
    统计发现设计错误占软件错误的63%,而编码错误仅占37%;而且错误发现与改正得越晚,所需付出的代价越高
  3. 实现严格的产品控制
    在软件开发中,改变需求是难免的;在改变需求时,为了保持软件各个配置成分的一致性,必须实行严格的产品控制,其中主要是实行基准配置管理;基准配置又称为基线配置,它是经过阶段评审后的软件配置成分;一旦有修改,必须按照严格的规程进行评审,在获得批准后才能实施修改
  4. 采用现代程序设计技术
    采用先进的技术既可以提高软件开发的效率,又可以降低软件维护的成本
  5. 结果应能清楚地审查
  6. 开发小组的人员应少而精
    人数多,则通信开销增加;高素质开发人员的效率高,同时错误也少
  7. 承认不断改进软件工程实践的必要性

软件生存周期

一个软件产品或软件系统要经历孕育、诞生、成长、成熟、衰亡的阶段,一般称为软件生存周期

软件开发的观点看,它就是使用适当的资源(包括人员,软硬件资源,时间等),为开发软件进行的一组开发活动,在活动结束时输入(即用户的需求)转化为输出(最终符合用户需求的软件产品)

分为三个阶段:

  • 定义阶段

    • 可行性研究初步项目计划

      确定软件的开发目标及其可行性

      需要回答:要解决的问题是什么?有可行解吗?若有解,需要多少费用、资源、时间?

      需要进行问题定义、可行性分析,制定项目开发计划

      参加人员:用户、项目负责人、系统分析师

      主要文档:可行性分析报告和项目开发计划

    • 需求分析

      准确地确定软件系统必须做什么确定软件系统的功能、性能、数据和界面等要求,从而确定系统的逻辑模型

      参加人员:用户、项目负责人、系统分析师

      主要文档:软件需求说明书

  • 开发阶段

    • 概要设计

      开发人员把确定的各项功能需求转换成需要的体系结构,在该体系结构中,每个成分都是意义明确的模块,即每个模块都和某些功能需求相对应。

      即设计软件的结构,明确软件由哪些模块组成,这些模块的层次结构、调用关系,功能,同时还要设计该项目的应用系统的总体数据结构和数据库结构,即应用系统要存储什么数据,这些数据是什么样的结构,它们之间有什么关系。

      参加人员:系统分析师、软件设计师

      主要文档:概要设计说明书

    • 详细设计
      对每个模块完成的功能进行具体描述,要把功能描述转变为精确的、结构化的过程描述。即该模块的控制结构是怎么样的,先做什么,后做什么,有什么样的条件判定,有些什么重复处理等,并用相应的表示工具把这些控制结构表示出来。

      参加人员:软件设计师、程序员

      主要文档:详细设计文档

    • 实现
      把每个模块的控制结构转换成计算机可接受的程序代码,即写成某种特定程序设计语言表示的源程序清单

    • 测试
      保证软件质量的重要手段,其主要方式是在设计测试用例的基础上检查软件的各个组成部分。

      参加人员:是另一部门(或单位)的软件设计师或系统分析师

      主要文档:软件测试计划、测试用例和软件测试报告

  • 运行和维护阶段

    • 运行

    • 维护
      软件生存周期中时间最长的阶段

      修改的情况:发现隐含错误、为了适应软件工作环境、由于用户业务需求的增加需要扩充和增强软件性能、为将来的软件维护活动做预先准备

    • 废弃

软件过程

在软件开发中所遵循的路线图称为“软件过程”。过程是活动的集合,活动是任务的集合。

软件过程有3层含义:个体含义(软件产品或系统在生存周期中的某一类活动的集合)、整体含义(软件产品或系统在所有上述含义下的软件过程的总体)、工程含义(解决软件过程的工程)


过程包含以下7种类型的要素:

  1. 活动
  2. 序列:活动的顺序
  3. 过程模型:是关于系统兴趣的观点
  4. 资源
  5. 控制
  6. 策略:指导原则
  7. 组织:过程代理的层次化结构

过程:一组有序的任务,它涉及活动、约束、资源使用的一系列步骤

软件开发过程有时又称为软件生命周期

包含以下活动:

  • 需求分析与定义
  • 系统设计
  • 程序设计
  • 编写程序
  • 单元测试
  • 集成测试
  • 系统测试
  • 系统交付
  • 维护

开发团队人员:

  • 需求分析员
  • 设计人员
  • 程序员
  • 测试人员
  • 培训人员

一般来说,维护过程中,开发团队的所有人员都需要参与

资料管理人员负责准备和存储在系统生命周期中用到的文档,包括需求规格说明、设计描述、程序文档、培训手册、测试数据、进度等。

配置管理小组,让开发人员知道若改变需求,需要改变哪些,会影响哪些;还要协调可能建立或支持的系统的不同版本

评价模型

CMM(Capability Maturity Model of Software)

软件过程能力成熟度模型,提供了一种评价软件承接方能力的方法,同时它可帮助软件组织改进软件过程。

五个成熟度级别:

  1. 初始级

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

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

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

  5. 优化级

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

CMMI

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

两种表示方法:

  1. 阶段式模型
    类似于CMM,关注组织的成熟度

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

    能力等级:

    • CL0(未完成的)
    • CL1(已执行的)
    • CL2(已管理的)
    • CL3(已定义级的)
    • CL4(定量管理的)
    • CL5(优化的)

UP 统一过程

统一过程模型是一种”用例和风险驱动以架构为中心迭代并且增量“的开发过程,由UML方法和工具支持。

迭代指的是将整个软件开发项目划分成许多个小的”袖珍项目”,每个”袖珍项目”都包含正常软件项目的所有元素:计划、分析与设计、构造、集成和测试,以及内部和外部发布。

典型代表:RUP(Rational unified Process)。RUP是UP的商业扩展,兼容UP,但比UP更完整、更详细

4个技术阶段及其制品:

  1. 起始阶段
    专注于项目的初创活动

    工作产品:构想文档、初始用例模型、初始项目术语表、初始业务用例、初始风险评估、项目计划(阶段及迭代)、业务模型以及一个或多个原型(需要时)

    里程碑:生命周期目标

  2. 精化阶段

    在理解了最初的领域范围之后进行需求分析架构演进

    工作产品:用例模型、补充需求(包括非功能需求)、分析模型、软件体系结构描述、可执行的软件体系结构原型、修订的风险列表、项目计划(包括迭代计划、调整的工作流、里程碑和技术工作产品)以及初始用户手册

    里程碑:生命周期架构

  3. 构建阶段
    关注系统的构建,产生实现模型

    工作产品:设计模型、软件构件、集成的软件增量、测试计划及步骤、测试用例以及支持文档(用户手册、安装手册和对于并发增量的描述)

    里程碑:初始运作功能

  4. 移交阶段
    关注于软件提交方面的工作,产生软件增量

    工作产品:提交的软件增量、$\beta$测试报告和综合用户反馈

    里程碑:产品发布

软件过程模型

也称为软件开发模型,它是软件开发全部过程、活动和任务的结构框架。

静态模型描述过程,表明了从输入到输出的转换过程

动态模型能够动态展现过程,这样用户能够看到中间产品和最终产品是如何随着时间的推移进行转换的


过程模型中,方框表示活动,从左边进入的箭头表示资源,从右边离开的箭头表示输出,从顶部进入的箭头表示控制或约束,从下部进入的箭头表示机制(这些机制辅助开发人员执行活动,例如:工具、数据库或技术)


过程表示法:如何用执行过程的人员能够理解的方式记录过程

过程模型:如何使用一组合适的活动、资源、产品和工具来描述过程

过程建模支持工具:如何演示或模拟一个过程模型,从而可以评价资源的可用性、有用性和性能

过程测度和评价:在特定的时间或环境下,如何判断哪些活动、资源、子过程和模型类型最有益于生产高质量产品


瀑布模型 Waterfall Model

瀑布模型是将软件生存周期中的各个活动规定为依线性顺序连接的若干阶段的模型,包括需求分析、设计、编码、测试、运行与维护。它规定了由前至后、相互衔接的固定次序,如同瀑布流水逐级下落。

文档为驱动,适合于软件需求很明确的软件项目的模型

优点:

易理解管理成本低;强调开发的阶段性早期计划及需求调查和产品测试

不足:

客户必须完整、正确、清晰地表达他们的需要;在开始的两个或三个阶段中,很难评估真正的进度状态;当接近项目结束时,出现了大量的集成和测试工作;直到项目结束之前,都不能演示系统的能力;对于项目风险的控制能力较弱


变体:V模型

V模型左侧步骤向下推进,基本问题需求逐步细化,形成问题及解决方案的技术描述。一旦编码结束,沿着V模型右侧的步骤向上推进工作,其实际上是执行了一系列的测试(质量保证活动)

瀑布模型关注文档和制品,而V模型关注活动和正确性


阶段化开发

阶段化开发:增量和迭代

通常会有两个系统在并行运行:产品系统和开发系统

运行系统产品系统是当前正在被客户和用户使用的系统;而开发系统是准备用来替换现行产品系统的下一个版本

增量模型 Incremental Model

将需求分段成一系列增量产品,每一增量分别开发。该模型采用随着日程时间的进展而交错的线性序列,每一线性序列产生软件的一个可发布的增量。客户对每个增量的使用和评估都作为下一个增量发布的新特征和功能

优点:(除具有瀑布模型的优点之外)

第一个可交付版本所需要的成本和时间很少;开发由增量表示的小系统所承担的风险不大;由于很快发布了第一个版本,因此可以减少用户需求的变更;运行增量投资,即在项目开始可以仅对一个或两个增量投资。

不足:

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

迭代模型

增量模型是增加功能,而迭代模型是在一开始就提交一个完整的系统,然后在每一个新的发布中改变每个子系统的功能。

演化模型 Evolutionary Model

演化模型是迭代的过程模型,特别适用于对软件需求缺乏准确认识的情况

原型模型 Prototype Model

原型是预期系统的一个可执行版本,反映了系统性质的一个选定的子集。一个原型不必满足目标软件的所有约束,其目的是能快速、低成本地构建原型。

原型模型开始于沟通,其目的是定义软件的总体目标,标识需求,然后快速制定原型开发的计划,确定原型的目标和范围,采用快速设计的方式对其进行建模,并构建原型。被开发的原型应交付给客户使用,并收集客户的反馈意见,这些反馈意见在下一轮中对原型进行改进。(一轮完成后,下一轮才开始,和增量的交错线性不同)

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

  • 探索型原型

    弄清目标的要求,确定所希望的特性,并探讨多种方案的可行性

  • 实验型原型
    验证方案或算法的合理性,是在大规模开发和实现前,用于考查方案是否合适、规格说明是否可靠

  • 演化型原型
    将原型作为目标系统的一部分,通过对原型的多次改进,逐步将原型演化成最终的目标系统

系统测试阶段会确认,确认validation)确保系统实现了所有的需求;也会对需求进行验证verification),验证确保每一项功能都是正确的

螺旋模型 Spiral Model

螺旋模型将瀑布模型和原型模型合起来,加入了两种模型均忽略的风险分析,适用于庞大、复杂且具有高风险的系统

四个步骤:

  1. 制定计划:决定目标、方案和限制
  2. 风险分析:评价方案、识别风险、消除风险
  3. 实施工程:开发、验证下一产品
  4. 用户评估

优点:

支持用户需求的动态变化,为用户参与软件开发的所有关键决策提供了方便,有助于提高软件的适应能力,并且为项目管理人员及时调整管理决策提供了便利,从而降低了软件开发的风险。

不足:

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

喷泉模型 Water Fountain Model

喷泉模型是一种以用户需求为动力,以对象作为驱动的模型,适合于面向对象的开发方法;开发过程具有迭代性和无间隙性(指在开发活动【如分析、设计、编码】之间不存在明显的边界)。

克服了瀑布模型不支持软件重用和多项开发活动集成的局限性

优点:

提高软件项目的开发效率,节省开发时间

不足:

需要大量开发人员,不利于项目的管理;需要严格管理文档,使得审核难度加大

基于构件的开发模型 Component-based Development Model

利用预先包装的构件来构造应用系统。构件可以是组织内部开发的构件,也可以是商品化成品(Commercial Off-The-Self,COTS)软件构件。本质上是演化模型,需要以迭代方式构建构件。

包括领域工程应用系统工程两部分

形式化方法模型 Formal Methods Model

建立在严格数学基础上的一种软件开发方法,其主要活动是生成形式化的数学规格说明

其它模型

可操作规格说明模型

可操作规格说明模型,通过演示系统行为的方式来评估或执行系统需求。即,一旦指定了需求,就可以用软件包进行演示。

瀑布模型把功能与设计分离,而可操作规格说明模型允许把功能和设计合并起来

可转换模型

去除某些主要开发步骤来设法减少出错机会。利用自动化手段的支持,转换过程使用一系列转换把需求规格说明变为一个可支付使用的系统。

转换的样例有:

  • 改变数据表示
  • 选择算法
  • 优化
  • 编译

软件开发方法

结构化方法

由结构化分析、结构化设计、结构化程序设计构成,是一种面向数据流的开发方法;总的指导思想是自顶向下、逐层分解,基本原则是功能的分解与抽象。适合于数据处理领域的问题。

结构化分析是根据分解与抽象的原则,按照系统中数据处理的流程,用数据流图来建立系统的功能模型,从而完成需求分析工作。

结构化设计是根据模块独立性准则,软件结构优化准则将数据流图转换为软件体系结构,用软件结构图来建立系统的物理模型,实现系统的概要设计。

结构化程序设计使用三种基本控制结构构造程序

Jackson方法

面向数据结构的开发方法,适合于小规模的项目。

又发展了JSD(Jackson System Development)。首先建立现实世界的模型,再确定系统的功能需求,对需求的描述特别强调操作之间的时序性。它是以事件作为驱动的,是一种基于进程的开发方法。适用于时许特点较强的系统,包括数据处理系统和一些实时控制系统。

原型方法

开发原型系统首先确定用户需求,开发初始原型,然后征求用户对初始原型的改进意见,并根据意见修改原型。适合于用户需求不清、需求经常变化的情况。

面向对象方法

UML(Unified Modeling Language)同一建模语言是面向对象的标准建模语言。

敏捷开发方法

总体目标是通过”尽可能早地、持续地对有价值的软件的交付”使客户满意。

4条原则:

  1. 相对于过程和工具,更强调个人和交互价值
  2. 更喜欢在生产运行的软件上花费时间,而不是将时间花费在编写各种文档上
  3. 将精力集中在与客户合作上,而不是合同谈判上
  4. 专注于对变化的反应,而不是创建一个计划而后遵循这个计划

极限编程XP

XP是一种轻量级(敏捷)、高效、低风险、柔性、可预测的、科学的软件开发方式。

4个部分组成:

  • 4大价值观:沟通、简单性、反馈和勇气(尽早和经常交付功能的承诺)
  • 5个原则:快速反馈、简单性假设、逐步修改、提倡更改和优质工作
  • 12个最佳实践:规划游戏、小的发布、隐喻(对于系统将如何运行的设想取得一致意见)、简单设计(只处理当前的需求)、首先编写测试、重构(重新审视需求和设计,重新明确地描述它们以符合新的现有的需要)、结对编程、集体所有权、持续集成、可以忍受的步伐、在现场的客户、代码标准
  • 行为

水晶法 Crystal

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

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

并列争球法 Scrum

使用迭代的方法,把每30天一次的迭代称为一个”冲刺”(sprint),并按优先级别来实现产品。多个自组织和自治的小组并行地递增实现产品。协调是通过简短的日常会议来进行。

自适应软件开发 ASD

软件工具

用来辅助软件开发、运行、维护、管理和支持等过程中的活动的软件称为软件工具

计算机辅助软件工程(Computer-Aided Software Engineer,CASE)工具

  • 软件开发工具
    • 需求分析工具
    • 设计工具
    • 编码与排错工具
    • 测试工具
  • 软件维护工具
    • 版本控制工具
    • 文档分析工具
    • 开发信息库工具
    • 逆向工程工具
    • 再工程工具
  • 软件管理和软件支持工具
    • 项目管理工具
    • 配置管理工具
    • 软件评价工具

软件开发环境

指支持软件产品开发的软件系统,它由软件工具集和环境集成机制构成

软件项目管理

有效的软件项目管理集集中在4个P上,即

  • 人员(Person):工作风格(外向/内向、理性/感性)
    • 项目管理人员
    • 高级管理人员
    • 研发人员
    • 客户
    • 最终用户
  • 产品(Product)
  • 过程(Procedure)
  • 项目(Project)

项目组织:

  • 主程序员负责制小组 chief programmer team
    • 高度确定性、稳定性、一致性和重复性
  • 忘我方法 egoless approach:让每个人平等地承担责任,民主式投票产生结果
    • 大量的不确定性时

软件项目估算

方法:已完成类似项目、分解技术、经验

成本估算方法

自顶向下、自底向上、差别估算、专家估算法、类推估算法、算式估算法

悲观的预测x,乐观的预测y、最有可能的猜测z

则beta概率分布的平均值为$(x+4y+z)/6$

COCOMO 估算模型

  • 基本COCOMO模型(静态单变量)

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

    其中E工作量,D开发时间,L源代码行数

  • 中级COCOMO模型(静态多变量,分为系统和部件)

    $E=a(L)^bEAF$

    其中EAF是工作量调节因子(15种影响软件工作量的因素)

  • 详细COCOMO模型(分为系统、子系统、模块)

演化成COCOMOII模型,包括应用组装模型、早期设计阶段模型、体系结构阶段模型

$E=bS^cm(X)$

其中,$bS^c$是初始的基于规模的估算,通过关于成本驱动因子信息的向量$m(X)$对它进行调整

Putnam模型

动态多变量模型,假设在软件开发的整个生存期种工作量有特定的分布

其中$t_d$表示开发持续时间,$C_k$表示技术状态常数,其值依赖于开发环境

软件度量

分类:

  1. 面向规模的度量:通常用程序的代码行数LOC来衡量
  2. 面向功能的度量

  1. 生产率度量
  2. 质量度量
  3. 技术度量

复杂性度量:规模、难度、结构、智能度

McCabe度量法

又称环路度量,是程序流程图的改变

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

其中,m是弧的个数,n是结点数,p是强连通分量的个数

一般10是上限,要充分测试此模块变得很艰难

进度管理

项目进度(Project schedule)通过列举项目的各个阶段,把每个阶段分解成离散的任务或活动,来描述特定项目的软件开发周期。

可交付产品(deliverable),即在项目开发的过程中客户希望看到的产品:

  • 文档
  • 功能的演示
  • 子系统的演示
  • 精确性的演示
  • 可靠性、安全性或性能的演示

活动(actibity):是项目的一部分,它在一段时间内发生

可以通过4个参数对活动进行描述:

  • 前驱(precursor):活动的一组条件
  • 工期(duration)
  • 截止日期(due date)
  • 终点(end point):表示活动已经结束,通常是一个里程碑或可交付的产品

活动图:表示活动之间的依赖关系,虚线表示这些活动必须在后一个活动之前完成

关键路径法(Critical Path Method, CPM):是每个节点的时差都为零的路径

时差slack time/浮动时间float = 可用时间available time - 真实时间real time/实际时间actual time

时差=最晚开始时间 - 最早开始时间

里程碑(milestone)是活动的完成——某一特定的时刻

工作分解结构(work breakdown structure):把项目描述为由若干离散部分构成的集合


工具:

  • Gantt甘特图
    • 水平条形图,以日历为基准
    • 清晰地描述,每个任务从何时开始,到何时结束,任务的进展情况以及各个任务之间的并行性
    • 但不能清楚地反映出各任务之间的依赖关系,难以确定整个项目的关键所在,也不能反映计划中有潜力的部分
  • PERT(Program Evaluation & Review Technique)项目计划评审技术图
    • 采用正态分布,对于实际时间的一个估计的窗口window/区间interval
    • 有向图,箭头表示任务,结点表示事件
    • 清晰地描述,每个任务从何时开始,到何时结束,给出各任务之间的关系,
    • 但不能反映各个任务之间的并行性

软件项目的组织

  • 主程序员制小组
  • 民主制小组
  • 层次式小组

软件质量管理

质量特性:

  • ISO/IEC 9126指标
    • 质量特性
      • 功能性
      • 可靠性
      • 易使用性
    • 质量子特性
    • 度量指标
  • Mc Call软件质量模型
    • 产品运行
    • 产品修正
    • 产品转移

质量保证

软件评审

软件容错技术

软件配置管理

Software Configure Management(SCM),用于整个软件工程过程,其目标是标识变更,控制变更,确保变更准确实现,报告有关变更

  • 基线:各开发阶段的一个特定点,使开发阶段的工作划分更明确
  • 软件配置项 Software Configure Item,SCI,配置管理的基本单位
  • 版本控制
  • 变更控制

风险管理

风险类别

识别风险

风险预测/分析风险

评估风险/为每个风险分配优先级

RE:风险显露度/风险暴露(risk exposure)

其中,P是风险发生的概率,C是风险发生时带来的项目成本

风险评估

分别表示风险,概率,影响

风险杠杆 = (降低前的风险暴露-降低后的风险暴露)/(降低风险的成本)

风险控制


回归测试 regression testing:确保已有的功能仍能正常地工作

获取需求

关注于问题

需求过程

寻找需求:标识关键实体、限定实体、定义实体之间的关系

特定于实现的描述不被认为是需求(除非是客户强行规定的),需求阶段的目标是理解客户的问题和需要,集中于客户和问题,而不是解决方案和实现

规格说明(specification)阶段,将决定我们的软件系统将完成哪些需求;在设计(design)阶段,制定关于将如何实现指定行为的计划

需求过程的最终结果是软件需求规格说明SRS,它用于与其他软件开发人员(设计人员、测试人员、维护人员)交流,探讨最终产品行为

需求引发

风险承担者

委托人:为要开发的软件支付费用的人

客户:在软件开发后购买软件的人

用户:熟悉当前系统,并将使用最终系统的人

领域专家:对软件必须自动化问题很熟悉的那些人

市场研究人员:进行调查,以确定将来的趋势以及潜在客户的需要的那些人

律师或审计人员:对政府、安全性以及法律的需求熟悉的那些人

软件工程师或其他技术专家:确保在技术上和经济上是可行的

需求的类型

功能需求(functional requirement)根据要求的活动来描述需要的行为。

质量需求(quality requirement)/非功能性需求(nonfunctional requirement)描述一些软件解决方案必须拥有的质量属性:如快速的响应时间、易使用性、高可靠性或低维护代价

设计约束(design constraint)是已经做出的设计决策或限制问题解决方案的设计决策:如平台或构件接口的选择

过程约束(process constraint)是对用于构建系统的技术和资源的限制

解决冲突

需求可分为:必须的、值得要的、可选的

两种需求文档

需求定义 requirement definition:面向业务相关的人员

需求规格说明 requirement specification:面向技术性人员

需求的特性

  • 正确
  • 一致性
  • 无二义性
  • 完备
  • 可行
  • 相关
  • 可测试
  • 可跟踪

建模表示法

实体-联系图

entity-relationship diagram,ER diagram

3个核心结构:实体、属性和联系

实体entity:矩形,代表具有共同性质和行为的现实世界对象构成的集合(有时也称为类)

联系relationship:表示为两个实体之间的边,边的中间有一个菱形,说明联系的类型

属性attribute:是实体上的注释,描述实体相关的数据或性质

UML 类图

统一建模语言是用于文档化软件规格说明和设计的一组表示法

每一个方框是一个,表示一组相似类型的实体。一个类具有名称(name)属性(attribute)集和类的属性上的操作(operation)

属性是简单数据变量(即它的值太简单,本身不足以成为一个类),其值可以随着时间和类实体的不同而发生变化

类范围属性(class-scope attribute)用带下划线的属性表示,是被类的所有实例共享的数据值

类范围操作(class-scope operation)书写为带下划线的操作,是由抽象类执行的操作,而不是由类的实例执行的操作

两个类之间的连线称为关联,表示类的实体之间的关系

聚合关联(aggregate association)has-a关系,用空心菱形的关联来表示

组装(composition)关联是一种特殊类型的聚合,用实心菱形的聚合来表示

一端带有三角形的关联表示概化(generalization)关联,也称为子类型或is-a关系

关联类(association class),将属性和操作联系到关联上

事件踪迹

event trace

事件踪迹是关于现实世界实体之间交换的事件序列的图形描述

每一条竖线表示不同实体的时间线,其名字出现在线的顶部。每一条水平线表示两个实体之间的一个事件或交互,通常理解为从一个实体到另一个实体的消息传递。时间按从顶到下踪迹进展。每一个图描述一个踪迹(trace),表示的是若干可能行为中的一个

消息时序图

Message Sequence Chart

是扩充的事件踪迹表示法。具有创建和撤销实体、指定动作和计时器,以及组合事件踪迹的能力;一般只用于描述关键的场景,而不是说明整个问题。

每一条竖线表示一个参与的实体,而将一个消息描述为从发送实体到接收实体的箭头,箭头上的标记制定该消息的名称和数据参数。一个消息箭头可以向下斜,体现了消息发送时间和消息接受时间之间的时间段。虚线的箭头表示创建事件,产生新的实体。实体线底部的交叉符号表示实体执行的终结。动作(action)为位于实体执行线上的带标记的矩形。条件(condition)带标记的六边形。

状态机

state machine

用于在单个模型中表示一组事件踪迹,是一种图形描述,描述了系统与其环境之间的所有对话。每一个节点称为状态,表示存在于事件发生之间的一个稳定的条件集合。每一个边称为转移(transition),表示由一个事件的发生而产生的行为或条件的变化。每一个转移都标记有触发事件,还可能有输出事件

UML 状态图

用于描述一个UML类中对象的动态行为。根据涉及的实体与实体之间的关系,刻画出了一个问题的静态的、总体的视图。没有说明实体是如何运转的,或对于输入事件,行为是如何变化的。而是说明的是,一个类的实例应该如何改变其状态,以及在这些对象彼此交互时,它们的属性值是如何改变的,即单个实体是如何响应输入事件以及产生输出事件的。

通常与MSC(消息时序图)【两个实体之间的消息传递】相辅相成。

具有公共转移的状态合并进超状态(superstate),可以将超状态看作是子状态机,具有自己的状态和转移集。


转移标记的语法:event(args) [condition] \ action* ^Object.event(args)*

触发事件是一个可能携带参数的消息;激活条件由方括弧括起来,是关于对象属性的谓词。如果一个转移发生,其动作(每一个的前面带有斜杠"/")表示对象属性的赋值;星号"*"表明一个转移可能含有任意多个动作。如果该转移发生,它可能生成多个输出事件,表示为/^对象.事件(/^Object.event)。一个输出事件可能携带参数,或者指定目标对象,或者广播到所有对象。

可以没有触发事件、条件等,表示总是被激活的


动作&活动:

动作是一个相对没有时间花费的计算,并且是不可中断的;活动是更为重要复杂的计算,它执行一段时间,并且可以被中断。

Petri图

状态-转移表示法的一种形式,用于建模并发活动以及它们之间的交互;Petri网中的圆圈称为位置,表示活动或条件;条表示变迁;有向的箭头称为弧,将变迁与其输入位置和输出位置连接在一起。位置中放置的是令牌(token),作为变迁的启动条件。当一个变迁被触发时,就清楚每一个输入位置中的令牌,并将令牌插入每一个输出位置。为每一条弧分配一个权重,指出在变迁触发的时候,在弧的输入位置清除了多少令牌,或者在弧的输出位置插入了多少令牌。

数据流图

data-flow diagram,DFD

建模功能以及从一个功能到另一个功能的数据流;一个泡泡表示一个加工(process)或功能,它转换数据。箭头表示数据流(data flow)。单个计算使用之外的持久数据保存在数据存储(data store),它是一个正式的库或信息库,表示为两个平行的条。数据源或数据接收器表示为矩形,称为参与者(actor)

优势:提供了两种直观的模型:一种是关于被提议系统的高层功能的,一种是各种加工之间的数据依赖关系。

最好作为问题的早期模型使用,因为此时细节并不是很重要。

UML用例图

use-case diagram

类似于顶层的数据流图,它根据系统和系统的环境之间的交互,描述可观察到的、用户发起的功能。大的方框表示系统的边界;方框外的小人描绘的是参与者,包括人或系统;方框之内的椭圆是用例,表示必需的主要功能及其变种;参与者和用例之间的线表明参与者参与了该用例。用例不一定建模系统应该提供的所有任务,而是用于说明用户对重要系统行为的观察。

虚线箭头,标注构造型<<include>>或者<extend>,分别表示调用和扩展

函数与关系

基于数学的规格说明和设计技术称为形式化方法

当一个输入值映射到多个输出值的时候,就使用关系

每一个输入都映射到单个输出,则这个函数定义上是一致的;如果一个函数为每一个不同的输入指定一个输出,则称为全函数,并且定义上是完备的

判定表

decision table

是函数规格说明的表格式表示,将事件和条件映射到适当的反应或动作上。

该规格说明是非形式化的,因为输入(事件和条件)和输出(动作)可以用自然语言表示,也可以用数学表达式表示,或者同时使用两者。

如果有$n$个输入条件,就可能会有$2^n$个条件的组合。

Parnas表

是数学函数和关系的表格式表示;与判定表不同的是,Parnas表的输入和输出是纯数学表达式。

逻辑

描述性的表示法是,根据问题或提议的解决方案的性质或者不变行为来对它进行描述的表示法,更适合于表达全局性质或约束

逻辑由一种表述性质的语言加上一组推理规则组成,这些推理规则用于规定的性质导出新的、合乎逻辑的性质。

对象约束语言

Object control language

具有数学精确性,又对非数学专业人员易读、易写、易理解的约束语言

(感觉像是类似于数据库的E-R图+类图)

Z

发音为zed

形式化的需求规格说明语言,将集合论的变量定义组织到一个问题的完整的抽象数据类型模型当中,并使用逻辑来表示每一个操作的前置条件和后置条件。

Z利用软件工程的抽象方法将规格说明分解为可管理的模块,称为模式(schema)

SDL数据

SDL数据用于创建规格说明和描述语言(SDL)中的用户定义数据类型以及参数化的数据类型

操作:生成操作(构建定义数据类型的规范表示)、操纵操作(返回值)、查询

需求和规格说明语言

一个完整的规格说明可能包含若干种模型,其中每一种说明系统的一个不同的方面

统一建模语言UML

UML标准包含8种图形建模表示法,再加上OCL约束语言:

  • 用例图
    • 一种高层的DFD(数据流图)
    • 用在新项目的开始阶段,记录基本顶层功能
  • 类图
    • 一种ER图
    • UML规格说明中最重要的模型,强调问题的实体以及实体之间的相互关系
  • 时序图
    • 一种事件踪迹
    • 开发阶段早期的行为模型
    • 强调时间顺序
  • 合作图
    • 一种事件踪迹
    • 强调类之间的关系
  • 状态图
    • 一种状态机模型
  • OCL特性
    • 逻辑
    • 描述建模元素的特性

规格说明和描述语言SDL

用于精确说明通过无限的消息队列通信实时、并发、分布处理行为

包含3个主要的图,外加定义复杂数据类型的代数规格说明

  • SDL系统图
    • 一种DFD
    • 描述规格说明的高层块和连接各个块的通信信道。信道是有向的。
    • 信道的消息传递是异步的,意味着不能对消息将被接收的时间做任何假设
  • SDL方框图
    • 一种DFD
    • 建模低层的一组块以及连接它们的消息延迟信道
    • 同一模块中的进程之间的消息传送是即时接收的(高度耦合的进程,并行同步的进程应该处于同一模块中)
  • SDL进程图
    • 一种状态机模型
    • 其转移是语言构成成分的序列(输入、决策、任务、输出),起始或终止于状态的构成成分
  • SDL数据类型(代数规格说明)
    • 用于声明复杂的、用户定义的变量类型

软件成本降低SCR

鼓励采用好的软件工程设计原则而设计的一组技术,将软件需求建模为数学函数,该函数将监测变量映射为控制变量。每一个函数都负责设置一个控制变量的值或一个项的值。

原型化需求

两种方法:(均称为快速化原型)

  • 抛弃型原型
    • 快速不考虑质量,确定问题的答案后抛弃
  • 演化型模型
    • 会演变成最终的产品

需求文档

需求定义:(客户的术语记录需求)

  1. 概述系统的总体目的和范围,并列出任何可能有用的术语、指称和缩写
  2. 描述系统的开发的背景和理由
  3. 描述可接受解决方案的基本特性
  4. 描述系统将运转的环境
  5. 描述客户对解决问题的提议
  6. 列出对环境行为所做的一切假设

需求规格说明:(开发人员的角度编写的,按照系统的接口)

  1. 详细描述所有的输入和输出(包括源、目的地、值的范围、数据格式、协议等等)
  2. 根据接口的输入和输出陈述要求的功能
  3. 对每一个客户的质量需求,设计适配标准

过程管理(它跟踪):

  • 定义系统应该做什么的需求
  • 需求生成的设计模块
  • 实现设计的程序代码
  • 验证系统功能的测试
  • 描述系统的文档

确认和验证

需求确认(requirements validation),检查需求定义是否准确地反映了客户(风险承担者)的需要,通过走查正式审查需求评审

验证(verification),检查一个文档或制品是否符合另一个文档或制品【在这一阶段,验证需求规格说明是否符合需求定义)

测量需求

对需求理解进行评分

选择规格说明技术

选择合适的标准,用标准来判断哪一个技术是最适合它的

设计体系结构

将系统分解为规模可管理的单元

设计

客户及相应的开发人员,会经常在最初的需求分析完成后适当地修改需求

体系结构风格:软件体系结构一般性的解决方案

设计模式design pattern:是一种针对单个软件模块或少量模块而给出的一般性解决方案,它提供相对较低层次的设计决策

设计公约design convention/idiom:是一系列设计决策和建议的集合,采用这些设计决策和建议,能够提高系统某方面的设计质量

创新设计:基于基本的设计原则,创造全新的解决方案;与例程设计(评估更加严格、困难,且评估侧重于专家评估而不是客观标准,不太准确)相反

每个计划用来描述体系结构的一个方面或视图

体系结构建模

使用体系结构模型的方式有以下6种:

  • 理解系统:它将做什么,以及如何做
  • 确定该系统的哪部分将复用前面已经构建的系统中的元素,以及系统哪些部分将会被复用
  • 展示构建系统的蓝图,包括系统可能的”承重”部分
  • 推测系统将会如何演变,包括性能、成本以及原型开发的问题
  • 分析依赖关系,选择合适的设计、实现和测试技术
  • 为管理决策提供支持,了解实现和维护时系统固有的风险

分解

“自顶向下”的方法

  • 功能性分解(根据功能或需求)
  • 面向特征的设计(为每个模块指定了各自的特征)
    • 高层:描述了具有某个服务和特征集的系统
    • 低层:描述了各个特征如何扩展服务,以及确定特征之间如何进行交互
  • 面向数据的分解
    • 高层:概念上的数据结构
    • 低层:提供细节
  • 面向进程的分解
    • 高层:确定系统的主要任务,为进程指派任务;解释任务之间是如何协调工作的
    • 低层:过程的实现细节
  • 面向事件的分解
    • 高层:将系统预期的输入编成目录
    • 低层:将系统分解为状态,并描述事件是如何触发状态转移的
  • 面向对象的设计
    • 高层:定义了系统对象的类型,解释了对象之间是如何关联的
    • 低层:细化了对象的属性和操作

视图

不仅可以展示构件间如何交互,而且能够描述这个系统的分布,或者它能够展示系统提供的所有服务以及这些服务间如何协调操作的视图

体系结构视图一般包括:

  • 分解视图
    • 将系统描述为若干个可编程的单元
  • 依赖视图
    • 展示了软件单元之间的依赖关系
  • 泛化视图
    • 展示了一个软件单元是否是另一个单元的泛化或特化
  • 执行视图
    • 方框-箭头图
    • 构件可能会拥有自己的程序栈
    • 连接器是一种构件之间的通信机制
  • 实现视图
    • 代码单元和源文件之间建立映射
  • 部署视图
    • 运行时实体和计算机资源之间建立映射
  • 工作分配视图
    • 将系统分解成可以分配给各项目团队的工作任务

体系结构风格和策略

体系结构风格是已建立的、大规模的系统结构模式

可以组合,有若干种组合方式:

  • 在系统分解的不同级别使用不同的风格
  • 体系结构可以使用一个混合的风格来为不同的构件或者构件间不同类型的交互建模
  • 当体系结构风格之间可以互相兼容时,风格的集成将会更加容易

管道和过滤器

管道不对数据进行处理,只是简单地将数据从一个过滤器到另一个过滤器;每个过滤器都是独立的

客户-服务器

服务器提供服务;客户通过请求/应答协议访问服务

回调callback:客户向服务器发送一个可执行的函数

支持构件重用

对等网络

peer to peer, P2P

规模已扩展,但增加了系统的容量,对故障具有较好的容错性

每个构件本身既是客户端又是服务端

适合于文件是静态的、文件内容和性质并不是关键、共享速度和可靠性不是很重要的时候

发布-订阅

构件对其他构件的存在一无所知

隐含调用:订阅者将自己的某个过程与感兴趣的事件建立关联

不利于单独测试,若需要共享固定不变的数据增加的信息库会减弱系统的可扩展性和可复用性

不影响其他的构件,轻松复用

信息库

repository

由中心数据存储以及与其相关联的访问构件组成

对系统关键数据的中心式的管理

黑板:知识源(数据存取器)

分层

只可以访问同层中的其他单元和相邻低层的接口所提供的服务;但是有时也会跨越(层次桥接)

在任何情况下任何层都不能访问它上面的层次

质量属性的策略tatic

可修改性

策略:

  • 预测预期改变
  • 高内聚性
  • 低耦合
  • 通用性
  • 通过接口进行交互
  • 多重接口

性能

描述了系统速度和容量上的特点,包括:

  • 响应时间:对请求的反应有多迅速
  • 吞吐量:每分钟可以处理多少请求
  • 负载:在响应时间和吞吐量变糟糕之前,可以支持多少用户使用

策略:

  • 提高资源的利用率(并行、复制以及分布共享数据)
  • 有效地管理资源分配
  • 降低对资源的需求

安全性

策略:

  • 能够阻挡攻击企图,就是具有高免疫力的;

    • 在设计中保证包含了所有的安全性特征
    • 将可能被攻击者利用的安全性弱点最小化
  • 能够快速容易地从成功的攻击中恢复,就是具有高弹性

    • 把功能分段,这样攻击造成的影响只会存在于系统的很小一部分之中
    • 使系统能够在一小段时间里快速恢复功能和性能

可靠性

如果一个软件系统可以在假设的环境下正确地实现所要求的功能,则称这个软件系统是可靠的

如果在”不正确的输入或意外的环境条件下”还能正常地工作,则称它是健壮的

故障:不可见的错误,只有开发人员可以看到的错误

失效:可见的错误,是客户和用户所看到的问题

不是每个故障都会引起失效

策略:

  • 主动故障检测
    • 加入异常处理
    • 加入某种形式的冗余
      • n版本编程:如果两个功能相同的系统是由两个不同团队、在不同时间、使用不同的技术开发而成,那么这两种实现出现同样的故障的几率十分小
  • 故障恢复
    • 需要立即处理,减少破坏性
    • 随时做好恢复的准备的同时也会带来额外的开销
    • 撤销事务
    • 检验点/回退
    • 备份
    • 服务降级
    • 修正和继续
    • 报告

健壮性

互相怀疑策略:每个软件单元都假设其他软件单元中含有故障

策略与可靠性相同,但是健壮性的问题来源在于软件环境中,而不是软件本身之中

易使用性

策略:

  • 用户界面需要放置于自己的软件单元中,或者是自己的体系结构层次中
    • 为不同国籍、不同能力的用户定制不同的界面
  • 一些用户发起的命令需要体系结构的支持
  • 一些系统发起的活动要求系统维护一个环境模型

商业目标

开发成本和产品上市时间最小化

策略:

  • 购买 or 开发
  • 最初的开发成本 or 维护成本(提早交付 or 易于维护)
  • 新的技术 or 已知的技术

体系结构的评估和改进

故障树分析

构造故障树(倒置的树),列出从结果到原因的逻辑路径,然后并根据选择的设计策略,将这些树用于故障改正或容错【和数字电路相同,有与门、或门】

每个节点代表一个独立的事件

割集树

当故障树过于复杂或难以视觉分析的情况下,割集树就非常有用。它揭示了哪些事件的组合可以引起失效。

构造割集树的规则

  1. 给割集树的顶点分配节点,使得该节点与故障树顶部的第一个逻辑门相对应
  2. 自顶向下地进行,按以下步骤扩展割集树
    • 扩展或门节点得到两个子节点,分别是或门的两个子节点
    • 扩展与门节点得到一个合成的节点,由与门的两个子节点组合而成
    • 扩展组合节点中的一个逻辑门产生节点,并把该组合节点中的其他逻辑门传播到各子节点中去
  3. 持续进行,直到所有的子节点都是基本事件节点或者基本事件的合成节点

割集是割集树中子节点的集合,表示了引起树顶失效所需的事件的最小集合

安全性分析

六个步骤:

  1. 软件特征化
    对系统的目标以及实现方式有了比较完整的理解
  2. 威胁分析
    寻找威胁的来源、威胁活动
  3. 漏洞评估
    漏洞的原因不仅仅是错误,也可能是由多义性、依赖性或者拙劣的错误处理引起的
  4. 风险可能性决策
    对每个漏洞都要检查它暴露的可能性大小,需要考虑四个问题:动机(即某人或系统为什么具有威胁性);威胁利用漏洞的能力;漏洞暴露的影响(即破坏性有多大,破坏遗留的时间会持续多久,以及是通过谁);当前控制可以抵制漏洞暴露的程度
  5. 风险影响决策
    级别最高的是业务终止性的;低一级别的是损害性的;再下一级别是可恢复的;最低级别的影响是妨碍
  6. 风险缓解计划
    为降低最严重的风险的可能性和后果做计划

权衡分析

  • 一个规格说明,多个设计
  • 比较表
    • 包含重要质量属性的表
    • 表的每一行表示一个质量属性
    • 表的每一列表示一种设计风格
    • -表示没有该特性,+表示有该特性
    • 接着为质量属性排优先级,然后每个设计标记满意度,相乘后相加

成本效益分析

用来估计和比较提议的改变所带来的成本和效益

  1. 计算效益

    限定在一段时间内

  2. 计算投资回报 ROI
    ROI = 效益 / 成本

    投资回收期:在累积的效益抵消实现时消耗成本之前的时间长度

原型化

文档化软件体系结构

SAD需包含以下信息:

  • 系统综述:系统的主要功能和用途
  • 视图:每个视图都从某个特定的角度传达了关于系统整体设计结构的信息;还应当包括视图之间的联系
  • 软件单元:还应该包括它们的接口的精确规格说明
  • 分析数据和结果:关于系统体系结构、计算资源和执行环境的充分信息
  • 设计合理性
  • 定义、术语表

并且必须定义文档的版本号或者发行日期(确定使用了同一版本,并且是最新的)

建立视图间的映射

文档化设计合理性,概述在构建设计的过程中所考虑的关键问题以及做出的权衡

体系结构设计评审

确认(validating):确认设计是否符合客户指定的所有需求

验证(verification):涉及保证设计是否遵循了良好的设计原则,且该设计的文档是否适合用户的需要;又分为主动设计评审(在SAD中发现问题)和被动评审过程(在阅读文档时发现问题)重点是发现错误而不是改正错误

软件产品线

开发一系列相关产品的成本和工作量会远远小于单独开发这些产品的总和

产品线

产品系列

核心资产库:一个系列的共性(可复用资源的集合【需求、设计、代码和测试用例等】)

战略范围

设计模块

设计方法

  1. 使用自顶向下或者由外向内的方法,以关注系统的输入和期望的输出
  2. 首先探究设计中最难最不明确的一部分,因为隐蔽问题所引发的异常可能会迫使整个设计做出改变
  3. 按垂直切片的方式推进开发过程,每次都迭代地设计和完成各个功能子集(敏捷开发中)
  4. 使用自底向上的方法,尝试使用和调整已有的方案去解决该部分的设计

重构:为了简化过度复杂的方案,或者出于某特殊性能的考虑而对设计进行优化,会周期性地重审和修改设计决策

设计原则

设计原则:把系统功能和行为分解成模块的指导方针

六个主要的原则:

  • 模块化/关注点分离
    • 耦合度
      • 当两个模块之间有大量依赖关系时,则说这两个模块是紧密耦合的
      • 松散耦合的模块之间具有某种程度的依赖性,但是它们之间的相互连接比较弱
      • 非耦合的模块之间没有任何相互连接,它们之间是完全独立的
      • 耦合类型:(越后面耦合程度越低)
        • 内容耦合:被修改的模块完全依赖于修改它的模块
        • 公共耦合:对公共数据的改变意味着需要通过反向跟踪所有访问过该数据的模块来评估该改变的影响
        • 控制耦合:某个模块通过传递参数或返回代码来控制另一个模块的活动
        • 标记耦合:使用一个复杂的数据结构来从一个模块到另一个模块传送消息,并且传递的是该数据结构本身
        • 数据耦合:传送的只是数据值,而不是结构数据时【理想的】
        • 非耦合
    • 内聚度:模块的内部元素的”粘合”程度(越后面内聚程度越高)
      • 巧合内聚:模块的各个部分互不相关
      • 逻辑内聚:模块的各个部分只通过代码的逻辑结构相关联
      • 时态内聚:设计被划分为几个用来表示不同执行状态的模块
      • 过程内聚:组合在一起只是为了确保执行顺序
      • 通信内聚:围绕数据集构造的模块
      • 功能内聚:在一个模块中包含了所有必需的元素,并且每一个处理元素对于执行单个功能来说都是必需的(某个功能内聚的模块不仅执行设计的功能,而且只执行该功能,不执行其他功能)【理想的】
      • 信息内聚:在功能内聚的基础上,将其调整为数据抽象化和基于对象的设计
  • 接口:为系统其余部分定义了该软件单元提供的服务,以及如何获取这些服务
    • 一个对象的接口是该对象所有公共操作以及这些操作的签名的集合,指定了操作名称、参数和可能的返回值
    • 软件单元接口的规格说明描述了软件单元外部可见的性质
      • 目标:为每个访问函数的功能性建立充分详细的文档,以帮助其他开发人员找出最符合他们需要的访问函数
      • 前置条件:所有的假设,以帮助其他开发人员了解在何种情况下,该软件单元才能正确工作
      • 协议:协议的信息包括访问函数的顺序、两个构件交换消息的模式,比如,调用一个模块访问共享资源之前需要被授权允许
      • 后置条件:可见的影响,为其编写文档,包括返回值、引发的异常以及公共变量的变化
      • 质量属性:是对开发人员和用户可见的质量属性
  • 信息隐藏:目标是使得软件系统更加易于维护;单元的设计决策被隐藏了
  • 增量式开发
    • 使用关系:为各个软件单元和它依赖的单元之间建立关联
    • 使用图:节点代表软件单元,有向边从使用其他单元的软件单元A出发指向被使用的单元B【表示需要一个正确的B,才能保证A也能正确地工作】良好的使用图应具有树型结构或者树型结构的森林
    • 扇入fan-in:使用某个软件单元的软件单元数量
    • 扇出fan-out:某个软件单元使用其他软件单元的数量
    • 最终目的是创建高扇入、低扇出的软件单元
    • 夹层法:消除循环,将循环中的一个单元被分解成两个单元
    • 依赖倒置dependency inversion
  • 抽象:忽略一些细节来关注其他细节的模型或表示
  • 通用性:在开发软件单元时,使它尽可能地能够成为通用的软件,来加强它在将来某个系统中能够被使用的可能性
    • 软件工程原则之一就是可复用性:构造在将来的软件产品中仍可使用的软件单元,旨在通过复用来分摊开发的成本(分摊amortization是指,在计算软件单元的成本时,考虑每次使用的成本,而不是开发项目时的整个成本)
    • 实现规则:
      • 将特定的上下文环境信息参数化
      • 去除前置条件
      • 简化后置条件

面向对象的设计

如果一个设计将系统分解成若干个封装了数据和函数的运行时构件,即所谓的对象,那么该设计是面向对象的object oriented

对象区别于其他构件的特征:

  • 对象是唯一可标识的运行时实体,它们可以设计为消息或请求的目标
  • 对象是可组合的,因为它的数据变量本身可能也是对象,因而封装了对象的内部变量的实现
  • 对象的实现可以通过继承的方式被复用和扩展,用来定义其他对象的实现
  • 面向对象的代码可以是多态的:可以对多个不同但类型相关的对象都起作用的通用代码。相关类型的对象会对一些相同消息或请求做出响应,但不同类型的对象会有不同的响应

对象的数据称作属性,而对象的操作称作方法;对象之间通过发送消息、调用彼此的方法进行交互。

对象是运行时实体;面向对象设计往往是由对象的接口构成的。接口声明了外部可访问的属性和方法。通常,这些信息是指公共方法,同时包含了这些方法的签名、前置条件、后置条件、协议要求以及可见的质量属性

若一个接口所提供的服务是另一个接口所提供的服务的严格子集,那么我们称前者是后者的子类型后者称为前者的超类型


抽象类 动态绑定 对象组合 继承

类继承和对象组合之间的选择是一种涉及设计一致性、行为可预测性、设计决策的封装性、运行时性能和运行时可配置性的权衡

组合方法在保持被复用的封装性方面优于继承,因为组合的对象仅能通过它声明的接口来访问构件

组合的最大优点,在于它允许动态替换对象构件;缺点:很难仅通过研究代码,就能够想清楚或推理出程序的运行时结构,也并不能搞清楚一个对象到底引用了哪些对象,同时对象的引入了一层间接性,一个构件的方法的每一访问都必须先访问这个构件对象,可能会影响到程序运行时的性能

继承最大的好处就是,通过选择性地覆盖被继承的定义,可以改变和特化继承方法的行为,可以帮助我们快速创建具有新行为的、新的类型的对象。


可替换性:但是不是强制的,在很多设计中,不能满足

依赖父类的客户端代码在传送一个子类实例时,可能不能正常工作。

利斯科夫替换原则 Liskov Substitutability Principle描述了一下原则:(根据该原则,当同时满足以下条件时,一个子类对于其父类就是可替换的)

  1. 子类支持父类的所有方法,并且他们的签名是兼容的。即子类的方法的参数和返回类型对于父类方法的对应的参数和返回类型,是可替代的。

  2. 子类的方法必须满足父类方法的规格说明。即这两个类的方法行为不一定要完全相同,但是子类必须不违反父类方法的前置条件以及后置条件
    $pre_{\text{parent}} = pre_{\text{sub}} $

    子类的前置条件必须和父类的前置条件相同或弱于父类的前置条件

    $pre_{\text{parent}}\to (Post_{\text{sub}}\to Post_{\text{parent}})$

    子类方法所做的和父类一样多,甚至比父类的还多

  3. 子类必须保留父类中声明了的所有性质


组合可能会导致类之间形成大量的依赖关系

德米特法则 law of Demeter(不要和陌生人说话):通过把组合类中作用在类构件上的每个方法都包含进来,可以降低他们的依赖程度

比如:

  • generateBill()方法调用CustomerAccount中的printSaleItems();
  • printSaleItems()调用合适的Sale对象中的printItemList()
  • printItemList()调用合适的Item对象中的打印方法

往往会使用包装类 wrapper class,以在不改变现有类的实现的前提下增加新功能。

尽管包装类使得向组合类中增加操作的任务变得轻松了,但同时也会使得设计更加复杂以及降低运行时的性能。

因此,在决定是否遵循德米特法则的时候,要在设计复杂度、开发时间、运行时性能、防止故障和易维护性之间进行权衡。


依赖倒置:消除类形成的依赖循环

客户端依赖于服务器

将依赖类(客户端)增加接口,然后打包成一个新的模块;再将被依赖类(服务器类)创建包装类,将两者连接起来;这样当服务器类改变接口时,不会影响客户端


4种面向对象的结构(类、对象、 接口和实例变量)以及结构之间的关系,用箭头指示,箭头的末端的注释表明了关系的多重性(有时也称为”元数”)(几对几的关系)

在UML中体现面向对象设计

UML图包括了系统的动态视图、静态视图、约束和形式化

动态视图:

  • 用例图use case diagram
    • 通过描述系统必须执行的一般过程对系统进行描述
    • 活动图、领域模型对其进行补充
    • 展示系统必要的功能,以及与系统交互的主要参与者 actor
  • 活动列表
  • 表示顺序和通信的交互图
  • 表明状态及其变化的状态机

静态视图:

  • 类图

    • 用来表示关系【关联、泛化、依赖和实现】和可扩展性【约束、标记值和构造性】)
    • 关系类型
      • 直线 关联
      • 组合 黑菱形
      • 聚合 白菱形
      • 泛化 空心三角形
      • 依赖 虚线 实心三角形
      • 导航 实心三角形
  • 包图

    • 虚线表示包的依赖关系
  • 部署图
    • 如何为构件分配计算资源

约束和形式化:

  • 对象约束语言(OCL)

模块设计首先从类图开始;接下来,将对系统的动态方面进行设计,交互图:顺序图(垂直线表示该对象的生命线)和通信图、活动图、状态图;最后将这些类打包,使得设计更加层次化、更便于理解。最终的模型是包图。

可以首先提取名词,寻找可以作为类的元素。

面向对象设计模式

设计模式

面向对象的度量

系统规模:

  1. COCOMO
  2. 专化指数 SI=(NOO*深度) / 类的方法的总数

NOO:子类覆载override的操作数目

设计质量:

  1. 每个类的加权
  2. LCOM

其他

  1. 数据管理
  2. 异常处理:用来消除易检查的前置条件的最有效的方法
  3. 用户界面设计
  4. 框架:设计复用

设计文档

按合同设计

接口规格说明:

  • 需要
  • 前置条件
  • 协议
  • 提供
  • 后置条件
  • 协议
  • 质量属性

编写程序

编程应该具有标准,不仅方便自己的修改,查找故障,之后返回工作等等;同时也助于小组工作

每个程序构件都至少包括3个主要方面:

  1. 控制结构
    通用化但又不影响其性能
    文件开头列出构件之间传递的参数信息
  2. 算法
    执行时间与设计质量、标准和客户需求之间平衡考虑
    对于速度,需要了解编译器是如何优化代码的
  3. 数据结构:通常是为了适合于整体方案:促进信息隐藏和对构件接口的控制
    保持程序简单

通用性指导原则:

  1. 局部化输入输出
  2. 包含伪代码
  3. 改正和重写,而不是打补丁
  4. 复用

文档

程序文档

解释程序做什么以及如何做的书面描述

内部文档

直接书写在代码中的描述性素材

头注释块:提供概要信息以识别程序,描述数据结构、算法和控制流;通常以一组注释的形式放在每个构件的开始部分

必须包含:

  1. 构件名字
  2. 谁编写了这个构件
  3. 构件应该装配在整个系统设计的哪个地方
  4. 构件是在何时编写和修改的
  5. 为什么要有这个构件
  6. 构件是如何使用数据结构、算法和控制的

其他注释块:对代码的解释,增加的是新信息而不是从标记和变量名中显而易见的部分

有意义的变量名和语句标记

安排格式以增强理解:例如缩进

文档化数据

外部文档

设计人员不看代码,就评审外部文档

从系统的角度回答:who, what, where, when, how and why

描述问题:不是重复需求文档,而是对背景的概要讨论,解释什么时候调用构件以及为什么需要它

描述算法

描述数据

编程过程

  • 将编程作为问题求解
  • 极限编程
  • 结对编程

测试程序

测试并不是发现故障的第一事件,需求和设计评审也会帮助我们在开发的早期检查出存在的问题

测试的重点是发现故障

软件故障和失效

软件失效,意味着该软件没有做需求描述的事情

失效是系统某些方面的一个或多个故障造成的

故障不一定会产生失效

可能的原因:

  • 规格说明可能是错误的,或者遗漏了某个需求
  • 对于指定的硬件和软件,规格说明可能包含不可能实现的需求
  • 系统设计中可能包含故障
  • 程序设计中可能包含故障
  • 程序代码是错误

测试的目标是发现错误,只有当发现了错误或者由于测试过程而使得失效发生,一个测试才被认为是成功的

故障识别 fault identification是确定由哪一个故障或哪些故障引起失效的过程

故障改正 fault correction故障去除 fault removal则是修改系统使得故障得以去除的过程

故障的类型

  1. 算法故障Algorithmic fault:由于处理步骤中的某些错误,使得对于给定的输入,构件的算法或逻辑没有产生适当的输出。这些故障有时仅通过通读程序(称为桌上检查)或者通过提交我们期望程序在其平常工作过程中接收的、来自于每一个不同类别数据的输入数据,就能很容易地找出来。

    典型的算法故障包括:

    • 分支太早
    • 分支太迟
    • 对错误的条件进行了测试
    • 忘记了初始化变量或忘记了设置循环不变量
    • 忘记针对特定的条件进行测试
    • 对不合适的数据类型变量进行比较
  2. 语法故障syntax fault

  3. 计算故障computation fault:一个公式的实现是错误的

  4. 精度故障precision fault:计算结果没有达到要求的精度

  5. 文档故障documentation fault:文档与程序实际做的事情不一致

  6. 压力故障或过载故障:队列长度、缓冲区大小、表的维度等的限制

  7. 能力故障或边界故障:系统活动到达指定的极限时,系统性能会变得不可接受

  8. 计时故障或协调故障:几个同事执行的或按仔细定义的顺序执行的进程之间的协调问题

  9. 吞吐量故障或性能故障:系统不能以需求规定的速度执行

  10. 硬件和系统软件故障

  11. 标准和过程故障

恢复故障 recovery fault

如果被分类的任何一项都只属于多个类别,则分类方案是正交的

测试的组织

模块测试

构件测试

单元测试

集成测试:是验证系统构件是否能够按照系统和程序设计规格说明中描述的那样共同工作的过程

功能测试

验收测试

安装测试

系统测试

忘我编程 egoless programming的态度,当发现一个故障或出现一次失效时,忘我开发团队关注的是修改故障,而不是谴责某个开发人员

黑(闭)盒测试/白(开)盒测试

单元测试

  • 代码评审 code review 客观的是专家小组来评审代码及其文档
    • 代码走查 code walkthrough :程序员向评审小组提交代码及其相关文档,然后评价它们的正确性,发现故障,不必修改它们
    • 代码审查 code inspection:正式点,事前准备好的关注问题清单来检查代码和文档
  1. 形式化证明技术(用逻辑变量说明,画出流程图,证明定理以及程序可终止)
  2. 符号执行,用符号代替变量执行
  3. 自动定理证明

测试测试点/测试用例的集合

  • 语句测试:构件中的每条语句至少执行一次
  • 分支测试
  • 路径测试
  • 定义使用的路径测试
  • 所有使用的测试
  • 所有谓词使用/部分计算使用的测试
  • 所有计算使用/部分谓词使用的测试

事务流测试

集成测试

集成策略的选择不仅依赖于系统特性,而且依赖于客户的期望
无论选择什么样的策略,测试的每个构件都只合并一次。而且,绝不要为了简化测试而修改构件。桩和驱动程序是单独的、新的程序,而不是现有程序的临时修改

  1. 自底向上集成
    合并构件来测试较大型系统的流行方法
    每一个处于系统层次中最底层的构件首先被测试。接着要测试的是那些调用了前面已经测试构件的构件。反复地采用此方法,直到所有的构件都被测试完毕。
    当很多底层构件是常被其他构件调用的通用实用例程的时候,当设计是面向对象的时候,或者当系统及集成大量独立的复用构件的时候,自底向上的方法是很有用的。
    需要构件驱动程序,调用特定构件并向其传递测试用例的程序
    顶层构件是最重要的,却是最后测试的构件。顶层指导主要的系统活动,而底层通常执行更为普遍的任务。顶层更概括,而较低层次更为具体。主要故障的发现会推迟到测试的后期(有时顶层的故障反映的是设计中的故障)顶层构件通常控制或影响计时,当系统的大部分处理都依赖于计时的时候,就很难自底向上地测试系统
    对于面向对象的程序来说,自底向上的测试通常是最明智的选择。
  2. 自顶向下集成
    顶层构件,通常是一个控制构件,是独立测试的。然后将被测构件调用的所有构件组合起来,作为一个更大的单元进行测试。重复执行这种方法,直到所有的构件都被测试。
    正在测试的构件可能会调用还没有经过测试的别的构件,因此我们需要编写一个桩 stub,这是一种专用程序,用于模拟缺少构件时的活动。但是编写桩比较困难,必须允许测试的所有可能的情况。并且测试中可能需要大量的桩
    不需要驱动程序
    修改以后:可以在合并之前单独测试构件,比如原来是(A) -> (A,B,C);修改后是(A) -> (B) (C) -> (A,B,C)
  3. 一次性集成
    当所有构件都分别经过测试,再将它们合在一起作为最终系统进行测试,看看这个系统是否能一次运行成功
    同时需要桩和驱动程序;所有的构件是一次性地进行合并,很难发现引起失效的原因;很难将接口故障与其他故障区分开来
  4. 三明治集成
    将自底向上和自顶向下结合起来
    将系统看成三层:目标层在中间
    在顶层使用自顶向下的方法,而在较低层次使用自底向上方法
    修改:和2一样,在合并之前单独测试较上层的构件

测试面向对象系统

测试应该处理多个不同的层次:功能、类、聚集(协作对象的多组交互)和整个系统

和传统测试来说,在源代码分析、覆盖分析、测试用例生成、需求分析和验证方面不同

测试计划

测试计划:描述我们将以何种方式向客户证明软件运转正确(即软件没有故障并且执行需求中指定的功能)

不仅强调单元测试、集成测试,还包括系统测试
Who、Why、How、When

  1. 制定测试目标
  2. 设计测试用例
  3. 编写测试用例
  4. 测试测试用例
  5. 执行测试
  6. 评估测试结果

测试工具

代码分析工具

报告代码本身和正在运行的测试用例的相关信息

  • 静态分析:在程序没有实际执行时使用的分析工具
    • 代码分析器:语法错误/概念易出现故障或者未定义;产生符号表
    • 结构检查器:将提交的构件作为输入,生成一张描述构件逻辑流的图。该工具检查结构方面的缺陷;确定循环的位置,标记永远不会执行的语句等
    • 数据分析器:检查数据结构、数据声明和构件接口,然后指出构件间不合适的链接、冲突的构件定义以及不合法的数据使用;当分母为零的时候,进行通知
    • 序列检查器:检查事件序列
  • 动态分析:当程序运行时进行的,称为程序监控器(监视并报告程序的行为)

测试执行工具

  • 获取和重放: 在测试运行时获取键击、输入和响应,并且将期望的输出与实际的输出进行比较
  • 桩和驱动程序
  • 自动化的测试环境

测试用例生成器

结构化测试用例生成器

停止测试

刚开始发现的错误多的话,说明之后的错误也很多;因此估算剩余故障是一个重要的环节

故障播种/错误播种

测试小组的一名成员在程序中有意地插入(播种)一定已知数目的故障,其他小组成员则尽可能多地查找故障

但是播种故障与程序中的实际故障难以保证时同种类型的而且复杂性相同;因此此方法,仅在以前构建过类似的系统时才有用。

通常情况下,我们会使用两个小组来查找,然后计算他们的有效性是多少,然后再计算出剩余的故障(有效性也要算进去)

可信度

通常用一个百分数表示,它说明软件无故障的可能性

直到发现了s个故障后

更改后,不用发现全部s个故障

这些估算假定所有故障被检测到的概率相同

其他标准

通过计算代码的覆盖值

识别出易出故障的代码

通过分析树进行判断

测试系统

目标:确保系统能做客户想要它做的事情

过程

  1. 功能测试
  2. 性能测试
  3. 验收测试:由客户进行的测试,判断是否满足他们的需求(从客户的角度考虑)
  4. 安装测试

递增测试需要仔细的计划,测试小组必须创建一个构建计划或集成计划来定义要测试的子系统,并且描述如何、何处、何时和由谁进行测试。构建计划的一个层次或子系统被称为一次旋转。旋转有编号,最低的层次称为旋转0

配置管理

系统配置:是向特定客户交付的一组系统构件

配置管理控制不同系统配置之间的差别,将风险和错误减少到最低程度

  1. 版本发布
  2. 回归测试
  3. delta、单独文件和条件编译
  4. 变化(变更)控制

测试小组

  • 专业测试人员:组织并运行测试
  • 分析员:参与最初需求定义和规格说明
  • 系统设计人员:使测试小组的工作更具目的性
  • 配置管理代表:当出现失效或变化请求的时候,配置管理专家安排该变动,使其反映在文档、需求、设计、代码或其他开发制品中
  • 用户:最具资格来评价

功能测试

有时候也称为”线程测试”

因果图

该过程对需求的语义进行检查,并将输入和输出之间或输入和转换之间的关系重新表述为逻辑关系。

输入称为原因,输出和转换称为结果。其结果是一张反映这些关系的布尔图,称为因果图。

性能测试

  • 压力测试:当系统在短时间内到达其压力极限时,对系统进行的测试
  • 容量测试:强调的是处理系统中的大量数据
  • 配置测试:分析需求中指定的各种软件和硬件配置
  • 兼容性测试:当一个系统与其他系统交互时,需要进行兼容性测试
  • 回归测试:当正在测试的系统要代替一个现有系统的时候,必须进行回归测试
  • 安全性测试:确保安全性需求得到满足
  • 计时测试:评估涉及对用户的响应时间和一个功能的执行时间的相关需求
  • 环境测试:考察系统在安装场所的执行能力
  • 质量测试:评估系统的可靠性、可维护性和可用性;计算平均无故障时间和平均修复时间以及发现和修复一个故障的平均时间
  • 恢复测试:强调的是系统对出现故障或丢失数据、电源、设备或服务时的反应
  • 维护测试:为了帮助人们发现问题的根源提供诊断工具和过程的需要
  • 文档测试:确保我们已经编写了必需的文档
  • 人为因素测试:检查涉及系统用户界面的需求;也称为”可使用性测试”

可靠性、可用性以及可维护性

可靠性指的是一段时期内的行为,而可用性指的是一段时间内某一特定时刻的事物

失效严重性级别:

  1. 灾难性的
  2. 危机的
  3. 边缘性的
  4. 轻微的

第一类不确定性:不能预测哪个故障将触发下一个失效

第二类不确定性:对去除故障的效果缺乏了解


平均无故障时间:MTTF

平均修复时间:MTTR

平均失效间隔时间:$MTBF=MTTF+MTTR$

可靠性:$R=MTTF/(1+MTTF)$

可用性:$A=MTBF/(1+MTBF)$

可维护性:$M=(1+MTTR)$

随着发现和修复故障,如果失效间隔时间保持不变,则称其具有可靠性稳定性;如果失效间隔时间增加了,则称其具有可靠性增长


可靠性模型:

  1. Jelinski-Moranda
  2. Musa
  3. Littlewood

验收测试

种类:

  1. 基准测试:客户准备一组代表在实际安装后系统运作的典型情况的测试用例
  2. 试验性测试:在试验的环境中安装系统
  3. 并行测试:新的与旧系统一起并行运转
  1. $\alpha$测试:内部测试
  2. $\beta$测试:客户的试验

测试文档

为了控制测试的复杂性和难度,我们使用完整的、详细设计的测试文档

  • 测试计划

    描述系统本身以及测试所有功能及特性的计划

  • 测试规格说明和评估

    详细描述每一个测试,以及为测试针对的每一个特征定义评估标准

  • 测试描述

    为每一个单独的测试提供测试数据和过程

    测试过程又称为测试脚本

  • 测试分析报告

    描述每一个测试的结果

  • 问题报告表

    来比较故障和失效的相关数据

  • 差异报告表

    是描述实际系统的行为和属性与我们预期的不相符的这类情况的问题报告

  • 故障报告表

    说明故障是如何发现和修复的

测试安全攸关的系统

当一个系统在$10^9$小时内最多出现一次失效时,这个系统具有极端高可靠性

可以对设计的每一个构件分配失效率或约束,先达到每一个较低层的目标,将这些目标”积累起来”,最终达到整个系统的安全性目标。通过这种方式,我们为系统做出安全性案例,明确软件要达到的安全攸关系统的性能目标的方式

故障树分析

帮助我们检查可能的后果、跟踪故障以回溯到其根源

失效模式和后果分析FMEA是对故障树的补充,从已知的失效模式推出未知的系统后果

危险是一个系统状态,与正好满足的条件一起,将导致一个事故

失效模式是一种情形,使得危险发生


净室方法

两个基本原理:

  1. 根据规格说明证明软件,而不是等待单元测试发现故障
  2. 产生零故障或接近零故障的软件

交付系统

从开发人员转向用户的两个关键问题:培训和文档

培训

用户执行系统的主要功能,以帮助解决需求定义文档中描述的问题。因而,用户是客户的问题解决者

操作员执行辅助任务功能的目的是支持主要的工作

分为:

  1. 用户培训

    主要是基于系统的主要功能以及用户使用它们的需要对用户进行的培训

    不必了解系统的内部操作

  2. 操作员培训

    熟悉系统支持功能。该培训针对的是系统是如何工作的,而不是系统做些什么。

    如何启动和运行新系统;如何支持用户。首先操作员学习如何配置系统,如何授权或拒绝对系统的访问,如何分配任务大小或磁盘空间,以及如何监控和改进系统性能等诸如此类的事情。然后,操作员集中于开发的系统的细节:如何恢复丢失的文件或文档,如何与其他系统通信,以及如何调用各种支持过程

  3. 特殊培训需求

    用户更换,系统更新

培训助手:

  1. 文档
  2. 图元和联机帮助
  3. 演示和上课
  4. 专家用户

文档

在需求分析一完成,就开始计划培训和文档了

种类:

  1. 考虑读者
  2. 用户手册(系统的目的或目标;系统的能力和功能;系统的特征、特性和优点,包括对系统所做工作的清晰展示
  3. 操作员手册(硬件和软件配置)
  4. 系统概况指南(针对客户)
  5. 教学软件和自动化系统的概述
  6. 其他的系统文档

用户帮助和疑难解答

  1. 失效消息参考指南
  2. 联机帮助
  3. 快速参考指南

维护系统

任何针对系统改变所做的工作,都被认为是维护

种类:

  1. S系统

    由规格说明形式化定义的,并且是由规格说明导出的。

    静态的,不容易适应问题中产生的变化

  2. P系统

    先抽象描述问题,然后根据抽象编写系统的需求规格说明

  3. E系统

    融入在现实世界中的系统,并随着现实世界的变化而改变

维护:

  1. 改正性维护:立即作出反应
  2. 适应性维护:对系统的一部分进行的改变会要求改系统的其他部分
  3. 完善性维护:利用基于规则的方法进行重新设计,可以增强将来的可维护性,以及使我们在将来更易于增加新的功能
  4. 预防性维护:预防失效的发生

维护问题:

  1. 有限的理解力

    共m个构件,改变k个构件

    则需要评估的数目:$k(m-k)+k(k-1)/2$

  2. 管理的优先级:超过技术优先级

  3. 士气

软件再生

需要考虑:文档重构(静态分析)、重组(结构改变)、逆向工程(根据代码重新创建设计和规格说明信息)、再工程(先进行逆向工程,再进行再工程)

评估产品、过程和资源

评估方法

  1. 特征分析

    各属性进行评分和排列

  2. 调查

  3. 案例研究

    确定影响结果的关键因素,随后记录下输入、约束、资源以及输出

    用姐妹项目、基线、随机选择来避免偏见

  4. 正式的试验

    严格的、受控的研究,他确定并操纵活动的关键因素,记录它们对结果的影响

    自变量、因变量

评价与预测

评价:现有实体

预测:预测将来实体的某些属性

评估产品

产品质量模型:

  1. Boehm

    可移植性、可靠性、完整性、一致性、可测试性、可理解性、可修改性、人类工程、效率、可维护性

  2. ISO 9126

    功能性、可用性、效率、可维护性、可移植性、可靠性

  3. Dromey

    正确性、内部的、上下文的、描述性的

基线:一般的/典型的结果

目标:最小可接受行为的方式

软件可复用性

软件复用:指重复使用软件系统的任何部分,包括文档、代码、设计、需求、测试用例和测试数据等

从复用者来看有两种复用:

  • 生产者复用

    创建可复用的构件

  • 消费者复用

    是在以后的系统中使用它们

    • 黑盒复用:我们可以使用整个产品,而不用对它进行修改
    • 透明盒/白盒复用:对其进行修改,使之符合特殊需要

  • 组合式复用

    将复用的代码看作是一组构造块,自底向上进行开发;它围绕可用的复用构件来构造系统的其他部分

  • 生成式复用

    针对于具体的应用领域

其根本的活动是领域分析


  • 水平复用

    跨多个领域的复用

  • 垂直复用

    同一个应用领域中的复用


复用的一个最大障碍是需要在大量的软件产品中进行搜索,以找出最适合于具体需求的软件产品

解决方案是:构件分类

  • 刻面分类

    每个构件是通过称为”刻面”的特性的一个有序列表来描述,而不是使用层次方案

    刻面是一个描述符,有助于标识构件

  • 分类系统由一个检索系统或库作为支撑

评估过程

  • 事后分析

    其目的是识别在将来的项目中可以改进的方面

  • 过程成熟度模型

    • 能力成熟度模型CMM

      ​ 初始级->可重复级->定义级->管理级->优化级

      分别通过 过程制度化-过程定义-过程控制-持续的过程改进

      以及通过 项目管理-工程化管理-量化的管理-变更管理

      接下来分别对每一个等级说明关键过程域

      • 初始级:无
      • 可重复级:需求管理、软件项目计划、软件项目跟踪及监督、软件分包合同管理、软件质量保证、软件配置管理;SADT图:左端输入、右端输出、约束在顶部、资源在底部
      • 定义级:组织机构过程的焦点、组织机构过程的定义、培训计划、集成软件管理、软件产品工程、小组间协作、同行的评审;文档化的、标准化的和集成的
      • 管理级:量化的过程管理、软件质量管理;重点是提高产品质量
      • 优化级:故障预防、技术变更管理、过程变更管理;融入了量化的反馈以得到持续的过程改进
    • SPICE:旨在协调并扩充现有的方法;既用于过程改进,也用于能力确定

      • 未执行级
      • 非正式执行级
      • 计划和跟踪级
      • 定义明确级
      • 量化控制级
      • 持续改进级
    • ISO 9000

评估资源

人员成熟度模型

等级表示一样

  • 初始级
  • 可重复级:管理层负责管理其人员
  • 定义级:基于能力的劳动力实践
  • 管理级:测量和管理有效性,发展高绩效团队
  • 优化级:持续的知识和技能提高

投资回报

净现值NVP:对评估软件相关的投资最有意义,用总的项目生命周期的形式表述经济价值,不考虑等级或时间限制;是收益的现值减去最初投资的值

现值:预测的将来现金流量在今天的值

贴现率

机会成本

改进预测、产品、过程和资源

改进预测

预测可能是不精确的,体现在两个不同的方面:

  1. 当预测与产品实际可靠性始终不一致时,称预测时有偏误的
  2. 当对一种测量的连续预测比实际可靠性具有更剧烈的波动时,称预测是有噪声的

处理偏误:U曲线:通过预测下一次失效的时间,然后测量,比较

预测和实际观察之间存在偏差:Kolmogorov距离

处理噪声:prequential似然度

重新校准预测:模型比以前更趋近于一致;与初始模型相比,新模型的偏误更少

改进产品

  1. 审查
  2. 复用

改进过程

  1. 过程和能力成熟度
  2. 维护
  3. 净室方法

改进资源

  1. 工作环境
  2. 成本和进度的权衡

指导原则

  1. 目标是相同的吗?
  2. 目标的优先级是相同的吗?
  3. 问题是相同的吗?
  4. 测度是相同的吗?
  5. 成熟度是相同的吗?
  6. 过程是相同的吗?
  7. 受众是相同的吗?

软件工程的未来

一些回顾和难题

后记

2019.05.07

终于看完了,完成了ANTLR的解释器,感觉还ok

完成了stereo的第一部分

接下来还有

  • 程序分析设计、project management、系统分析与设计的presentation
  • stereo的剩下两个部分
  • 算法两次上机作业
  • 100个政治知识点(领导晚点来检查,我好像只看了第一个
  • B类实验测试
  • ……奇奇怪怪得事情,比如:我好久没练字了!!!!!!!!!!!

转载请注明出处,谢谢。

愿 我是你的小太阳

买糖果去喽