第七章 软件维护 一、复习要求 1. 了解软件质量定义和软件质量度量。 2. 了解软件维护的类型与策略。 3. 了解软件维护的过程与管理方法。 4. 了解可维护性的概念。 5. 了解提高可维护性的方法。 6. 了解软件逆向工程与再工程的概念 二、内容提要 1. 软件质量的概念 (1) 软件质量的定义 关于软件质量的定义,曾给出过多种定义。 ( ANSI/IEEE Std 729-1983定义软件质量为“与软件产品满足规定的和隐含的需求的能力有关的特征或特性的全体”。 ( M.J. Fisher 定义软件质量为“所有描述计算机软件优秀程度的特性的组合”。 也就是说,为满足软件的各项精确定义的功能、性能需求,符合文档化的开发标准,需要相应地给出或设计一些质量特性及其组合,作为在软件开发与维护中的重要考虑因素。如果这些质量特性及其组合都能在产品中得到满足,则这个软件产品质量就是高的。 软件质量反映了以下三方面的问题: ( 软件需求是度量软件质量的基础。不符合需求的软件就不具备质量。 ( 规范化的标准定义了一组开发准则,用来指导软件人员用工程化的方法来开发软件。如果不遵守这些开发准则,软件质量就得不到保证。 ( 往往会有一些隐含的需求没有显式地提出来。如软件应具备良好的可维护性。如果软件只满足那些精确定义了的需求而没有满足这些隐含的需求,软件质量也不能保证。 软件质量是各种特性的复杂组合。它随着应用的不同而不同,随着用户提出的质量要求不同而不同。因此,有必要讨论各种质量特性,以及评价质量的准则,还要介绍为保证质量所进行的各种活动。 (2) 软件质量特性与质量模型 软件质量特性,反映了软件的本质。讨论一个软件的质量,问题最终要归结到定义软件的质量特性。而定义一个软件的质量,就等价于为该软件定义一系列质量特性。 人们通常把影响软件质量的特性用软件质量模型来描述。已有多种有关软件质量模型的方案。它们共同的特点是:把软件质量特性定义成分层模型。最基本的叫做基本质量特性,它可以由一些子质量特性定义和度量。子质量特性在必要时又可由它的一些子质量特性定义和度量。 早在1976年,由Boehm等提出软件质量模型的分层方案。1979年McCall等人改进Boehm质量模型又提出了一种软件质量模型。模型的三层次式框架如图7.1所示。质量模型中的质量概念基于11个特性之上。而这11个特性分别面向软件产品的运行、修正、转移。它们与特性的关系如图7.2所示。McCall等认为,特性是软件质量的反映,软件属性可用做评价准则,定量化地度量软件属性可知软件质量的优劣。 图7.2 McCall软件质量模型 McCall等人的质量特性定义如下: 正 确 性 在预定环境下,软件满足设计规格说明及用户预期目标的程度。它要求软件本身没有错误。  可 靠 性 软件按照设计要求,在规定时间和条件下不出故障,持续运行的程度。  效 率 为了完成预定功能,软件系统所需的计算机资源的多少。  完 整 性 为某一目的而保护数据,避免它受到偶然的或有意的破坏、改动或遗失的能力。  可使用性 对于一个软件系统,用户学习、使用软件及为程序准备输入和解释输出所需工作量的大小。  可维护性 为满足用户新的要求,或当环境发生了变化,或运行中发现了新的错误时,对一个已投入运行的软件进行相应诊断和修改所需工作量的大小。  可测试性 测试软件以确保其能够执行预定功能所需工作量的大小。  灵 活 性 修改或改进一个已投入运行的软件所需工作量的大小。  可移植性 将一个软件系统从一个计算机系统或环境移植到另一个计算机系统或环境中运行时所需工作量的大小。  可复用性 一个软件(或软件的部件)能再次用于其它应用(该应用的功能与此软件或软件部件的所完成的功能有关)的程度。  互 连 性 又称相互操作性。连接一个软件和其它系统所需工作量的大小。如果这个软件要联网或与其它系统通信或要把其它系统纳入到自己的控制之下,必须有系统间的接口,使之可以联结。  对以上各个质量特性直接进行度量是很困难的,有些情况下甚至是不可能的。因此,McCall定义了一些评价准则,使用它们对反映质量特性的软件属性分级,以此来估计软件质量特性的值。软件属性一般分级范围从0 (最低)到10 (最高)。各评价准则定义如下。 可跟踪性 在特定的开发和运行环境下跟踪设计表示或实际程序部件到原始需求的(可追溯)能力。  完 备 性 软件需求充分实现的程度。  一 致 性 在整个软件设计与实现的过程中技术与记号的统一程度。  安 全 性 防止软件受到意外的或蓄意的存取、使用、修改、毁坏,或防止泄密的程度。  容 错 性 系统出错(机器临时发生故障或数据输入不合理)时,能以某种预定方式,做出适当处理,得以继续执行和恢复系统的能力。它又称健壮性。  准 确 性 能达到的计算或控制精度。它又称精确性。  简 单 性 在不复杂、可理解的方式下,定义和实现软件功能的程度。  执行效率 为了实现某个功能,提供使用最少处理时间的程度。  存储效率 为了实现某个功能,提供使用最少存储空间的程度。  存取控制 软件对用户存取权限的控制方式达到的程度。  存取审查 软件对用户存取权限的检查程度。  操 作 性 操作软件的难易程度。它通常取决于与软件操作有关的操作规程,以及是否提供有用的输入/输出方法。  易训练性 软件辅助新的用户使用系统的能力。这取决于是否提供帮助用户熟练掌握软件系统的方法。它又称可培训性或培训性。  简 明 性 软件易读的程度。这个特性可以帮助人们方便地阅读本人或他人编制的程序和文档。它又称可理解性。  模块独立性 软件系统内部接口达到的高内聚、低耦合的程度。  自描述性 对软件功能进行自我说明的程度。亦称自含文档性。  结 构 性 软件能达到的结构良好的程度。  文档完备性 软件文档齐全、描述清楚、满足规范或标准的程度。  通 用 性 软件功能覆盖面宽广的程度。  可扩充性 软件的体系结构、数据设计和过程设计的可扩充的程度。  可修改性 软件容易修改,而不致于产生副作用的程度。  自 检 性 软件监测自身操作效果和发现自身错误的能力,又称工具性。  机器独立性 不依赖于某个特定设备及计算机而能工作的程度,又称硬件独立性。  软件独立性 软件不依赖于非标准程序设计语言特征、操作系统特征,或其它环境约束,仅靠自身能实现其功能的程度,又称自包含性。  通信共享性 使用标准的通信协议、接口和带宽的标准化的程度。  数据共享性 使用标准数据结构和数据类型的程度。  通 信 性 提供有效的I/O方式的程度。  正确性和容错性是相互补充的。正确的程序不一定是可容错的程序。反过来,可容错的程序不一定是完全正确的程序。我们要求一个可靠的软件应当在正常的情况下能够正确地工作;而在意外的情况下,也能做出适当的处理,隔离故障,尽快地恢复。这才是一个好的程序。此外,有人在灵活性中加了一个评价准则,叫做“可重配置特性”,它是指软件系统本身各部分的配置能按用户要求实现的容易程度。在简明性中也加了一个评价准则,即“清晰性”,它是指软件的内部结构、内部接口要清晰,人―机界面要清晰。 (3) ISO的软件质量评价模型 ISO/IEC 9126-1991标准规定的软件质量模型由三层组成。在这个标准中,三层次中的第一层为称为质量特性,第二层称为质量子特性,第三层称为度量。如图7.3所示。该标准定义了6个质量特性,即功能性、可靠性、可维护性、效率、可使用性、可移植性;并推荐了21个子特性,如适合性、准确性、互操作性、依从性、安全性、成熟性、容错性、易恢复性、易理解性、易学习性、易操作性、时间特性、资源特性、易分析性、易变更性、稳定性、易测试性、适应性、易安装性、遵循性、易替换性,但不做为标准。用于评价质量子特性的度量没有统一的标准,由各使用单位视实际情况制定。 (4) 软件质量特性之间的竞争 在软件的质量特性与质量特性之间、质量特性与子特性之间存在着有利影响和不利影响,若用“△”表示该质量特性对质量特性有有利影响;用“▽”表示该质量特性对质量特性有不利影响。 则有下面表7.1所示的关系。例如,由于效率的要求,应尽可能采用汇编语言。但是用汇编语言编制出的程序,可靠性、可移植性以及可维护性都很差。 在进行软件质量设计时,必须考虑利弊,全面权衡,根据质量需求,适当合理地选择/设计质量特性,并进行评价。 质量特性 质量子特性 度量 图7.3 ISO软件质量度量模型 表7.1 各质量特性与质量特性之间的关系 功能性 可靠性 可使用性 效 率 可维护性 可移植性  功能性  △   △   可靠性    ▽  △  可使用性    ▽ △ △  效 率  ▽   ▽ ▽  可维护性  △  ▽  △  可移植性  ▽  ▽     (5) 质量活动 产品的高质量将导致成本的下降。质量活动开始于20世纪40年代E. Edwards Deming的开创性工作,由日本人发扬广大,他们开发了一种系统化的方法以从根本上消除造成产品缺陷的原因。1970年代到1980年代,他们的工作移植到西方,称为“全面质量管理(TQC)”。该活动采用的是4个步骤的过程: ① 开展:是一个连续的过程改进体系,其目的是开发一个看得见的可重复的和可度量的过程; ② 当然的质量:只有在前一步完成之后才可启动。这一步将检查影响过程的无形因素,并优化这些因素对过程的影响。例如,软件过程可能受到高层职员流动的影响,而这又是由于公司内部不断重组引起的。公司组织的稳定对于软件质量的提高有很大帮助,因此在这一步可以对公司的重组提出建议。 ③ 感性:意指“第五感觉”。这一步从过程转到用户身上。通过检查用户使用软件产品的情况,改进产品本身,并(潜在地)改进软件产品的生产过程。 ④ 有魅力的质量:通过观察产品在市场上的使用,寻找产品在相关领域发展的方向,从而开发出有预见性的新产品。 2. 软件维护的概念 (1) 软件维护的定义 我们称在软件运行∕维护阶段对软件产品所进行的修改就是所谓的维护。要求进行维护的原因多种多样,归结起来有三种类型: ( 改正在特定的使用条件下暴露出来的一些潜在程序错误或设计缺陷; ( 因在软件使用过程中数据环境发生变化或处理环境发生变化,需要修改软件以适应这种变化。 ( 用户和数据处理人员在使用时常提出改进现有功能,增加新的功能,以及改善总体性能的要求,为满足这些要求,就需要修改软件把这些要求纳入到软件之中。 由这些原因引起的维护活动可以归为以下几类: ① 改正性维护 在软件交付使用后,必然会有一部分隐藏的错误被带到运行阶段来。这些隐藏下来的错误在某些特定的使用环境下就会暴露出来。为了识别和纠正软件错误、改正软件性能上的缺陷、排除实施中的误使用,应当进行的诊断和改正错误的过程,就叫做改正性维护。 ② 适应性维护 随着计算机的飞速发展,外部环境(新的硬、软件配置)或数据环境(数据库、数据格式、数据输入∕输出方式、数据存储介质)可能发生变化,为了使软件适应这种变化,而去修改软件的过程就叫做适应性维护。 ③ 完善性维护 在软件的使用过程中,用户往往会对软件提出新的功能与性能要求。为了满足这些要求,需要修改或再开发软件,以扩充软件功能、增强软件性能、改进加工效率、提高软件的可维护性。这种情况下进行的维护活动叫做完善性维护。 在维护阶段的最初一、二年,改正性维护的工作量较大。随着错误发现率急剧降低,并趋于稳定,就进入了正常使用期。然而,由于改造的要求,适应性维护和完善性维护的工作量逐步增加。实践表明,在几种维护活动中,完善性维护所占的比重最大,来自用户要求扩充、加强软件功能、性能的维护活动约占整个维护工作的50%。  图7.4 三类维护占总维护比例 图7.5 维护在软件生存期所占比例 ④ 预防性维护 除了以上三类维护之外,还有一类维护活动,叫做预防性维护。这是为了提高软件的可维护性、可靠性等,为以后进一步改进软件打下良好基础。通常,预防性维护定义为:“把今天的方法学用于昨天的系统以满足明天的需要”。也就是说,采用先进的软件工程方法对需要维护的软件或软件中的某一部分(重新)进行设计、编制和测试。 在整个软件维护阶段所花费的全部工作量中,预防性维护只占很小的比例,而完善性维护占了几乎一半的工作量。参看图7.4。从图7.5中可以看到,软件维护活动所花费的工作占整个生存期工作量的70%以上。 (2) 影响维护工作量的因素 在软件维护中,影响维护工作量的程序特性有以下6种。 ( 系统大小:系统越大,理解掌握起来越困难。系统越大,所执行功能越复杂。因而需要更多的维护工作量。 ( 程序设计语言:语言的功能越强,生成程序所需的指令数就越少;语言的功能越弱,实现同样功能所需语句就越多,程序就越大。有许多软件是用较老的程序设计语言书写的,程序逻辑复杂而混乱,且没有做到模块化和结构化,直接影响到程序的可读性。 ( 系统年龄:老系统随着不断的修改,结构越来越乱;由于维护人员经常更换,程序又变得越来越难于理解。而且许多老系统在当初并未按照软件工程的要求进行开发,因而没有文档,或文档太少,或在长期的维护过程中文档在许多地方与程序实现变得不一致,这样在维护时就会遇到很大困难。 ( 数据库技术的应用:使用数据库,可以简单而有效地管理和存储用户程序中的数据,还可以减少生成用户报表应用软件的维护工作量。 ( 先进的软件开发技术:在软件开发时,若使用能使软件结构比较稳定的分析与设计技术,及程序设计技术,如面向对象技术、复用技术等,可减少大量的工作量。 ( 其它:例如,应用的类型、数学模型、任务的难度、开关与标记、IF嵌套深度、索引或下标数等,对维护工作量都有影响。 此外,许多软件在开发时并未考虑将来的修改,这就为软件的维护带来许多问题。 (3) 软件维护的策略 根据影响软件维护工作量的各种因素,针对三种典型的维护,James Martin等提出了一些策略,以控制维护成本。 ① 改正性维护 要生成100%可靠的软件成本太高,不一定合算。但通过使用新技术,可大大提高可靠性,减少进行改正性维护的需要。这些技术包括:数据库管理系统、软件开发环境、程序自动生成系统、较高级(第四代)的语言,应用以上4种方法可产生更加可靠的代码。此外, ( 利用应用软件包,可开发出比由用户完全自己开发的系统可靠性更高的软件。 ( 结构化技术,用它开发的软件易于理解和测试。 ( 防错性程序设计。把自检能力引入程序,通过非正常状态的检查,提供审查跟踪。 ( 通过周期性维护审查,在形成维护问题之前就可确定质量缺陷。 ② 适应性维护 这一类的维护不可避免,但可以控制。 ( 在配置管理时,把硬件、操作系统和其它相关环境因素的可能变化考虑在内,可以减少某些适应性维护的工作量。 ( 把与硬件、操作系统,以及其它外围设备有关的程序归到特定的程序模块中。可把因环境变化而必须修改的程序局部于某些程序模块之中。 ( 使用内部程序列表、外部文件,以及处理的例行程序包,可为维护时修改程序提供方便。 ③ 完善性维护 利用前两类维护中列举的方法,也可以减少这一类维护。特别是数据库管理系统、程序生成器、应用软件包,可减少系统或程序员的维护工作量。 此外,建立软件系统的原型,把它在实际系统开发之前提供给用户。用户通过研究原型,进一步完善他们的功能要求,就可以减少以后完善性维护的需要。 (4) 维护成本 有形的软件维护成本是花费了多少钱,而其它非直接的的维护成本有更大的影响。例如,无形的成本可以是: ( 一些看起来是合理的修复或修改请求不能及时安排,使得客户不满意; ( 变更的结果把一些潜在的错误引入正在维护的软件,使得软件整体质量下降; ( 当必须把软件人员抽调到维护工作中去时,就使得软件开发工作受到干扰。 软件维护的代价是在生产率方面的惊人下降。有报告说,生产率将降到原来的40分之一。维护工作量可以分成生产性活动(如分析和评价、设计修改和实现)和“轮转”活动(如力图理解代码在做什么、试图判明数据结构、接口特性、性能界限等)。下面的公式给出了一个维护工作量的模型:      其中,M是维护中消耗的总工作量,p是上面描述的生产性工作量,K是一个经验常数,c是因缺乏好的设计和文档而导致复杂性的度量,d是对软件熟悉程度的度量。 这个模型指明,如果使用了不好的软件开发方法(未按软件工程要求做),原来参加开发的人员或小组不能参加维护,则工作量(及成本)将按指数级增加。 3. 软件维护活动 (1) 维护机构 除了较大的软件开发公司外,通常在软件维护工作方面,不保持正式的维护机构。维护往往是在没有计划的情况下进行的。虽然不要求建立一个正式的维护机构,但是在开发部门,确立一个非正式的维护机构则是非常必要的。例如图7.6就是一个维护机构的组织方案。  图7.6 软件维护的机构 维护申请提交给一个维护管理员,他把申请交给某个系统监督员去评价。一旦做出评价,由修改负责人确定如何进行修改。在维护人员对程序进行修改的过程中,由配置管理员严格把关,控制修改的范围,对软件配置进行审计。 维护管理员、系统监督员、修改负责人等,均代表维护工作的某个职责范围。修改负责人、维护管理员可以是指定的某个人,也可以是一个包括管理人员、高级技术人员在内的小组。系统监督员可以有其他职责,但应具体分管某一个软件包。 在开始维护之前,就把责任明确下来,可以大大减少维护过程中的混乱。 (2) 软件维护工作流程 ( 先确认维护要求。这需要维护人员与用户反复协商,弄清错误概况以及对业务的影响大小,以及用户希望做什么样的修改。然后由维护组织管理员确认维护类型。 ( 对于改正性维护申请,从评价错误的严重性开始。如果存在严重的错误,则必须安排人员,在系统监督员的指导下,进行问题分析,寻找错误发生的原因,进行“救火”性的紧急维护; 对于不严重的错误,可根据任务、机时情况、视轻重缓急,进行排队,统一安排时间。 ( 对于适应性维护和完善性维护申请,需要先确定每项申请的优先次序。若某项申请的优先级非常高,就可立即开始维护工作,否则,维护申请和其它的开发工作一样,进行排队,统一安排时间。 ( 尽管维护申请的类型不同,但都要进行同样的技术工作。这些工作有:修改软件需求说明、修改软件设计、设计评审、对源程序做必要的修改、单元测试、集成测试( 回归测试)、确认测试、软件配置评审等。 ( 在每次软件维护任务完成后,最好进行一次情况评审,确认:在目前情况下,设计、编码、测试中的哪一方面可以改进?哪些维护资源应该有但没有?工作中主要的或次要的障碍是什么?从维护申请的类型来看是否应当有预防性维护? (3) 维护评价 评价维护活动比较困难,因为缺乏可靠的数据。但如果维护记录做得比较好,就可以得出一些维护“性能”方面的度量值。可参考的度量值如: ( 每次程序运行时的平均出错次数; ( 花费在每类维护上的总“人时”数; ( 每个程序、每种语言、每种维护类型的程序平均修改次数; ( 因为维护,增加或删除每个源程序语句所花费的平均“人时”数; ( 用于每种语言的平均“人时”数; ( 维护申请报告的平均处理时间; ( 各类维护申请的百分比。 这七种度量值提供了定量的数据,据此可对开发技术、语言选择、维护工作计划、资源分配、以及其它许多方面做出判定。因此,这些数据可以用来评价维护工作。 4. 程序修改的步骤及修改的副作用 (1) 分析和理解程序 经过分析,全面、准确、迅速地理解程序是决定维护成败和质量好坏的关键。在这方面, 软件的可理解性和文档的质量非常重要。必须: ( 理解程序的功能和目标; ( 掌握程序的结构信息,即从程序中细分出若干结构成分。如程序系统结构、 控制结构、数据结构和输入/输出结构等; ( 了解数据流信息,即所涉及到的数据来源何处,在哪里被使用; ( 了解控制流信息,即执行每条路径的结果; ( 理解程序的操作(使用)要求; 为了容易地理解程序,要求自顶向下地理解现有源程序的程序结构和数据结构,为此可采用如下方法: ① 分析程序结构图: ( 分析各个过程的源代码,建立一个直接调用矩阵D或调用树。 ( 建立过程的间接调用矩阵。 ( 分析各个过程的接口,估计更改的复杂性。 ② 数据跟踪 ( 建立各层次的程序级上的接口图,展示各模块或过程的调用方式和接口参数; ( 利用数据流分析方法,对过程内部的一些变量进行跟踪;维护人员通过这种数据流跟踪,可获得有关数据在过程间如何传递,在过程内如何处理等信息。 ③ 控制跟踪 控制流跟踪同样可在结构图基础上或源程序基础上进行。可采用符号执行或实际动态跟踪的方法,了解数据如何从一个输入源到达输出点的。 ④ 在分析的过程中,充分阅读和使用源程序清单和文档,分析现有文档的合理性。 ⑤ 充分使用由编译程序或汇编程序提供的交叉引用表、符号表、以及其它有用的信息。 ⑥ 如有可能,积极参加开发工作。 (2) 修改程序 对程序的修改,必须事先做出计划,有预谋地、周密有效地实施修改。 ① 设计程序的修改计划 程序的修改计划要考虑人员和资源的安排。修改计划的内容主要包括: ( 规格说明信息:数据修改、处理修改、作业控制语言修改、系统之间接口的修改等; ( 维护资源:新程序版本、测试数据、所需的软件系统、计算机时间等; ( 人员:程序员、用户相关人员、技术支持人员、厂家联系人、数据录入员等; ( 提供:纸面、计算机媒体等。 针对以上每一项,要说明必要性、从何处着手、是否接受、日期等。通常,可采用自顶向下的方法,在理解程序的基础上, ⅰ) 研究程序的各个模块、模块的接口、及数据库,从全局的观点,提出修改计划。 ⅱ) 依次地把要修改的、以及那些受修改影响的模块和数据结构分离出来。为此,要 ( 识别受修改影响的数据; ( 识别使用这些数据的程序模块; ( 对于上面程序模块,按是产生数据、修改数据、还是删除数据进行分类; ( 识别对这些数据元素的外部控制信息; ( 识别编辑和检查这些数据元素的地方; ( 隔离要修改的部分; ⅲ) 详细地分析要修改的、以及那些受变更影响的模块和数据结构的内部细节,设计修改计划,标明新逻辑及要改动的现有逻辑。 ⅳ) 向用户提供回避措施。用户的某些业务因软件中发生问题而中断,为不让系统长时间停止运行,需把问题局部化,在可能的范围内继续开展业务。可以采取的措施有: ( 在问题的原因还未找到时,先就问题的现象,提供回避的操作方法。 ( 如果弄清了问题的原因,可通过临时修改或改变运行控制以回避在系统运行时产生的问题。 ② 修改代码,以适应变化 ( 正确、有效地编写修改代码; ( 要谨慎地修改程序,尽量保持程序的风格及格式,要在程序清单上注明改动的指令; ( 不要删除程序语句,除非完全肯定它是无用的; ( 不要试图共用程序中已有的临时变量或工作区,为了避免冲突或混淆用途,应自行设置自己的变量; ( 插入错误检测语句; ( 在修改过程中做好修改的详细记录,消除变更中任何有害的副作用(波动效应); ③ 修改程序的副作用 所谓副作用是指因修改软件而造成的错误或其它不希望发生的情况,有三种副作用: ( 修改代码的副作用。在使用程序设计语言修改源代码时,都可能引入错误。例如,删除或修改一个子程序、删除或修改一个标号、 删除或修改一个标识符、改变程序代码的时序关系、改变占用存储的大小、改变逻辑运算符、修改文件的打开或关闭、改进程序的执行效率,以及把设计上的改变翻译成代码的改变、为边界条件的逻辑测试做出改变时,都容易引入错误。 ( 修改数据的副作用。在修改数据结构时,有可能造成软件设计与数据结构不匹配,因而导致软件出错。数据副作用就是修改软件信息结构导致的结果。例如,在重新定义局部的或全局的常量、 重新定义记录或文件的格式、增大或减小一个数组或高层数据结构的大小、修改全局或公共数据、重新初始化控制标志或指针、重新排列输入/输出或子程序的参数时,容易导致设计与数据不相容的错误。数据副作用可以通过详细的设计文档加以控制。在此文档中描述了一种交叉引用,把数据元素、记录、文件和其它结构联系起来。 ( 文档的副作用。对数据流、软件结构、 模块逻辑或任何其它有关特性进行修改时,必须对相关技术文档进行相应修改。否则会导致文档与程序功能不匹配,缺省条件改变,新错误信息不正确等错误。使得软件文档不能反映软件的当前状态。对于用户来说,软件事实上就是文档。如果对可执行软件的修改不反映在文档里,就会产生文档的副作用。例如,对交互输入的顺序或格式进行修改,如果没有正确地记入文档中,就可能引起重大的问题。过时的文档内容、索引和文本可能造成冲突,引起用户的失败和不满。因此,必须在软件交付之前对整个软件配置进行评审,以减少文档的副作用。 为了控制因修改而引起的副作用,要做到: ⅰ) 按模块把修改分组; ⅱ) 自顶向下地安排被修改模块的顺序; ⅲ) 每次修改一个模块; ⅳ) 对于每个修改了的模块,在安排修改下一个模块之前,要确定这个修改的副作用。可以使用交叉引用表、存储映象表、执行流程跟踪等。 (3) 重新验证程序 在将修改后的程序提交用户之前,需要用以下的方法进行充分的确认和测试,以保证整个修改后的程序的正确性。 ① 静态确认 修改软件,伴随着引起新的错误的危险。为了能够做出正确的判断,验证修改后的程序至少需要两个人参加。要检查 ( 修改是否涉及到规格说明? 修改结果是否符合规格说明? 有没有歪曲规格说明? ( 程序的修改是否足以修正软件中的问题? 源程序代码有无逻辑错误? 修改时有无修补失误? ( 修改部分对其它部分有无不良影响(副作用)? 对软件进行修改,常常会引发别的问题,因此有必要检查修改的影响范围。 ② 计算机确认 在充分进行了以上确认的基础上,要用计算机对修改程序进行确认测试: ( 确认测试顺序:先对修改部分进行测试,然后隔离修改部分,测试程序的未修改部分,最后再把它们集成起来进行测试。这种测试称为回归测试。 ( 准备标准的测试用例。 ( 充分利用软件工具帮助重新验证过程。 ( 在重新确认过程中,需邀请用户参加。 ③ 维护后的验收──在交付新软件之前,维护主管部门要检验: ( 全部文档是否完备,并已更新; ( 所有测试用例和测试结果已经正确记载; ( 记录软件配置所有副本的工作已经完成; ( 维护工序和责任已经确定。 从维护角度来看所需测试种类如: ( 对修改事务的测试; ( 对修改程序的测试;  ( 操作过程的测试; ( 应用系统运用过程的测试;  ( 使用过程的测试; ( 系统各部分之间接口的测试;  ( 作业控制语言的测试; ( 与系统软件接口的测试;  ( 软件系统之间接口的测试; ( 安全性测试;  ( 后备/恢复过程的测试。   5. 软件可维护性 (1) 软件可维护性的定义 所谓软件可维护性,是指纠正软件系统出现的错误和缺陷,以及为满足新的要求进行修改、扩充或压缩的容易程度。可维护性、可使用性、可靠性是衡量软件质量的几个主要质量特性,也是用户十分关心的几个方面。可惜的是影响软件质量的这些重要因素,目前尚没有对它们定量度量的普遍适用的方法。但是就它们的概念和内涵来说则是很明确的。 软件的可维护性是软件开发阶段各个时期的关键目标。 目前广泛使用的是用如下的七个特性来衡量程序的可维护性。而且对于不同类型的维护,这七种特性的侧重点也不相同。表7.2显示了在各类维护中应侧重哪些特性。图中的“”表示需要的特性。 表7.2 在各类维护中的侧重点  改正性维护  适应性维护  完善性维护   可理解性  (     可测试性  (     可修改性  (  (    可 靠 性  (     可移植性   (    可使用性   ( (   效 率   (   上面所列举的这些质量特性通常体现在软件产品的许多方面,为使每一个质量特性都达到预定的要求,需要在软件开发的各个阶段采取相应的措施加以保证。因此,软件的可维护性是产品投入运行以前各阶段面向上述各质量特性要求进行开发的最终结果。 (2) 可维护性的度量 人们一直期望对软件的可维护性做出定量度量,但要做到这一点并不容易。许多研究工作集中在这个方面,形成了一个引人注目的学科──软件度量学。下面我们介绍度量一个可维护的程序的七种特性时常用的方法。这就是质量检查表、质量测试、质量标准。 质量检查表是用于测试程序中某些质量特性是否存在的一个问题清单。评价者针对检查表上的每一个问题,依据自己的定性判断,回答“Yes”或者“No”。质量测试与质量标准则用于定量分析和评价程序的质量。由于许多质量特性是相互抵触的,要考虑几种不同的度量标准,相应地去度量不同的质量特性。 ① 可理解性 可理解性表明人们通过阅读源代码和相关文档,了解程序功能及其如何运行的容易程度。一个可理解的程序主要应具备以下一些特性:模块化(模块结构良好、功能完整、简明),风格一致性(代码风格及设计风格的一致性),不使用令人捉摸不定或含糊不清的代码,使用有意义的数据名和过程名,结构化,完整性(对输入数据进行完整性检查)等。 用于可理解性度量的检查表的内容有:程序是否模块化? 结构是否良好?每个模块是否有注释块,说明程序的功能、主要变量的用途及取值、所有调用它的模块、以及它调用的所有模块?在模块中是否有其它有用的注释内容,包括输入输出、精确度检查、限制范围和约束条件、假设、错误信息、程序履历等?在整个程序中缩进和间隔的使用风格是否一致?在程序中每一个变量,过程是否具有单一的有意义的名字?程序是否体现了设计思想?程序是否限制使用一般系统中没有的内部函数过程与子程序?是否能通过建立公共模块或子程序来避免多余的代码?所有变量是否是必不可少的?是否避免了把程序分解成过多的模块、函数或子程序?程序是否避免了很难理解的、非标准的语言特性? 对于可理解性,可以使用一种叫做“90-10测试”的方法来衡量。即把一份被测试的源程序清单拿给一位有经验的程序员阅读10分钟,然后把这个源程序清单拿开,让这位程序员凭自己的理解和记忆,写出该程序的90%。如果程序员真的写出来了,则认为这个程序具有可理解性,否则这个要重新编写。 ② 可靠性 可靠性表明一个程序按照用户的要求和设计目标,在给定的一段时间内正确执行的概率。关于可靠性,度量的标准主要有:平均失效间隔时间MTTF(Mean Time To Failure)、平均修复时间MTTR(Mean Time To Repair error)、有效性A( = MTBD∕(MTBD+MDT))。 度量可靠性的方法,主要有两类: ( 根据程序错误统计数字,进行可靠性预测。常用方法是利用一些可靠性模型,根据程序测试时发现并排除的错误数预测平均失效间隔时间MTTF。 ( 根据程序复杂性,预测软件可靠性。用程序复杂性预测可靠性,前提条件是可靠性与复杂性有关。因此可用复杂性预测出错率。程序复杂性度量标准可用于预测哪些模块最可能发生错误,以及可能出现的错误类型。了解了错误类型及它们在哪里可能出现,就能更快地查出和纠正更多的错误,提高可靠性。 用于可靠性度量的检查表的内容有:程序中对可能出现的没有定义的数学运算是否做了检查?如除以“0”。循环终止和多重转换变址参数的范围,是否在使用前做了测试?下标的范围是否在使用前测试过?是否包括错误恢复和再启动过程?所有数值方法是否足够准确?输入的数据是否检查过?测试结果是否令人满意?大多数执行路径在测试过程中是否都已执行过?对最复杂的模块和最复杂的模块接口,在测试过程中是否集中做过测试?测试是否包括正常的、特殊的和非正常的测试用例?程序测试中除了假设数据外,是否还用了实际数据?为了执行一些常用功能,程序是否使用了程序库? ③ 可测试性 可测试性表明论证程序正确性的容易程度。程序越简单,证明其正确性就越容易。而且设计合用的测试用例,取决于对程序的全面理解。因此,一个可测试的程序应当是可理解的,可靠的,简单的。 用于可测试性度量的检查表的内容有:程序是否模块化?结构是否良好?程序是否可理解?程序是否可靠?程序是否能显示任意的中间结果?程序是否能以清楚的方式描述它的输出?程序是否能及时地按照要求显示所有的输入?程序是否有跟踪及显示逻辑控制流程的能力?程序是否能从检查点再启动?程序是否能显示带说明的错误信息? 对于程序模块,可用程序复杂性来度量可测试性。程序的环路复杂性越大,程序的路径就越多。因此,全面测试程序的难度就越大。 ④ 可修改性 可修改性表明程序容易修改的程度。一个可修改的程序应当是可理解的、通用的、灵活的、简单的。其中,通用性是指程序适用于各种功能变化而无需修改。灵活性是指能够容易地对程序进行修改。 测试可修改性的一种定量方法是修改练习。其基本思想是通过做几个简单的修改,来评价修改的难度。设C是程序中各个模块的平均复杂性,n是必须修改的模块数,A 是要修改的模块的平均复杂性。 则修改的难度D由下式计算: D = A∕C 对于简单的修改,若D>1,说明该程序修改困难。A和C 可用任何一种度量程序复杂性的方法计算。 用于可修改性度量的检查表的内容有:程序是否模块化?结构是否良好?程序是否可理解?在表达式、数组/表的上下界、输入/输出设备命名符中是否使用了预定义的文字常数?是否具有可用于支持程序扩充的附加存储空间?是否使用了提供常用功能的标准库函数?程序是否把可能变化的特定功能部分都分离到单独的模块中?程序是否提供了不受个别功能发生预期变化影响的模块接口?是否确定了一个能够当做应急措施的一部分,或者能在小一些的计算机上运行的系统子集?是否允许一个模块只执行一个功能?每一个变量在程序中是否用途单一?能否在不同的硬件配置上运行?能否以不同的输入/输出方式操作?能否根据资源的可利用情形,以不同的数据结构或不同的算法执行? ⑤ 可移植性 可移植性表明程序转移到一个新的计算环境的可能性的大小。或者它表明程序可以容易地、有效地在各种各样的计算环境中运行的容易程度。 一个可移植的程序应具有结构良好、灵活、不依赖于某一具体计算机或操作系统的性能。 用于可移植性度量的检查表的内容有:是否是用高级的独立于机器的语言来编写程序? 是否是用广泛使用的标准化的程序设计语言来编写程序,且是否仅使用了这种语言的标准版本和特性?程序中是否使用了标准的普遍使用的库功能和子程序?程序中是否极少使用或根本不使用操作系统的功能?程序中数值计算的精度是否与机器的字长或存储器大小的限制无关?程序在执行之前是否初始化内存?程序在执行之前是否测定当前的输入/输出设备? 程序是否把与机器相关的语句分离了出来,集中放在了一些单独的程序模块中,并有说明文档?程序是否结构化? 并允许在小一些的计算机上分段(覆盖)运行?程序中是否避免了依赖于字母数字或特殊字符的内部位表示,并有说明文件? ⑥ 效率 效率表明一个程序能执行预定功能而又不浪费机器资源的程度。这些机器资源包括内存容量、外存容量、通道容量和执行时间。 用于效率度量的检查表的内容有:程序是否模块化?结构是否良好?程序是否具有高度的区域性(与操作系统的段页处理有关)?是否消除了无用的标号与表达式,以充分发挥编译器优化作用?程序的编译器是否有优化功能?是否把特殊子程序和错误处理子程序都归入了单独的模块中?在编译时是否尽可能多地完成了初始化工作?是否把所有在一个循环内不变的代码都放在了循环外处理?是否以快速的数学运算代替了较慢的数学运算?是否尽可能地使用了整数运算,而不是实数运算?是否在表达式中避免了混合数据类型的使用,消除了不必要的类型转换?程序是否避免了非标准的函数或子程序的调用?在几条分支结构中,是否最有可能为“真“的分支首先得到测试?在复杂的逻辑条件中,是否最有可能为“真“的表达式首先得到测试? ⑦ 可使用性 从用户观点出发,把可使用性定义为程序方便、实用、及易于使用的程度。一个可使用的程序应是易于使用的、能允许用户出错和改变,并尽可能不使用户陷入混乱状态的程序。 用于可使用性度量的检查表的内容有: ⅰ) 程序是否具有自描述性?例如,是否有适应不同读者,并附有实例的程序使用说明?是否有交互形式的Help功能?是否一有请求,就能对每一个操作方式作出解释?用户能否很快熟悉程序的使用而无需他人的帮助?是否一有请求,就能很容易地获得当前程序状态信息? ⅱ) 程序是否能始终如一地按照用户的要求运行?例如,程序是否有句法上统一的命令语言和错误信息格式?通过尽量缩小响应时间的差异,程序在相似的条件下,其表现是否也相似? ⅲ) 程序是否让用户对数据处理有一个满意的和适当的控制?例如,程序在交互方式运行时,能否控制中止一项任务,开始或恢复另一项任务?在没有副作用的情形下,程序是否允许处理作废?程序是否允许用户查看后台处理?程序是否有一种易懂的命令语言并允许通过命令组合建立宏指令?程序能否在一旦用户有要求时提供提示信息,帮助用户使用系统?程序能否提供可理解的、非危险性的错误信息? ⅳ) 程序是否容易学会使用?例如,程序是否不需要专门的数据处理知识就能使用?对输入格式、要求和限制的解释是否完整和清楚?在交互系统中,用户输入是否在菜单指示支持下进行?程序是否提供带有纠错提示的错误信息?对交互式系统,是否有“联机“手册? 对批处理系统,手册是否容易得到?手册是否是用用户术语写的? ⅴ) 程序是否使用数据管理系统来自动地处理事务性工作和管理格式化、地址分配及存储器组织。 ⅵ) 程序是否具有容错性?例如,程序是否容忍典型的输入打字错误?当输入动作需要重复时,程序能否接受简化输入?命令能否简写?程序能否验证输入的数据? ⅶ) 程序是否灵活?例如,程序是否允许以自由形式输入?程序是否可以重复使用而无须对输入值做过多的说明?对用户而言,是否有各种不同的输出选择?程序是否可以针对所选择的运行方式,删除不必要的输入、计算和输出?程序是否允许用户扩充命令语言?程序是否可移植?程序是否允许用户定义自己的功能集和特性集?程序能否以子集形式出现?程序是否允许有经验的用户使用运行较快的版本、简写命令、缺省值等, 而让没有经验的用户使用运行较慢的版本,并提供求助命令及监控能力等。 ⑧ 其它间接定量度量可维护性的方法 Gilb提出了与软件维护期间工作量有关的一些数据,可以使用它们间接地对软件的可维护性做出估计。 ( 问题识别的时间; ( 收集维护工具的时间;  ( 分析、诊断问题的时间; ( 具体的改错或修改的时间;  ( 局部测试的时间; ( 集成或回归测试的时间;  ( 修改规格说明的时间; ( 维护的评审时间;  ( 因管理活动拖延的时间; ( 恢复时间。  这些数据反映了维护全过程中检错-纠错-验证的周期,即从检测出软件存在的问题开始至修正它们并经回归测试验证这段时间。可以粗略地认为,这个周期越短,维护越容易。 6. 提高可维护性的方法 (1) 建立明确的软件质量目标和优先级 一个可维护的程序应是可理解的、可靠的、可测试的、可修改的、可移植的、效率高的、可使用的。但要实现这所有的目标,需要付出很大的代价,而且也不一定行得通。因为某些质量特性是相互促进的,例如可理解性和可测试性、可理解性和可修改性。但另一些质量特性却是相互抵触的,例如效率和可移植性、效率和可修改性等。因此,尽管可维护性要求每一种质量特性都要得到满足,但它们的相对重要性应随程序的用途及计算环境的不同而不同。所以,应当对程序的质量特性,在提出目标的同时还必须规定它们的优先级。这样有助于提高软件的质量,并对软件生存期的费用产生很大的影响。 (2) 使用提高软件质量的技术和工具 ① 模块化 模块化是软件开发过程中提高软件质量,降低成本的有效方法之一。也是提高可维护性的有效的技术。它的优点是如果需要改变某个模块的功能,则只要改变这个模块,对其它模块影响很小;如果需要增加程序的某些功能,则仅需增加完成这些功能的新的模块或模块层;程序的测试与重复测试比较容易;程序错误易于定位和纠正;容易提高程序效率。 ② 结构化程序设计 结构化程序设计不仅使得模块结构标准化,而且将模块间的相互作用也标准化了。因而把模块化又向前推进了一步。采用结构化程序设计可以获得良好的程序结构。 ③ 使用结构化程序设计技术,提高现有系统的可维护性 ( 采用备用件的方法──当要修改某一个模块时,用一个新的结构良好的模块替换掉整个模块。这种方法要求了解所替换模块的外部(接口)特性,可以不了解其内部工作情况。它有利于减少新的错误,并提供了一个用结构化模块逐步替换掉非结构化模块的机会。 ( 采用自动重建结构和重新格式化的工具(结构更新技术)──这种方法采用如代码评价程序、重定格式程序、结构化工具等自动软件工具,把非结构化代码转换成良好结构代码。 ( 改进现有程序的不完善的文档──改进和补充文档的目的是为了提高程序的可理解性,以提高可维护性。 ( 使用结构化程序设计方法实现新的子系统。 ( 采用结构化小组程序设计的思想和结构文档工具──软件开发过程中,建立主程序员小组,实现严格的组织化结构,强调规范,明确领导以及职能分工,能够改善通信、提高程序生产率;在检查程序质量时,采取有组织分工的结构普查,分工合作,各司其职,能够有效地实施质量检查。同样,在软件维护过程中,维护小组也可以采取与主程序员小组和结构普查类似的方式,以保证程序的质量。 (3) 进行明确的质量保证审查 质量保证审查对于获得和维持软件的质量,是一个很有用的技术。除了保证软件得到适当的质量外,审查还可以用来检测在开发和维护阶段内发生的质量变化。一旦检测出问题来,就可以采取措施来纠正,以控制不断增长的软件维护成本,延长软件系统的有效生命期。 为了保证软件的可维护性,有四种类型的软件审查。 ① 在检查点进行复审 保证软件质量的最佳方法是在软件开发的最初阶段就把质量要求考虑进去,并在开发过程每一阶段的终点,设置检查点进行检查。检查的目的是要证实已开发的软件是否符合标准,是否满足规定的质量需求。在不同的检查点,检查的重点不完全相同。如图7.7所示。  图7.7 软件开发期间各个检查点的检查重点 ② 验收检查 验收检查是一个特殊的检查点的检查,是交付使用前的最后一次检查,是软件投入运行之前保证可维护性的最后机会。它实际上是验收测试的一部分,只不过它是从维护的角度提出验收的条件和标准。 ③ 周期性地维护审查 软件在运行期间,为了纠正新发现的错误或缺陷,为了适应计算环境的变化,为了响应用户新的需求,必须进行修改。因此会导致软件质量有变坏的危险,可能产生新的错误,破坏程序概念的完整性。因此,必须像硬件的定期检查一样,每月一次,或二月一次,对软件做周期性的维护审查,以跟踪软件质量的变化。周期性维护审查实际上是开发阶段检查点复查的继续,并且采用的检查方法、检查内容都是相同的。为了便于用户进行运行管理,适时提供维护工具以及有关信息是很重要的。 维护审查的结果可以同以前的维护审查的结果,以及以前的验收检查的结果和检查点检查的结果相比较,任何一种改变都表明在软件质量上或其它类型的问题上可能起了变化。 对于改变的原因应当进行分析。例如,如果使用的是复杂性度量标准,则应当随机地选择少量模块,再次测量其复杂性。如果新的复杂性值大于以前的值,则可能: ( 是软件可维护性退化的症兆; ( 预示将来维护该系统需要更多的维护工作量; ( 表明修改太仓促,没有考虑到要保持系统的完整性; ( 是软件的文档化工具以及维护人员的专业知识不足所造成的。 反之,若复杂性值减小,则表明软件质量是稳定的。 ④ 对软件包进行检查 软件包是一种标准化了的,可为不同单位、不同用户使用的软件。软件包卖主考虑到他的专利权,一般不会提供给用户他的源代码和程序文档。因此,对软件包的维护采取以下方法。使用单位的维护人员首先要仔细分析、研究卖主提供的用户手册、操作手册、培训教程、新版本说明、计算机环境要求书、未来特性表,以及卖方提供的验收测试报告等,在此基础上,深入了解本单位的希望和要求,编制软件包的检验程序。该检验程序检查软件包程序所执行的功能是否与用户的要求和条件相一致。为了建立这个程序,维护人员可以利用卖方提供的验收测试实例,还可以自己重新设计新的测试实例。根据测试结果,检查和验证软件包的参数或控制结构,以完成软件包的维护。 (4) 选择可维护的程序设计语言 程序设计语言的选择,对程序的可维护性影响很大。 机器语言 汇编语言  高级语言: 查询语言,报表生成语言, (FORTRAN、 图象语言,应用生成语言 COBOL等) 图7.8 程序设计语言对可维护性的影响 低级语言,即机器语言和汇编语言,很难理解,很难掌握,因此很难维护。高级语言比低级语言容易理解,具有更好的可维护性。但同是高级语言,可理解的难易程度也不一样。 第四代语言,例如查询语言、图形语言、报表生成器、非常高级的语言等,有的是过程化的语言,有的是非过程化的语言。不论是哪种语言,编制出的程序都容易理解和修改,而且,其产生的指令条数可能要比用COBOL语言或用PL/1语言编制出的少一个数量级,开发速度快许多倍。有些非过程化的第四代语言,用户不需要指出实现的算法,仅需向编译程序或解释程序提出自己的要求,由编译程序或解释程序自己做出实现用户要求的智能假设,例如自动选择报表格式,选择字符类型和图形显示方式等。总之,从维护角度来看,第四代语言比其它语言更容易维护。 (5) 改进程序的文档 程序文档是对程序总目标、程序各组成部分之间的关系、程序设计策略、程序实现过程的历史数据等的说明和补充。程序文档对提高程序的可理解性有着重要作用。即使是一个十分简单的程序,要想有效地、高效率地维护它,也需要编制文档来解释其目的及任务。而对于程序维护人员来说,要想对程序编制人员的意图重新改造,并对今后变化的可能性进行估计,缺了文档也是不行的。因此,为了维护程序,人们必须阅读和理解文档。 好的文档是建立可维护性的基本条件。它的作用和意义有三点: ( 文档好的程序比没有文档的程序容易操作。因为它增加了程序的可读性和可使用性。但不正确的文档比根本没有文档要坏得多。 ( 好的文档意味着简洁、风格一致、且易于更新。 ( 程序应当成为其自身的文档。就是说,在程序中应插入注释,以提高程序的可理解,并以移行、空行等明显的视觉组织来突出程序的控制结构。如果程序越长、越复杂,则它对文档的需要就越迫切。 另外,在软件维护阶段,利用历史文档,可以大大简化维护工作。历史文档有三种: ① 系统开发日志 ② 错误记载 ③ 系统维护日志 7. 软件再工程 术语“逆向工程”来自硬件。软件公司对竞争对手的硬件产品进行分解,了解竞争对手在设计和制造上的“隐秘”。成功的逆向工程应当通过考察产品的实际样品,导出该产品的一个或多个设计与制造的规格说明。 (1) 逆向工程与再工程的概念 软件的逆向工程是完全类似的。但是,要做逆向工程的程序常常不是竞争对手的,因为要受到法律约束。公司做逆向工程的程序,一般是自己的程序,有些是在多年以前开发出来的。这些程序没有规格说明,对它们的了解很模糊。因此,软件的逆向工程是分析程序,力图在比源代码更高抽象层次上建立程序表示的过程。逆向工程是设计恢复的过程。逆向工程工具可以从已存在程序中抽取数据结构、体系结构和程序设计信息。 再工程,也叫做复壮(修理)或再生。它不仅能从已存在的程序中重新获得设计信息,而且还能使用这些信息来改建或重构现有的系统,以改进它的综合质量。一般软件人员利用再工程重新实现已存在的程序,同时加进新的功能或改善它的性能。 为了执行预防性维护,软件开发组织必须选择在最近的将来可能变更的程序,做好变更它们的准备。逆向工程和再工程可用于执行这种维护任务。 (2) 逆向工程 逆向工程就好象是一个魔术管道。我们把一个非结构化的无文档的源代码或目标代码清单喂入管道,则从管道的另一端出来计算机软件的全部文档。逆向工程可以从源代码或目标代码中提取设计信息,其中抽象的层次、文档的完全性、工具与人的交互程度,以及过程的方法都是重要的因素。如图7.9所示。 逆向工程的抽象层次和用来产生它的工具提交的设计信息是原来设计的赝品,它是从源代码或目标代码中提取出来的。理想情况是抽象层次尽可能地高,也就是说,逆向工程过程应当能够导出过程性设计的表示(最低层抽象)、程序和数据结构信息(低层抽象)、数据和控制流模型(中层抽象)和实体联系模型(高层抽象)。随着抽象层次的增加,可以给软件工程师提供更多的信息,使得理解程序更容易。 逆向工程的文档完全性给出了一个抽象层次所能提供细节的详细程度。在多数情况下,文档完全性随着抽象层次的增加而减少。例如,给出一个源代码清单,可利用它得到比较完全的过程性设计表示;可能还能得到简单的数据流表示;但要得到完全的数据流图则比较困难。 如果逆向工程过程的方向只有一条路,则从源代码或目标代码中提取的所有信息都将提供给软件工程师。他们可以用来进行维护活动。如果方向有两条路,则信息将反馈给再工程工具,以便重新构造或重新生成老的程序。 (3) 软件再工程 再工程组合了逆向工程的分析和设计抽象的特点,具有对程序数据、体系结构和逻辑的重构能力。执行重构可生成一个设计,它产生与原来程序相同的功能,但具有比原来程序更高的质量。 ① 为什么要实施软件再工程 ( 再工程可帮助软件机构降低软件演化的风险。当改进原有软件时必须频繁地对软件实施变更,因而降低了软件的可靠性。而软件再工程可以降低变更带来的风险。 ( 再工程可帮助软件机构补偿软件的投资。许多软件机构每年要花费大量的资金用于开发软件。如果采用再工程,而不是扔掉原来的软件,可以部分补偿他们在软件上的投资。 ( 再工程可使得软件易于进一步变更。再工程可使得程序员更容易理解程序,更容易对其开展工作,从而提高维护工作的生产效率。 ( 再工程有着广阔的市场。 ( 再工程是推动自动软件维护发展的动力。 ② 软件再工程技术 ⅰ)改进软件 ( 软件重构:软件重构是对软件进行修改,使其易于理解或易于维护。所谓重构,意味着变更源代码的控制结构,它是实现再工程全面自动化的第一步。软件重构的示意图参看图7.10。 ( 文档重写、加注释及文档更新:软件文档重写是要生成更新的校正了的软件信息。重写代码是要将程序代码、其它文档及程序员知识转换成更新了的代码文档。这种文档一般是文本形式的,但可以有图形表示。(包括嵌入的注释、设计和程序规格说明。)用更新文档来实现软件改进是一种早期的软件再工程方法,程序员可以通过嵌入的注释了解程序的功能。文档重写的示意图参见图7.11。 ( 复用工程:复用工程的目的是将软件修改成可复用的。通常的做法是:首先寻找软件部件,然后将其改造并放入复用库中。开发新的应用时,可从复用库中选取可复用的构件,实现复用。利用再工程实现复用的过程如图7.12所示。 ( 重分模块:重分模块时要变更系统的模块结构,这项工作有赖于对系统构件特性分析和模块耦合性的度量值。 ( 数据再工程:数据再工程是为了改善系统的数据组织,使得数据模式可以辨认和更新。它往往是其它任务(如将数据迁移到其它数据库管理系统)的前期工作。 ( 业务过程再工程:现在的趋势是,使软件去适应业务而不是让业务去适应软件。经验表明,生产率的显著提高有时可能来自在软件帮助下对业务过程所做的自动地重新思考。这种思考可能会导致新的软件设计,新的设计可以成为软件系统再工程、演化的基础。 ( 可维护性分析、业务量分析和经济分析:可维护性分析对于寻找出系统的哪些部分需要再工程十分有用。一般来讲,大多数维护工作往往集中在系统的少数模块。这些部分对于维护成本有着最为强烈的初始冲击。 ⅱ)理解软件 ( 浏览:利用文本编辑器来浏览软件是最早的理解软件的手段。近年,来浏览方法已大有改进,利用超文本,可以在鼠标的帮助下,提供多种视图。如图7.13所示。另一种重要的浏览手段是交叉索引。 图7.13 软件视图示例 ( 分析与度量:这也是理解程序特性(如复杂性)的重要方法。软件度量问题已受到软件界的广泛关注。与再工程相关的技术是程序分片、控制流复杂性度量及耦合性度量等。 ( 逆向工程与设计恢复:这两者有相同的含义,都是从另外的途径取得软件信息。这一方法已被人们普遍采用,但用其确定某些设计信息(如设计说明)仍有风险。目前广泛应用的逆向工程是从源程序产生软件设计的结构图或数据流图。 ⅲ)获取、保护和扩充软件的已有知识 ( 程序分解:利用程序分解从程序中找出对象和关系,并将它们存入信息库。而对象和关系一般用于分析、度量以及进一步对信息实施分析和提取。不是直接对源程序实现分解可以节省利用工具进行程序语法分析和生成对象和关系的工作量。 ( 对象恢复:它可以从源程序中取得对象,这可以帮助我们用面向对象的方法来观察以前的一些非面向对象的源程序。面向对象(类、继承、方法、抽象数据类型等)可能是部分的,也可能是全部的。 ( 程序理解:有几种形式。一种是程序员用手工的或自动的方式获得对软件的较好理解,另一种是将有关编程的信息保存起来,再利用这些信息找到编程知识的实例。理解是否正确,需要由软件与编程知识库中信息相匹配的程度决定。 ( 知识库和程序变换:知识库和程序变换是许多再工程技术的基础。变换在程序图上和存于知识库的对象图上进行。为开发新的再工程工具,基于对象的、针对再工程工具的变换结构正在受到广泛关注。 ③ 软件再工程的风险 软件再工程是一种软件工程活动,它与任何软件工程项目一样,可能遇到各种风险。软件管理人员必须在工程活动之前有所准备,采取适当对策,防止风险带来的损失。可能出现的风险有以下6种。 ( 过程风险:如过高的再工程人工成本;在规定的时间内未达到成本―效益要求;未从经济上规划再工程的投入;对再工程项目的人力投入放任自流;对再工程方案缺少管理的承诺。 ( 人员风险:软件人员对再工程项目意见不一致,影响工作进展;程序员工作低效。 ( 应用风险:再工程项目缺少本应用论域专家的支持;对源程序中体现的业务知识不熟悉;再工程项目的工作完成得不够充分。 ( 技术风险:恢复的信息是无用的或未被充分利用;大批昂贵的文档被开发出来;逆向工程得到的成果不可共享;采用的再工程方法对再工程目标不适合;缺乏再工程的技术支持。 ( 工具风险:依靠了并非如同广告宣传那样的工具;未经过安装的工具。 ( 策略风险:对整个再工程方案的承诺不成熟;对暂定的目标无长期的打算;对程序、数据和工程过程缺乏全面的观点;无计划地使用再工程工具。 三、例题分析 【例1】软件产品质量是生产者和用户都十分关心的问题,早期的质量管理只看到产品的质量,近年来质量管理向( A )发展,一个重要的基本假设是( B )直接影响( C )。这一假设最初是根据制造业情况作出的。( D )自然就会得到高质量的产品。( D )的思想是美国工程师戴明(W. E. Deming)提出来的,戴明等人在质量管理中引入统计质量控制的概念,以降低软件产品缺陷数作为( D )的目标,请选择合适的答案完成下面有关软件质量与过程的关系的示意图。 供选择的答案: A ( D. ① 过程的质量 ② 产品的质量 ③ 产品质量的控制 ④ 过程质量的控制 ⑤ 过程的改进 ⑥ 产品的改进 ⑦ 技术的革新 E ( H. ① 定义过程 ② 将过程标准化 ③ 开发产品 ④ 改进过程 ⑤ 质量控制 答案:A. ④, B. ①, C. ②, D. ⑤ E. ③ F. ④ G. ② H. ① 分析:软件产品质量是生产者和用户都十分关心的问题,早期的质量管理只看到产品的质量,实行的是最终产品检验的方法,对质量进行控制。把不合格的产品挑拣出来报废或返工后,得到的产品就是合格的。 近年来质量管理向过程质量的控制方向发展,一个重要的基本假设是开发过程的质量直接影响交付产品的质量。这一假设最初是根据制造业情况作出的。因为在制造业中,产品的质量与生产过程有着十分密切的关系,过程的改进自然就会得到高质量的产品。 过程改进的思想是美国工程师戴明(W. E. Deming)提出来的。二次大战后他到日本工作,在他的指导下,日本产业界一直坚持不断地改进过程,取得了很好的效果。戴明等人在质量管理中引入统计质量控制的概念。这一概念的基础是在产品缺陷数和过程之间建立联系,以降低产品缺陷数作为过程改进的目标。过程改进直到过程成为可以重复的为止,就是说,直到过程的结果成为预期的为止。这时,产品的缺陷数就降下来了,然后再将过程标准化,这时下一步的改进又开始了。产品质量与过程的关系的示意图如下。 【例2】国际标准化组织和国际电工委员会于1991年发布了关于软件质量的标准ISO∕IEC 9126―1991。我国于1996年将其等同采用,成为国家标准GB∕T16260―1996《软件产品评价、质量特性及其使用指南》。在此标准中规定了6个质量特性及相关的( A )个质量子特性。质量特性包括( B )、( C )、( D )、效率、可维护性和可移植性等。准确性属于( B ),容错性属于( C ),易学习性属于( D )。 供选择的答案: A. ① 12 ② 16 ③ 21 ④ 22 B ( D. ① 可靠性 ② 适应性 ③ 可使用性 ④ 安全性 ⑤ 一致性 ⑥ 功能性 ⑦ 依从性 ⑧ 互操作性 ⑨ 时间特性 ⑩ 资源特性 答案:A. ③, B. ⑥, C. ①, D. ③ 分析:在ISO∕IEC 9126―1991中规定了6个质量特性及相关的21个质量子特性。如下表所示。这些特性的规定是基于用户的观点的。 功 能 性 与一组功能及其指定的性质的存在有关的一组属性。功能是指能满足规定或隐含需求的那些功能。   适合性 与对规定任务能否提供一组功能以及这组功能能否适合有关的软件属性。   准确性 与能否得到正确的或相符的结果或效果有关的软件属性。   互操作性 与同其它指定系统进行交互操作的能力有关的软件属性。   依从性 使软件服从有关的标准、约定、法规及类似规定的软件属性。   安全性 与避免对程序及数据的非授权故意或意外访问的能力有关的软件属性。  可 靠 性 与在规定的一段时间内和规定的条件下,软件维持其性能水平有关的能力。   成熟性 与由软件故障引起失效的频度有关的软件属性。   容错性 与在软件错误或违反指定接口情况下,维持指定的性能水平的能力有关的软件属性。   易恢复性 与在故障发生后重新建立其性能水平并恢复直接受影响数据的能力,以及为达此目的所需的时间和有关的软件属性。  可使用性 与为使用所需的努力和由一组规定的或隐含的用户对如此使用所做的评价有关的一组属性。   易理解性 与用户为理解逻辑概念及其应用性所需努力有关的软件属性。   易学习性 与用户为学习其应用(例如操作控制、输入、输出)所需努力有关的软件属性。   易操作性 与用户为进行操作或操作控制所需努力有关的软件属性。  效 率 与在规定条件下,软件的性能水平与所用资源量之间的关系有关的一组属性。   时间特性 与响应和处理时间以及软件执行其功能时的吞吐量有关的软件属性。   资源特性 与软件执行其功能时所使用的资源量以及使用资源的持续时间有关的软件属性。  可维护性 与进行规定的修改所需努力有关的一组属性。   易分析性 与为诊断缺陷或失效原因,或为判定待修改的部分所需努力有关的软件属性。   易变更性 与进行修改、调试或适应环境变化所需努力有关的软件属性。   稳定性 与修改造成未预料后果的风险有关的软件属性。   易测试性 为确认经修改软件所需努力有关的软件属性。  可移植性 与软件从一种环境转移到另一环境的能力有关的一组属性。   适应性 与一软件无需采用有别于为该软件准备的处理和手段就能适应规定的环境有关的软件属性。   易安装性 与在指定环境下安装软件所需努力有关的软件属性。   遵循性 使软件服从与可移植性有关的标准或约定的软件属性。   易替换性 与一软件在该软件环境中用来替代指定的其它软件的可能和努力有关的软件属性。   【例3】一个软件产品开发完成投入使用后,常常由于各种原因需要对它做适当的变更。在软件的使用过程中,软件原来的( A )可能不再适应用户的要求,需要进行变更;软件的工作环境也可能发生变化,最常见的是配合软件工作的( B )有变动;还有一种情况是在软件使用过程中发现错误,需要进行修正。通常把软件交付使用后做的变更称为( C )。软件投入使用后的另一项工作是( D ),针对这类软件实施的软件工程活动,主要是对其重新实现,使其具有更好的( E ),包括软件重构、重写文档等。( D )和新的软件开发工作的主要差别在于( H )。我们把常规的软件开发称为( F ),而( G )是从代码开始推导出设计或是规格说明来。 供选择的答案: A, B. ① 环境 ② 软件 ③ 硬件 ④ 功能和性能 ⑤ 要求 C, D, F, G. ① 逆向工程 ② 正向工程 ③ 软件再工程 ④ 维护 ⑤ 设计 E. ① 可靠性 ② 可维护性 ③ 可移植性 ④ 可修改性 H. ① 使用的工具不同 ② 开发的过程不同 ③ 开发的起点不同 ④ 要求不同 答案:A. ④, B. ③, C. ④, D. ③, E. ②, F ②, G ①, H ③ 分析:一个软件产品开发完成投入使用后,常常由于各种原因需要对它做适当的变更。在软件的使用过程中,软件原来的功能和性能可能不再适应用户的要求,需要进行变更;软件的工作环境也可能发生变化,最常见的是配合软件工作的硬件有变动;还有一种情况是在软件使用过程中发现错误,需要进行修正。通常把软件交付使用后做的变更称为维护。软件维护是软件生存期中的一个重要阶段。 软件投入使用后的另一项工作是软件再工程,它和上述的软件维护很有关系,但不是一回事,只是近年来才受到重视。 近年来,随着计算机的普及,各产业部门、政府机构等在软件方面的投资迅速增长。几乎所有的部门、机构的活动都采用了计算机。但大量软件的维护不是轻松的事,需要维护的软件越来越多,而维护工作的开销又大得惊人,这就使得许多机构不可能在开发新的软件上再投资来提高自己的工作能力。另一方面,待维护的软件又常常是一些单位业务工作的关键,其中渗入了许多业务知识和工作经验,这些知识和经验除在软件中体现外并没有其它记载。如果这些软件是早期开发的,当时没有采用软件工程技术,程序结构很差,甚至使用的是陈旧的程序设计语言,或者这些软件的文档已经过时,也可能原来就极不完整,当时的开发人员早已不知去向,使用单位没有人能够了解和看懂软件内部的细节,这样的软件不可能对他们做维护工作。针对这类软件实施的软件工程活动,主要是对其重新实现,使其具有更好的可维护性,包括软件重构、重写文档,或是改用新的编程语言,或是将其数据转移到现行的数据库管理系统中,或是在分布式平台上实现等。 软件再工程和新的软件开发工作的主要差别在于开发的起点不同。再工程工作并不是从编写需求规格说明开始,而是将原有的软件作为规格说明。正因为如此,我们把常规的软件开发称为正向工程,而逆向工程是从代码开始推导出设计或是规格说明来。再工程的最终目标仍然是产生新的、更容易维护的新系统。 【例4】软件维护是软件生存期的最后一个阶段。软件工程学针对维护工作的主要目标是提高( A ),降低( B )。软件的( C )、( D )、( E )是决定软件可维护性的基本因素。软件生存期( F )的工作与软件可维护性有密切的关系。 右图为结构化维护和非结构化维护的对比,请选择合适的答案填入图中。 供选择的答案: A, B. ① 软件的生产率 ② 文档 ③ 软件的可靠性 ④ 软件的可维护性 ⑤ 维护的代价 ⑥ 维护的效率 C, D, E. ① 可测试性 ② 互操作性 ③ 可理解性 ④ 可修改性 ⑤ 可复用性 ⑥ 可管理性 F. ① 编码阶段 ② 设计阶段 ③ 测试阶段 ④ 每个阶段 G ( L. ① 复查 ② 计划途径 ③ 重编程序 ④ 评价设计 ⑤ 评价代码 ⑥ 修改设计 答案:A. ④, B. ⑤, C. ①, D. ③, E. ④, F ④, G ④, H ⑤, I ③, J ①, K ②, L ⑥。其中,C、D、E的答案顺序可互换。 分析:软件维护是软件生存期的最后一个阶段,也是持续时间最长、代价最大的一个阶段。软件工程学针对维护工作的主要目标就是提高软件的可维护性,降低维护的代价。软件维护通常包括4类维护活动:改正性维护、适应性维护、完善性维护和预防性维护。软件的可理解性、可修改性和可测试性是决定软件可维护性的基本因素。软件生存期各个阶段的工作都与软件可维护性有密切的关系。 在右图中描述了作为维护要求结果可能发生的事件流。 如果软件配置的唯一成分使程序代码,则维护活动从艰难地评价程序代码开始。如果程序内部的文档不足,将使得评价更为困难。诸如软件结构、全局数据结构、系统接口、性能和(或)设计约束等微妙的特征难以搞清,常常会产生误解。最后对程序代码所做变更的后果是难于估量的。因为没有测试方面的文档,所以不可能做回归测试(对修改后的软件进行测试以确认软件的功能没有因修改而退化)。这就是非结构化维护,是我们没有使用良好定义的方法论开发软件的必然结果。因此,必须为此付出代价(大量人力的浪费并要经受挫折)。 如果存在完整的软件配置,则维护工作可以从评价设计文档开始,确定软件重要的结构特征、性能特征以及接口特征,估计要求的改动可能带来的影响,并且计划实施途径。然后首先修改设计并对所做的改动进行仔细的复查。接下来编写相应的源代码程序,使用在测试规格说明中包含的信息进行回归测试。最后,把修改后的软件交付使用。这就是结构化维护。它是在软件开发的早期就应用软件工程方法论的结果。虽然有了完整的软件配置并不能保证维护中一定没有问题,但确实能减少人力的浪费且能提高软件维护的总体质量。 【例5】软件可维护性是指纠正软件系统出现的错误和缺陷,以及为满足新的要求进行修改,( A )的容易程度。目前广泛使用7个特性来衡量软件的可维护性,其中就有( B )、( C )、( D )。其中,( B )和( D )主要在改正性维护中侧重应用,( C )主要在适应性维护和( E )维护中侧重应用。 供选择的答案: A. ① 维护 ② 扩充与压缩 ③ 调整 ④ 再工程 B ( D. ① 安全性 ② 可靠性 ③ 完整性 ④ 适应性 ⑤ 可理解性 ⑥ 可使用性 ⑦ 一致性 ⑧ 数据无关性 E. ① 预防性 ② 完善性 ③ 改正性 ④ 容错性 答案:A. ②, B. ②, C. ⑥, D. ⑤, E. ②。其中,B、D的答案顺序可互换。 分析:软件可维护性是指纠正软件系统出现的错误和缺陷,以及为满足新的要求进行修改,扩充或压缩的容易程度。目前广泛使用7个特性来衡量软件的可维护性,其中就有可靠性、可理解性和可使用性。其中,可靠性和可理解性主要在改正性维护中侧重应用,可使用性主要在适应性维护和完善性维护中侧重应用。参看下面有关7种质量特性在各类维护中侧重点的列表。 可理解性 可测试性 可修改性 可 靠 性 可移植性 可使用性 效 率  改正性维护  ○  ○  ○  ○     适应性维护    ○   ○  ○   完善性维护       ○  ○   【例6】软件再工程技术主要有( A )、( B )和( C )。分析、度量属于( C ),知识库和变换属于( D );复用工程属于( E ),其目的是( F ),使其更容易复用。通常是先寻找( G ),然后将其改造,使之能放入( H )中。实际应用可从( H )中选取( I ),以实现复用。 供选择的答案: A ( E. ① 改进软件 ② 编码 ③ 理解软件 ④ 维护 ⑤ 获取、保存及扩充软件知识 F. ① 保存软件 ② 修改软件 ③ 设计软件 ④ 开发软件 G ( I. ① 构件 ② 可复用库 ③ 可复用构件 ④ 信息库 答案:A. ①, B. ⑤, C. ③, D. ⑤, E. ①, F ②, G ①, H ②, I ③ 分析:软件再工程技术包括:ⅰ) 改进软件,如重构、文档重写、复用工程、重新划分模块、数据再工程、业务过程再工程、可维护分析等。ⅱ) 理解软件,如浏览、分析、度量、逆向工程和设计恢复等。ⅲ) 获取、保护及扩充软件知识,如分解、逆向工程和设计恢复、对象恢复、程序理解、知识库和变换等。 因此,本例题的解答是:软件再工程技术主要有改进软件、获取、保存及扩充软件知识和理解软件。分析、度量属于理解软件,知识库和变换属于获取、保存及扩充软件知识;复用工程属于改进软件,其目的是修改软件,使其更容易复用。通常是先寻找构件,然后将其改造,使之能放入可复用库中。实际应用可从库中选取可复用的构件,以实现复用。 四、习题 【7-1】对于软件产品来说,有4个方面影响着产品的质量,即( A )、( B )、( C )及成本、时间和进度等条件。重视软件过程的质量是近年来质量管理理论和实践的新发展。重视软件过程质量的控制,其部分原因可能是:相对于产品质量的控制来说,过程质量的控制是( D )、( E )、( F ),而产品质量的控制是( G )、( H )、( I )。 供选择的答案: A ( C. ① 开发时间 ② 开发技术 ③ 过程质量 ④ 风险控制 ⑤ 质量控制 ⑥ 人员素质 ⑦ 项目管理 ⑧ 配置管理 D ( I. ① 主动的 ② 被动的 ③ 整体的 ④ 系统的 ⑤ 先期的 ⑥ 事后的 ⑦ 个别的 ⑧ 部分的 【7-2】McCall提出了表明软件质量的11个质量特性。它们是( A )、( B )、( C )、( D )、( E )、( F )、( G )、( H )、效率、可测试性和互连性。我们把这11个特性分为3组,使其分别隶属于产品修正、产品转移和产品运行等3个方面,如图所示。 供选择的答案: A ( H. ① 可读性 ② 正确性 ③ 功能性 ④ 完整性 ⑤ 可靠性 ⑥ 可移植性 ⑦ 可复用性 ⑧ 灵活性 ⑨ 可维护性 ⑩ 可使用性 【7-3】为什么软件需要维护?维护有哪几种类型?简述它们的维护过程。 【7-4】 在软件维护的实施过程中,为了正确、有效地修改,需要经历以下3个步骤:( A )、( B )、( C )。( A )是决定维护成败和质量好坏的关键。( C )包括( D )确认、计算机确认和维护后的( E )。 供选择的答案: A ( C. ① 修改程序 ② 建立目标程序 ③ 分析和理解程序 ④ 重新验证程序 ⑤ 验收程序 D. ① 动态 ② 静态 ③ 人工 ④ 自动 E. ① 验证 ② 验收 ③ 检验 ④ 存档 【7-5】从供选择的答案中选出同下列各叙述关系最密切的字句。 A. 软件从一个计算机系统或环境转移到另一个计算系统或环境的容易程度。 B. 软件在需要它投入使用时能实现其指定的功能的概率。 C. 软件使不同的系统约束条件和用户需求得到满足的容易程度。 D. 在规定的条件下和规定的一段期间内,实现所指定的功能的概率。 E. 尽管有不合法的输入,软件仍能继续正常工作的能力。 供选择的答案: ① 可测试性 ② 可理解性 ③ 可靠性 ④ 可移植性 ⑤ 可使用性 ⑥ 兼容性 ⑦ 容错性 ⑧ 可修改性 ⑨ 可接近性  ⑩ 一致性 【7-6】改错性维护与“排错”是否是一回事?为什么? 【7-7】从下列叙述中选出5条与提高软件的可移植性有关的叙述。 ① 把程序中与计算机硬件特性有关的部分集成在一起。 ② 选择时间效率和空间效率高的算法。 ③ 使用结构化的程序设计方法。 ④ 尽量用高级语言编写程序中对效率要求不高的部分。 ⑤ 尽可能减少注释。 ⑥ 采用表格控制方式。 ⑦ 文档资料详尽、正确。 ⑧ 在有虚拟存储器的计算机系统上开发软件。 ⑨ 减少程序中对文件的读写次数。 ⑩ 充分利用宿主计算机的硬件特性。 【7-8】软件可移植性是用来衡量软件的( A )的重要尺度之一。为了提高软件的可移植性,应注意提高软件的( B )。采用( C )有助于提高( B )。为了提高可移植性,还应( D )。使用( E )语言开发的系统软件具有较好的可移植性。 供选择的答案: A. ① 通用性 ② 效率 ③ 质量 ④ 人机界面 B. ① 使用的方便性 ② 简洁性 ③ 可靠性 ④ 设备独立性 C. ① 优化算法 ② 专用设备 ③ 表格驱动方式 ④ 树型文件目录 D. ① 有完备的文件资料 ② 选择好的宿主计算机 ③ 减少输入输出次数 ④ 选择好的操作系统 E. ① COBOL ② APL ③ C ④ SQL 【7-9】下面有关软件维护的叙述有些是不准确的,请将它们列举出来。 供选择的答案: ① 要维护一个软件,必须先理解这个软件。 ② 阅读别人写的程序并不困难。 ③ 如果文档不齐全也可以维护一个软件。 ④ 谁写的软件就得由谁来维护这个软件。 ⑤ 设计软件时就应考虑到将来的可修改性。 ⑥ 维护软件时一件很吸引人的创造性工作。 ⑦ 维护软件就是改正软件中的错误。 ⑧ 维护好一个软件是一件很难的事情。 【7-10】什么是程序修改的副作用?程序修改的副作用有哪几种?试举例说明? 【7-11】讨论高级语言对适应性维护的影响。使程序适应新的环境是可能的吗? 【7-12】在软件计划中是否应该把维护费用计划在内?实际情况如何? 【7-13】软件再工程是一类软件工程活动,它能够使我们:ⅰ)增进对软件的理解;ⅱ)准备或直接提高软件自身的( A )、( B )或演化性。第ⅱ部分旨在改善软件的( C ),使得软件更容易为人们服务。纯粹是出于改善性能的代码优化( D )软件再工程。逆向工程属于上述软件再工程的第( E )部分。 供选择的答案: A, B. ① 可靠性 ② 灵活性 ③ 可维护性 ④ 可复用性 ⑤ 可修改性 C. ① 静态质量 ② 动态质量 ③ 性能 ④ 功能 D. ① 属于 ② 不属于 E. ① ⅱ ② ⅰ 【7-14】关于软件再工程的定义有这样两种说法。ⅰ)软件再工程是变更系统(或程序)的( A ),或是系统(或程序)的( B ),而不变更其( C )的一种工程活动。ⅱ)检查并改进对象系统,按新的模式对系统进行( D ),进而实现其新的模式。 多数软件再工程工具可按图示的自动进行再工程的模式工作。请选择合适的答案完成这个图示。 供选择的答案: A ( D. ① 外部环境 ② 接口 ③ 内部机制 ④ 流程图 ⑤ 重构 ⑥ 数据结构 ⑦ 功能性 ⑧ 层次性 E ( H. ① 其它产品 ② 软件工具 ③ 信息库 ④ 软件 ⑤ 软件的新视图 【7-15】( A )是软件的一种表达形式,或是有关软件的一种报告。( A )可分为4类:软件的规格说明属于( B ),PDL或DFD属于( C ),规格说明文本的图索引属于( D ),程序和程序段属于( E )。( A )得到工具支持时,就成为( F ),可用其支持加入、变更或浏览信息。 供选择的答案: A, F. ① 软件说明 ② 概要设计 ③ 软件视图 ④ 信息库 ⑤ 视图编辑器 ⑥ 软件设计 B ( E. ① A类视图 ② 1类视图(非过程性描述和∕或元描述) ③ 2类视图(伪过程性描述和∕或面向体系结构的描述) ④ 3类视图(纯过程性描述或直接导出的信息) 五、习题解答 【7-1】A. ②, B. ③, C. ⑥, D. ⑤, E. ①, F ④, G ⑥, H ②, I ⑦ 其中,A、B、C答案顺序可互换,D、E、F答案顺序可互换,G、H、I答案顺序可互换。 对于软件产品来说,有4个方面影响着产品的质量,即开发技术、过程质量、人员素质及成本、时间和进度等条件。这4个方面因素对产品质量究竟有多少影响又取决于项目的项目的规模和项目的类型。 重视软件过程的质量是近年来质量管理理论和实践的新发展,但不能把产品质量的控制与过程质量的控制相对立起来。重视软件过程质量的控制,其部分原因可能是,相对于产品质量的控制来说,过程质量的控制是先期的、主动的、系统的,而产品质量的控制是事后的(产品已经生产出来)、被动的(发现了不合格产品只能报废或采取其它补救措施)、个别的(逐个产品的质量检验)。 【7-2】A ②, B ⑤, C ⑩, D ④, E ⑨, F ⑧, G ⑥, H ⑦ 其中,A、B、C、D答案顺序可互换,E、F答案顺序可互换,G、H答案顺序可互换。 McCall质量模型如图所示。用户对于已经投入使用的软件,会特别关注软件运行以后所表现出来的运行特性、修正特性和转移特性。它们直接关系到软件的使用寿命。因此,软件的11个质量特性应分为3组,分别隶属于这3个方面。 其中,属于产品运行特性的有正确性、可靠性、可使用性、完整性和效率。属于产品修正特性的有可维护性、可测试性及灵活性。属于产品转移特性的有可移植性、可复用性和互连性等。 【7-3】在软件开发完成交付用户使用后,为了保证软件在一个相当长的时期能够正常运行,就需要对软件进行维护。 软件维护的类型有4种:改正性维护、适应性维护、完善性维护和预防性维护。其中,改正性维护是要改正在特定的使用条件下暴露出来的一些潜在程序错误或设计缺陷;适应性维护是要在软件使用过程中数据环境发生变化或处理环境发生变化时修改软件以适应这种变化;完善性维护是在用户和数据处理人员使用软件过程中提出改进现有功能,增加新的功能,以及改善总体性能的要求后,修改软件以把这些要求纳入到软件之中。 由这些原因引起的维护活动可以归为以下几类:预防性维护是为了提高软件的可维护性、可靠性等,事先采用先进的软件工程方法对需要维护的软件或软件中的某一部分(重新)进行设计、编制和测试,为以后进一步改进软件打下良好基础。 软件维护的过程如图所示。第一步是先确认维护要求。这需要维护人员与用户反复协商,弄清错误概况以及对业务的影响大小,以及用户希望做什么样的修改,并把这些情况存入故障数据库。然后由维护组织管理员确认维护类型。 对于改正性维护申请,从评价错误的严重性开始。如果存在严重的错误,则必须安排人员,在系统监督员的指导下,进行问题分析,寻找错误发生的原因,进行“救火”性的紧急维护;对于不严重的错误,可根据任务、机时情况、视轻重缓急,进行排队,统一安排时间。对于适应性维护和完善性维护申请,需要先确定每项申请的优先次序。若某项申请的优先级非常高,就可立即开始维护工作,否则,维护申请和其它的开发工作一样,进行排队,统一安排时间。并不是所有的完善性维护申请都必须承担,因为进行完善性维护等于是做二次开发,工作量很大,所以需要根据商业需要、可利用资源的情况、目前和将来软件的发展方向、以及其它的考虑,决定是否承担。 尽管维护申请的类型不同,但都要进行同样的技术工作。这些工作有:修改软件需求说明、修改软件设计、设计评审、对源程序做必要的修改、单元测试、集成测试(回归测试)、确认测试、软件配置评审等。在每次软件维护任务完成后,最好进行一次情况评审,对以下问题做一总结: ( 在目前情况下,设计、编码、测试中的哪一方面可以改进? ( 哪些维护资源应该有但没有? ( 工作中主要的或次要的障碍是什么? ( 从维护申请的类型来看是否应当有预防性维护? 情况评审对将来的维护工作如何进行会产生重要的影响,并可为软件机构的有效管理提供重要的反馈信息。   【7-4】A. ③, B. ①, C. ④, D. ②, E. ② 在软件维护的实施过程中,为了正确、有效地修改,需要经历以下3个步骤:分析和理解程序、修改程序和重新验证程序。经过分析,全面、准确、迅速地理解程序是决定维护成败和质量好坏的关键。有如下几种方法:分析程序结构图、数据跟踪、控制跟踪及其它方法。在将修改后的程序提交用户之前,需要通过静态确认、计算机确认和维护后的验收,保证修改后的程序的正确性。 【7-5】A. ④, B. ⑤ C. ⑥, D. ③, E. ⑦ 论述A是指可移植性。可移植性的定义是:将一个软件系统从一个计算机系统或环境移植到另一个计算机系统或环境中运行时所需工作量的大小。 论述B是指可使用性。可使用性的定义是:程序方便、实用、及易于使用的程度。用户一有请求,就能对每一个操作方式作出解释,始终如一地按照用户的要求运行。计算其按用户请求实现指定功能的概率,是一种度量准则。 论述C是指兼容性。有两类基本的兼容性:向下兼容和交错兼容。向下兼容是软件新版本保留它早期版本的功能的情况;交错兼容是共同存在的两个相关但不同的产品之间的兼容性。软件可以在不同系统约束和不同用户需求下完成指定的工作。 论述D是指可靠性。可靠性的定义是:一个程序按照用户的要求和设计目标,在给定的一段时间内正确执行的概率。 论述E是指容错性。容错性的定义是:系统出错(机器临时发生故障或数据输入不合理)时,能以某种预定方式,做出适当处理,得以继续执行和恢复系统的能力。 【7-6】改错性维护与“排错(调试)”不是一个概念。调试是作为测试的后继工作而出现的,是当测试发现软件中的错误后,进一步诊断和改正程序中潜在的错误的活动。而改正性维护是指在软件交付使用后,由于开发时测试的不彻底、不完全,必然会有一部分隐藏的错误被带到运行阶段来,这些隐藏下来的错误在某些特定的使用环境下就会暴露出来。为了识别和纠正软件错误、改正软件性能上的缺陷、排除实施中的误使用所进行的诊断和改正错误的过程。调试在程序编码阶段、测试阶段、运行和维护阶段都可以发挥作用,它实际上是一种工具或手段。在软件交付运行之后,用户实际充当了测试员的角色,一旦发现软件运行中的错误或缺陷,就会将问题报告通报软件销售商,申请软件维护。其后软件维护人员可以利用调试手段来诊断和改正软件中存在的错误。这时可能涉及的范围不只包括程序,还有文档和数据,不仅可能修改程序代码,而且可能需要修改设计。甚至需求。所以改正性维护是在更大范围中做工作。 【7-7】正确的叙述有 ①、③、④、⑥、⑦。 为了提高软件的可移植性,应当尽可能用高级语言编写源程序代码。对于与硬件或操作系统有关的部分,或对效率要求很高的部分,应当为它们建立专门的模块,将用汇编语言写的程序封装在这些模块中,与程序中其它部分以事先约定的标准方式接口。这样,一旦硬件环境或操作系统环境发生变化,只需修改个别模块即可。 采用表格控制方式,将所有的外部设备接口或与其它系统的接口,包括信息传递、驱动程序入口等都用表格控制,即使将来硬件、相关软件发生的变化,只需修改表格中的登记项,原来的程序一律可以不改。 为了将来修改方便,不致于引入新的错误,相关文档一定要齐全、正确,程序中必须有必要的注释,并使用如结构化程序设计方法这样的良好的程序设计方法来编写程序。至于算法选择,与效率有关,与可移植性无关。其它叙述,如⑧、⑨、⑩,都不利于可移植性。 【7-8】A. ③, B. ④, C. ③, D. ①, E. ③ 软件可移植性是用来衡量软件质量的重要尺度之一。为了提高软件的可移植性,应注意提高软件的设备独立性。采用表格驱动的方式有助于提高软件的设备独立性。为了提高可移植性,还应有完备的文档资料。使用C语言开发的系统软件具有较好的可移植性。 【7-9】软件维护人员通常不是改软件的开发人员,这给软件维护带来很大的困难。特别是有些软件在开发时没有遵循软件开发的准则,没有开发方法的支持,维护这样的软件就更困难。下面列举一些与软件维护有关的问题。 ① 要维护一个软件,首先必须要理解它。而理解一个别人编写的程序通常是很困难的,尤其是对软件配置(指各种相关的文档)不齐全的软件,理解起来就更加困难。 ② 需要维护的软件往往缺少合格的文档,或者文档资料不齐全,甚至根本没有文档。在软维护中,合格的文档十分重要,它有助于理解被维护的软件。合格的文档不仅要完整正确地反映开发过程各阶段的工作成果,而且应当容易理解并应与程序源代码一致。而错误的文档会把对软件的理解引入歧途。 ③ 在软件维护时,不要指望得到原来开发该软件的人员的帮助。开发人员开发完一个软件后,往往会从事另一软件的开发,甚至已离开原开发单位。即使原来的开发人员还在,也可能时间太久而忘却了实现的细节。。 ④ 多数软件在设计时没有考虑到将来的修改,这给软件的修改造成了困难。而且在修改软件时很可能引入新的差错。 ⑤ 软件维护通常不是一件吸引人的工作。从事维护工作常使维护人员缺乏成就感,这也严重影响维护工作,从而影响了维护质量的提高。 【7-10】所谓副作用是指因修改软件而造成的错误或其它不希望发生的情况,有以下三种副作用: ① 修改代码的副作用:在使用程序设计语言修改源代码时,都可能引入错误。例如,删除或修改一个子程序、删除或修改一个标号、 删除或修改一个标识符、改变程序代码的时序关系、改变占用存储的大小、改变逻辑运算符、修改文件的打开或关闭、改进程序的执行效率,以及把设计上的改变翻译成代码的改变、为边界条件的逻辑测试做出改变时,都容易引入错误。 ② 修改数据的副作用:在修改数据结构时,有可能造成软件设计与数据结构不匹配,因而导致软件出错。数据副作用就是修改软件信息结构导致的结果。例如,在重新定义局部的或全局的常量、 重新定义记录或文件的格式、增大或减小一个数组或高层数据结构的大小、修改全局或公共数据、重新初始化控制标志或指针、重新排列输入/输出或子程序的参数时,容易导致设计与数据不相容的错误。 ③ 文档的副作用:对数据流、软件结构、 模块逻辑或任何其它有关特性进行修改时,必须对相关技术文档进行相应修改。否则会导致文档与程序功能不匹配,缺省条件改变,新错误信息不正确等错误。使得软件文档不能反映软件的当前状态。如果对可执行软件的修改不反映在文档里,就会产生文档的副作用。例如,对交互输入的顺序或格式进行修改,如果没有正确地记入文档中,就可能引起重大的问题。过时的文档内容、索引和文本可能造成冲突,引起用户的失败和不满。 【7-11】所谓适应性维护,是指修改软件以适应外部环境(新的硬、软件配置)或数据环境(数据库、数据格式、数据输入∕输出方式、数据存储介质)发生的变化。 使用高级语言,如FORTRAN,C等,它们以接近自然语言的方式编写程序。这样的程序可理解性强,结构化、模块化都很好,除了与硬件、相关软件(如操作系统)和数据库管理系统有关的部分外,都可以用它们编写程序。但每一种类型的计算机,即使提供同一种高级语言,也有可能渗入它自己特有的功能。为作到可移植,必须避开这些特殊的功能,使用语言的标准文本来编写程序。 当前。许多软件开发商以一些高级语言为核心,开发了众多功能强大的软件编程环境,如Delphi(以Pascal为编程语言),Vasual C++(以C++为编程语言),Vasual Basic(以Basic为编程语言),Powerbuilder(以C++为编程语言)等。在这些软件中提供了大量的控件或类库,可以帮助程序员以最小的工作量实现多种功能,还可以与多种数据库接口。所有这些,对于适应性维护,都有有利的影响。但事情也不是绝对的。在MS Windows*.* 或NT环境下开发的软件不一定能够直接转移到Linux环境下。虽然许多人在这方面正在努力。 国外一些计算机厂商在硬、软件结合的环境下综合考虑软件跨平台使用的问题。例如,Oracle数据库管理系统在几乎所有的硬件平台上,微软和苹果的Windows环境下都可以安装,这样使用Oracle SQL编写的程序在多种硬件平台上,多种数据库和操作系统的环境下,不用修改程序,或修改少量程序,就可以直接运行。 【7-12】针对不同的工作目标,软件计划的可以有以下多种类型: ① 项目实施计划(或称为软件开发计划)──这是软件开发的综合性计划,通常应包括任务、进度、人力、环境、资源、组织等多个方面。 ② 质量保证计划──把软件开发的质量要求具体规定为每个开发阶段可以检查的质量保证活动。 ③ 软件测试计划──规定测试活动的任务、测试方法、进度、资源、人员职责等。 ④ 文档编制计划──规定所开发项目应编制的文档种类、内容、进度、人员职责等。 ⑤ 用户培训计划──规定对用户进行培训的目标、要求、进度、人员职责等。 ⑥ 综合支持计划──规定软件开发过程中所需要的支持,以及如何获取和利用这些支持。 ⑦ 软件分发计划──软件开发项目完成后,如何提供给用户。 并未专门考虑软件的维护费用问题。但实际上,为了提高软件的可维护性,在软件开发的具体操作时,必须考虑将来的维护,采取必要的措施,以降低将来维护的费用。 【7-13】A ③, B ④, C ①, D ②, E ②。其中,A、B答案的顺序可互换。 软件再工程是一类软件工程活动,它能够使我们:ⅰ)增进对软件的理解;ⅱ)准备或直接提高软件自身的可维护性、可复用性或演化性。上述软件再工程的第ⅱ部分旨在改善软件的静态质量,使得软件更容易为人们服务。但是如果修改的目的并不是为了提高可维护性、可复用性或演化性或是帮人们更好地理解,那么应当把这种修改排除在软件再工程之外。如纯粹是出于改善性能的代码优化或对其重构,都不能算做软件再工程。逆向工程是属于上述软件再工程的第ⅰ部分。因为逆向工程所得到的软件表达信息(如源程序)能够帮助人们理解该软件,或是便于对它做进一步的处理。 【7-14】A ③, B ⑥, C ⑦, D ⑤, E ④, F ⑤, G ③, H ①。其中,A、B的答案顺序可互换; 关于软件再工程的定义有这样两种说法。 ⅰ)软件再工程是变更系统(或程序)的内部机制,或是系统(或程序)的数据结构,而不变更其功能性的一种工程活动。 ⅱ)检查并改进对象系统,按新的模式对系统进行重构,进而实现其新的模式。 多数软件再工程工具可按图示的自动进行再工程的模式工作。图中大的矩形框表示某个再工程工具。在分解器中进行语法分析和语义分析之后,将相关信息送到信息库。信息库中的信息经过合成器,可以在软件维护中生成各种视图和其它产品,对于进一步分析,自动进行维护以及进行相关的研究都很有价值。 【7-15】A. ③, B. ②, C. ③, D. ①, E. ④, F ⑤ 软件视图的示例如下图所示。 有规格说明、数据流图(DFD)、源程序、度量值、根据静态源程序导出的报告、以及表明软件性能的测试数据等。图中的软件视图包含了视图信息。在视图得到视图编辑器的支持时,可以使用这种工具加入、变更和浏览视图的信息。 视图分为4类: ① 1类视图:非过程性描述和∕或元描述。例如,软件的规格说明和数据库的概念模式即属于此类。 ② 2类视图:伪过程性描述和∕或面向体系结构的描述。例如,软件设计、PDL描述和软件的体系结构(如模块调用层次图、数据流图DFD)即属于此类。 ③ 3类视图:纯过程性描述或直接导出的信息。例如,源程序、程序段、数据、源程序中的数据定义、由视图分解得到的对象、关系和语法树均属于此类。 ④ A类视图:伴随以上各类的分析视图。 ( A1类视图是对1类视图的分析信息。例如,规格说明文本的图索引。 ( A2类视图是有关2类视图的分析信息,如源模块的耦合度。 ( A3类视图是有关3类视图的分析视图,如源程序中的模块数。 下面将有关视图的概念解释一下。 ① 信息库:信息库是有关软件的信息仓库。通常,把有关软件的信息存入信息库,有3种方法: ( 将软件分解成为对象和关系; ( 逐步得到对象和关系,借助工具将它们送入信息库; ( 从其它信息库引入信息。 ① 分解:把视图变换成信息库中存放的对象和关系。例如,编译器将程序分解为抽象的语法树表示。 ② 合成:根据信息库中的信息生成视图信息。合成器(工具或完成合成工作的人员)在信息库中寻找有关的对象和关系,组合成视图信息,然后根据要求将视图格式化,以显示视图信息。例如,编辑器的后端常常借助于扫视语法树或其它类似成分产生程序代码。 ③ 变换:变换是再工程的核心活动。再工程可在同一类视图或前一类视图上把一个软件视图的信息变换成为另一个视图的信息。例如,再工程变换可以是把源代码(属3类视图)变换成结构化源代码(3类视图)、更新的设计(2类视图)、修正的规格说明(1类视图),或是计算出的静态度量值(A3类视图)。再工程变换通常按某些准则来改进视图中的信息。软件重构则是变换源代码结构(语法和语义)的变换。 变换也是逆向工程和设计恢复的基础。逆向工程与再工程十分相似,只是两者的原始视图和目标视图不同,逆向工程的目标视图一般在前一类视图。例如,把源代码变换成程序结构图,既可以看成是再工程,也可以看成是逆向工程。但把源代码变换成重构的源代码则不是逆向工程的工作,而是再工程的任务了。 正向工程也是一种变换。它是从较前类的软件视图变换到较后类的软件视图的活动。例如,从数据流图(DFD)生成源程序代码,就是正向工程的活动。