软件工程期末复习题

课程学习资料 专栏收录该内容
30 篇文章 12 订阅

软件工程概述
例题分析

【例1】软件是计算机系统中与硬件相互依存的另一部分,它是包括( A )、( B )及( C )的完整集合。其中,( A )是按事先设计的功能和性能要求执行的指令序列。( B )是使程序能够正确操纵信息的数据结构。( C )是与程序开发、维护和使用有关的图文材料。
供选择的答案:
AC. ① 软件 ② 程序 ③ 代码 ④ 硬件
⑤ 文档 ⑥ 外设 ⑦ 数据 ⑧ 图表
答案:A. ②, B. ⑦, C. ⑤
分析:软件是计算机系统中与硬件相互依存的另一部分,它是包括程序、数据及相关文档的完整集合。其中,程序是按事先设计的功能和性能要求执行的指令序列。数据是使程序能够正确操纵信息的数据结构。文档是与程序开发、维护和使用有关的图文材料。需要注意的是,程序与算法在含义上有不同:算法的每一条指令必须是最基本的、必须通过有限步做完,而程序没有这个要求。
【例2】开发软件时对提高软件开发人员工作效率至关重要的是( A )。软件工程中描述生存周期的瀑布模型一般包括计划、( B )、设计、编码、测试、维护等几个阶段,其中设计阶段在管理上又可以依次分成( C )和( D )两步。
供选择的答案:
A. ① 程序开发环境 ② 操作系统的资源管理功能
③ 程序人员数量 ④ 计算机的并行处理能力
B. ① 需求分析 ② 需求调查 ③ 可行性分析 ④ 问题定义
C、D.① 方案设计 ② 代码设计 ③ 概要设计 ④ 数据设计
⑤ 运行设计 ⑥ 详细设计 ⑦ 故障处理设计 ⑧ 软件体系结构设计
答案:A. ①, B. ①, C. ③, D. ⑥
分析:有合适的程序开发环境可以提供有用的工具,大大提高开发人员的工作效率。
软件工程中描述软件生存周期的瀑布模型一般包括计划、需求分析、设计、编码、测试和运行维护等六个阶段。需求分析完成对软件产品在功能、性能、用户接口、运行环境、可靠性、安全性、开发资源、开发进度、开发成本等方面的需求定义。问题定义、可行性分析、需求调查都可能是需求分析中要做的一部分工作。
软件设计在管理上划分为概要设计与详细设计两个步骤。概要设计的目标是建立软件的体系结构,完成全局数据结构设计,同时进行处理方式设计、运行配置设计、出错处理设计、故障恢复设计等。详细设计是对每一个模块的操作的控制流程和局部数据结构进行设计。
【例3】从供选择的答案中选出适当字句填入下列关于软件发展过程的叙述中的( )内。
有人将软件的发展过程划分为4个阶段:
第一阶段(1950~1950年代末)称为“程序设计的原始时期”,这时既没有( A ),也没有( B ),程序员只能用机器指令编写程序。
第二阶段(1950年代末~1960年代末)称为“基本软件期”。出现了( A ),并逐渐普及。随着( B )的发展,编译技术也有较大的发展。
第三阶段(1960年代末~1970年代中期)称为“程序设计方法时代”。这一时期,与硬件费用下降相反,软件开发费急剧上升。人们提出了( C )和( D )等程序设计方法,设法降低软件的开发费用。
第四阶段(1970年代中期~现在)称为“软件工程时期”。软件开发技术不再仅仅是程序设计技术,而是包括了与软件开发的各个阶段,如( E )、( F )、编码、单元测试、综合测试、( G )及其整体有关的各种管理技术。
供选择的答案:
A D:① 汇编语言 ② 操作系统 ③ 虚拟存储器概念 ④ 高级语言
⑤ 结构式程序设计 ⑥ 数据库概念 ⑦ 固件 ⑧ 模块化程序设计
E G:① 使用和维护 ② 兼容性的确认 ③ 完整性的确认 ④ 设计
⑤ 需求定义 ⑥ 图象处理
答案:A. ①, B. ④, C. ⑤, D. ⑧, E. ⑤, F. ④, G. ①。注意,C与D的答案顺序可互换。
分析:在软件发展的早期,汇编语言和高级语言尚未出现,人们只能用机器指令来编写程序。为了好读,有的指令系统可以用八进制代码书写。其后,由于汇编语言的出现,人们摆脱了繁重的地址分配等工作,可以用符号编程。随着高级语言的出现和普及,人们用近似于自然语言的语句编写程序,大大减轻了程序员的负担。高级语言的处理,从解释执行到编译执行,处理效率和存储利用率不断提高。
结构化程序设计和模块化程序设计是科学家为解决软件危机,借用其它领域的技术改进程序设计方法而提出来的。由于这些技术的使用,提高了程序的可读性、局部性、抽象性、清晰性、简单性、确定性、一致性等,降低了程序开发的费用。后来发展到软件工程阶段,明确地划分了软件开发阶段,规范了软件开发过程,明确了各个阶段的任务以及应交付的成果和里程碑,使得软件开发逐步达到工程化和标准化。

习题

【1-1】软件工程过程有哪几个基本过程活动?试说明之。
【1-2】试说明“软件生存周期”的概念。
【1-3】试论述瀑布模型软件开发方法的基本过程。
【1-4】软件工程是开发、运行、维护和修复软件的系统化方法,它包含哪些要素?试说明之。
【1-5】软件工程学的基本原则有哪些?试说明之。
【1-6】有人说:软件开发时,一个错误发现得越晚,为改正它所付出的代价就越大。对否?请解释你的回答。

习题解答

【1-1】软件工程过程的基本过程活动有4步:
·P (Plan) : 软件规格说明。规定软件的功能及其运行的限制;
·D (Do) : 软件开发。产生满足规格说明的软件;
·C (Check) : 软件确认。确认软件能够完成客户提出的要求;
·A (Action) : 软件演进。为满足客户的变更要求,软件必须在使用的过程中演进。
【1-2】软件与任何一个事物一样,有它的孕育、诞生、成长、成熟、衰亡的生存过程。这就是软件的生存周期。它主要分为6个阶段:软件项目计划、软件需求分析和定义、软件设计、程序编码、软件测试,以及运行维护。
(1) 软件项目计划 :在这一步要确定软件工作范围,进行软件风险分析,预计软件开发所需要的资源,建立成本与进度的估算。根据有关成本与进度的限制分析项目的可行性。
(2) 软件需求分析和定义 :在这一步详细定义分配给软件的系统元素。可以用以下两种方式中的一种对需求进行分析和定义。一种是正式的信息域分析,可用于建立信息流和信息结构的模型,然后逐渐扩充这些模型成为软件的规格说明。另一种是软件原型化方法,即建立软件原型,并由用户进行评价,从而确定软件需求。
(3) 软件设计 :软件的设计过程分两步走。第一步进行概要设计,以结构设计和数据设计开始,建立程序的模块结构,定义接口并建立数据结构。此外,要使用一些设计准则来判断软件的质量。第二步做详细设计,考虑设计每一个模块部件的过程描述。经过评审后,把每一个加细的过程性描述加到设计规格说明中去。
(4) 程序编码 :在设计完成之后,用一种适当的程序设计语言或CASE工具生成源程序。应当就风格及清晰性对代码进行评审,而且反过来应能直接追溯到详细设计描述。
(5) 软件测试 :单元测试检查每一单独的模块部件的功能和性能。组装测试提供了构造软件模块结构的手段,同时测试其功能和接口。确认测试检查所有的需求是否都得到满足。在每一个测试步骤之后,要进行调试,以诊断和纠正软件的故障。
(6) 软件维护 :为改正错误,适应环境变化及功能增强而进行的一系列修改活动。与软件维护相关联的那些任务依赖于所要实施的维护的类型。
【1-3】瀑布模型规定了各项软件工程活动,包括:制定软件项目计划,进行需求分析和定义,软件设计,程序编码,测试及运行维护。并且规定了它们自上而下,相互衔接的固定次序,如同瀑布流水,逐级下落。然而软件开发的实践表明,上述各项活动之间并非完全是自上而下,呈线性图式。实际情况是,每项开发活动均应具有以下特征:
(1) 从上一项活动接受本项活动的工作对象,做为输入;
(2) 利用这一输入实施本项活动应完成的内容;
(3) 给出本项活动的工作成果,做为输出传给下一项活动;
(4) 对本项活动实施的工作进行评审。若其工作得到确认,则继续进行下一项活动,否则返回前项,甚至更前项的活动进行返工。
【1-4】软件工程包括三个要素:方法、工具和过程。
软件工程方法为软件开发提供了“如何做”的技术。它包括了多方面的任务,如项目计划与估算、软件系统需求分析、数据结构、系统总体结构的设计、算法过程的设计、编码、测试以及维护等。软件工程方法常采用某一种特殊的语言或图形的表达方法及一套质量保证标准。
软件工具为软件工程方法提供了自动的或半自动的软件支撑环境。目前,已经推出了许多软件工具,已经能够支持上述的软件工程方法。特别地,已经有人把诸多的软件工具集成起来,使得一种工具产生的信息可以为其它的工具所使用,这样建立起一种被称之为计算机辅助软件工程(CASE)的软件开发支撑系统。CASE将各种软件工具、开发机器和一个存放开发过程信息的工程数据库组合起来形成一个软件工程环境。
软件工程的过程则是将软件工程的方法和工具综合起来以达到合理、及时地进行计算机软件开发的目的。过程定义了方法使用的顺序、要求交付的文档资料、为保证质量和协调变化所需要的管理、及软件开发各个阶段完成的里程碑。
【1-5】在软件开发过程中必须遵循下列软件工程原则。
·抽象 : 采用分层次抽象,自顶向下、逐层细化的办法进行功能分解和过程分解,可以由抽象到具体、由复杂到简单,逐步得到问题的解。
·信息隐蔽 : 遵循信息封装,使用与实现分离的原则,将模块设计成“黑箱”,可以将实现的细节隐藏在模块内部,使用者只能通过模块接口访问模块中封装的数据。
·模块化 : 按模块划分系统的体系结构,使得各模块间有良好的接口。这样有助于信息隐蔽和抽象,有助于表示复杂的系统。
·局部化 : 按抽象数据类型思想及问题域中的概念来建立模块,确保模块之间低耦合,模块内部高内聚。这有助于控制解的复杂性。
·确定性 : 软件开发过程中所有概念的表达应是确定的、无歧义性的、规范的。这有助于人们之间的沟通,保证整个开发工作协调一致。
·一致性 : 强调软件开发过程的标准化、统一化。包括文档格式的一致,工作流程的一致,内、外部接口的一致,系统规格说明与系统行为的一致等。
·完备性 : 软件系统不丢失任何重要成分,可以完全实现系统所要求功能。
·可验证性 : 开发大型的软件系统需要对系统自顶向下、逐层分解。系统分解应遵循系统易于检查、测试、评审的原则,以确保系统的正确性。
【1-6】软件开发时,一个错误发现得越晚,为改正它所付出的代价就越大。这个说法是对的。在1970年代,GTE、TRW和IBM等三家公司对此问题做了独立研究,最后它们得到相似的结论:
阶段 需求分析 软件设计 程序编码 单元测试 验收测试 维 护
相对修复代价 0.10.2 0.5 1 2 5 20
从表中可以看出,在需求分析阶段检查和修复一个错误所需的代价只有编码阶段所需代价的1/5到1/10,而在维护阶段做同样的工作所付出的代价却是编码阶段的20倍。
软件需求分析
例题分析

【例1】软件需求分析阶段的工作,可以分为以下4个方面:对问题的识别、分析与综合、编写需求分析文档以及( )。
供选择的答案:
A. 总结 B. 阶段性报告 C. 需求分析评审 D. 以上答案都不正确
答案: C.
分析:作为需求分析阶段工作的复查手段,在需求分析的最后一步,应该对功能的正确性、完整性和清晰性,以及其它需求给予评价。一般,评审的结果都包括了一些修改意见,待修改完成后再经评审通过,才可进入设计阶段。
【例2】各种需求方法都有它们共同适用的( )。
供选择的答案:
A.说明方法 B.描述方式 C. 准则 D.基本原则
答案: D.
分析:虽然各种分析方法都有独特的描述方法,但所有的分析方法还是有它们共同适用的基本原则。这些基本原则包括:
需要能够表达和理解问题的信息域和功能域;
要能以层次化的方式对问题进行分解和不断细化;
要分别给出系统的逻辑视图和物理视图。
【例3】在结构化分析方法中,用以表达系统内数据的运动情况的工具有( )。
供选择的答案:
A. 数据流图 B. 数据词典 C. 结构化英语 D. 判定表与判定树
答案: A.
分析:数据流图从数据传递和加工的角度,以图形的方式刻画数据流从输入到输出的移动变换过程,所以,它不是描述数据的静态结构,而是描述数据流的传递和变换。数据词典主要用于定义数据和控制对象的细节,结构化英语、判定表和判定树主要用于描述加工规格说明,都不是表达数据在系统内运动情况的工具。
【例4】在结构化分析方法中用状态―迁移图表达系统或对象的行为。在状态―迁移图中,由一个状态和一个事件所决定的下一状态可能会有( )个。
供选择的答案:
A. 1 B. 2 C. 多个 D. 不确定
答案: C.
分析:在状态―迁移图中,由一个状态和一个事件所确定的下一状态可能会有多个。实际会迁移到哪一个状态,是由更详细的内部状态和更详细的事件信息来决定的,此时在状态―迁移图中可能需要使用加进判断框和处理框的记法。状态―迁移图的优点:第一,状态之间的关系能够直观地捕捉到,这样用眼睛就能看到是否所有可能的状态迁移都已纳入图中,是否存在不必要的状态等。第二,由于状态―迁移图的单纯性,能够机械地分析许多情况,可很容易地建立分析工具。
【例5】在结构化分析方法中用实体―关系图表达系统中的对象及其关系。在实体―关系图中,表达对象的实例之间的关联有三种类型:一对一联系、( )联系、多对多联系。
供选择的答案:
A. 多对一 B. 一对多
分析:使用实体―关系图,可以建立系统中各个数据对象及对象之间的关系。对象的实例间的关联称为“基数”,共有3种类型的基数:一对一,一对多,多对多。它反映了现实世界中实体之间的联系,多对一的情况可以归入一对多的关联中去。
【例6】 软件需求分析的任务不应包括( A )。进行需求分析可使用多种工具,但( B )是不适用的。在需求分析中,分析员要从用户那里解决的最重要的问题是( C )。需求规格说明书的内容不应当包括( D )。该文档在软件开发中具有重要的作用,但其作用不应当包括( E )。
供选择的答案:
A. ① 问题分析 ② 信息域分析 ③ 结构化程序设计 ④ 确定逻辑模型
B. ① 数据流图 ② 判定表 ③ PAD图 ④ 数据词典
C. ① 要让软件做什么 ② 要给该软件提供哪些信息
③ 要求软件工作效率如何 ④ 要让软件具有什么样的结构
D. ① 对重要功能的描述 ② 对算法的详细过程性描述
③ 软件确认准则 ④ 软件的性能
E. ① 软件设计的依据 ② 用户和开发人员对软件要“做什么”的共同理解
③ 软件验收的依据 ④ 软件可行性分析的依据
答案:A. ③, B. ③, C. ①, D. ②, E. ④
分析:软件需求分析的任务是通过与用户的合作,了解用户对待开发系统的要求;根据对用户要求的系统所在的信息域的调查、分析,确定系统的逻辑模型;并对求解的问题做适当的分解,使之适合于计算机求解。需求分析的结果是软件需求规格说明书。
结构化程序设计是在详细设计和编码阶段所采用的技术,而不是需求分析阶段要采用的技术。在需求分析阶段,分析人员可以用数据流图描述系统的数据流的变换和流向,用数据词典定义在数据流图中出现的数据流、数据文件、加工或处理,用判定表表示复杂条件和动作组合的情况。但PAD图是在详细设计阶段使用的描述加工逻辑的工具,不适用于需求分析。此外,软件需求分析阶段只确定软件系统要“做什么”,完成对重要功能、性能、确认准则的描述,至于“怎么做”由后续的设计阶段完成,对算法的详细过程性描述也是在设计阶段给出。软件可行性分析应在需求分析之前,所以需求分析规格说明不能成为可行性分析的依据。
【例7】原型化方法是用户和软件开发人员之间进行的一种交互过程,适用于( A )系统。它从用户界面的开发入手,首先形成( B ),用户( C ),并就( D )提出意见,它是一种( E )型的设计过程。
供选择的答案:
A. ① 需求不确定性高的 ② 需求确定的 ③ 管理信息 ④ 决策支持
B. ① 用户界面使用手册 ② 用户界面需求分析说明书
③ 系统界面原型 ④ 完善的用户界面
C. ① 改进用户界面的设计 ② 阅读文档资料
③ 模拟用户界面的运行 ④ 运行用户界面原型
D.① 同意什么和不同意什么 ② 使用和不使用哪一种编程语言
③ 程序的结构 ④ 执行速度是否满足要求
E.① 自外向内 ② 自顶向下 ③ 自内向外 ④ 自底向上
答案:A. ① B. ③ C. ④ D. ① E. ①
分析:通常,原型是指模拟某种产品的原始模型。在软件开发中,原型是软件的一个早期可运行的版本,它反映最终系统的部分重要特性。
使用原型的原型化方法特别适用于需求不确定性较高的软件系统的开发。它的基本思想是根据用户给出的基本需求,通过快速实现构造出一个小型的可执行的模型,满足用户的基本要求,这就是系统界面原型。让用户计算机上实际运行这个用户界面原型,在试用的过程中得到亲身感受和受到启发,做出反应和评价,提出同意什么和不同意什么。然后开发者根据用户的意见对原型加以改进。随着不断试验、纠错、使用、评价和修改,获得新的原型版本,如此周而复始,逐步减少分析和通信中的误解,弥补不足之处,进一步确定各种需求细节,适应需求的变更,从而提高了最终产品的质量。
它是一种自外向内型的设计过程。
习题
【2-1】在软件需求分析时,首先建立当前系统的物理模型,再根据物理模型建立当前系统的逻辑模型。试问:什么是当前系统?当前系统的物理模型与逻辑模型有什么差别?
【2-2】软件需求分析是软件工程过程中交换意见最频繁的步骤。为什么交换意见的途径会经常阻塞?
【2-3】你认为一个系统分析员的理想训练和基础知识是什么?请说明理由。
【2-4】可行性研究主要研究哪些问题?试说明之。
【2-5】信息和信息结构有什么区别?有没有不存在信息流的系统?有没有不存在信息结构的系统?
【2-6】软件需求分析的操作性原则和需求工程的指导性原则是什么?
【2-7】数据流图的作用是什么?它有哪些基本成份?
【2-8】考务处理系统的分层数据流图如下图所示。
该考务处理系统有如下功能:
①对考生送来的报名表进行检查;
② 对合格的报名表编好准考证号码后将准考证送给考生,并将汇总后的考生名单送给阅卷站;
③ 对阅卷站送来的成绩表进行检查,并根据考试中心指定的合格标准审定合格者;
④ 填写考生通知单(内容包含考试成绩及合格∕不合格标志),送给考生;
⑤ 按地区、年龄、文化程度、职业、考试级别等进行成绩分类统计及试题难度分析,产生统计分析表。
(1) 图©中,加工1.1的输入数据流是( A ),输出数据流是( B ),图(b)中,加工2的输出数据流是( C ),它是由( D )和( E )组成。
供选择的答案:
A E. ① 统计分析表 ② 报名表 ③ 准考证 ④ 考生通知单
⑤ 合格报名表 ⑥ 难度分析表 ⑦ 错误成绩表 ⑧ 分类统计表

(2) 图(d)中的文件“试题得分表”是否在图(b)中漏掉了? 回答是(  F  )。

供选择的答案:
F. ① “试题得分表”没有在图(b)中画出,是错误的。
② “试题得分表”是图(b)中加工的内部文件,不必在图(b)中画出。
③ “试题得分表”是多余的。
【2-9】Petri网可以描述计算机软件系统的执行。现有一个程序如下(类似于Pascal语言)
L : S1;
WHILE P1 DO
BEGIN
IF P2 THEN S2
ELSE S3;
COBEGIN
S4;
S5;
S6;
COEND
END;
GOTO L;
其中,P1和P2为逻辑表达式,S1S6是单个执行语句,COBEGIN和COEND是并行执行开始和并行执行结束(即S4,S5和S6语句并行执行)。试用Petri网描述这段程序的执行过程。
【2-10】数据词典的作用是什么?它有哪些基本词条?
【2-11】传统的软件开发模型的缺陷是什么?原型化方法的类型有哪些?原型开发模型的主要优点是什么?
【2-12】试简述原型开发的过程和运用原型化方法的软件开发过程。
【2-13】软件需求分析说明书主要包括哪些内容?
【2-14】阅读下列关于开发人事管理系统的交互式工作方式的叙述,再回答问题。
某大企业最近决定采用高性能微机开发人事管理系统,将四台联机终端分置于人事处的三个科室。该系统可供操作员和程序员使用,也可供人事处负责人和主管人事的副厂长等查询人事信息用。人事管理系统通过录入人事数据和修改、删除等操作,产生和更新各类人事文件,通过搜索这些文件进行各类人事信息的查询。
该企业有3000多个工人、干部和技术人员,大体可分成机关科室、生产车间、后勤服务和开发研制部门等几类部门。厂领导决定由计算机应用科来负责协调和开发应用系统。计算机应用科科长指示系统工程师张某负责进行系统分析。
考虑到人事处有大量的查询信息要求、频繁的人事信息修改和文件存档、查阅等特点,计算机应用科决定认真设计人机交互界面,首先设计好在终端上的交互式会话的方式。
系统工程师张某通过调查收集到如下10条意见:
(1)某程序员认为:系统在屏幕格式、编码等方面应具有一致性和清晰性,否则会影响操作人员的工作效率。
(2)某操作人员认为:在交互式会话过程中,操作人员可能会忘记或记错某些事情,系统应当提供HELP功能。
(3)某操作人员认为:既然是交互式会话,那么对所有的输入都应当作出响应,不应出现击键后,计算机没有任何反应的情况。
(4)某操作人员认为:在出错的时候,交互式会话系统应当给出出错信息,并且尽可能告诉我们出错的性质和错在什么地方。
(5)某程序员认为:终端会话也应当符合程序员编制程序时的习惯,这样可以更高效地维护人事管理系统。
(6)教育科干部甲认为:应当对操作员进行一些必要的培训,让他们掌握交互式会话系统的设计技巧,有助于提高系统的使用效率。
(7)教育科干部乙认为:尽管操作人员的指法已经强化训练但在交互式会话时应尽可能缩短和减少操作员输入的信息,以降低出错概率。
(8)某程序员认为:由于本企业中有很多较大的文件,文件的查找很费时间,交互式会话系统在响应时间较长时应给予使用者以提示信息。
(9)人事处干部丙认为:我们企业的人事资料相当复杂,格式非常之多,希望交互式系统使用十分清晰的格式,并容易对输入数据中的错误进行修改。
(10) 人事处干部丁认为:人事管理系统应当具有相当的保密性和数据安全性,因此在屏幕上显示出的信息应该含混一些,以免泄密。
系统工程师张某对上述调查情况和其他要求作了分析后,发现收集到的10条意见中有3条意见是不能接受的,写出编号并各用40字以内叙述理由。

习题解答

【2-1】所谓当前系统可能是需要改进的某个已在计算机上运行的数据处理系统,也可能是一个人工的数据处理过程。当前系统的物理模型客观地反映当前系统实际的工作情况。但在物理模型中有许多物理的因素,随着分析工作的深入,有些非本质的物理因素就成为不必要的负担,因而需要对物理模型进行分析,区分出本质的和非本质的因素,去掉那些非本质的因素即可获得反映系统本质的逻辑模型。所以当前系统的逻辑模型是从当前系统的物理模型抽象出来的。
【2-2】软件需求分析过程中,由于最初分析员对要解决的问题了解很少,用户对问题的描述、对目标软件的要求也很凌乱、模糊,再加上分析员和用户共同的知识领域不多,导致相互间通信的需求。首先,由于分析员和用户之间需要通信的内容相当多,业务知识上的不足,表达方式的不足,可能对某些需求存在错误解释或误解的可能性,造成需求的模糊性。其次,用户和分析员之间经常存在无意识的“我们和他们”的界限,不是按工作需要组成统一的精干的队伍,而是各自定义自己的“版图”,并通过一系列备忘录、正式的意见书、文档,以及提问和回答来相互通信。历史已经证明,这样会产生大量误解。忽略重要信息,无法建立成功的工作关系。
【2-3】系统分析员处在用户和高级程序员之间,负责沟通用户和开发人员的认识和见解,起着桥梁的作用。一方面要协助用户对所开发的软件阐明要求,另一方面还要与高级程序员交换意见,探讨用户所提要求的合理性以及实现的可能性。最后还要负责编写软件需求规格说明和初步的用户手册。

为能胜任上述任务,分析员应当具备如下的素质:
(1) 能够熟练地掌握计算机硬、软件的专业知识,具有一定的系统开发经验。
(2) 善于进行抽象的思维和创造性的思维,善于把握抽象的概念,并把它们重新整理成为各种逻辑成分,并给出简明、清晰的描述。
(3) 善于从相互冲突或混淆的原始资料中抽出恰当的条目来。
(4) 善于进行调查研究,能够很快学习用户的专业领域知识,理解用户的环境条件。
(5) 能够倾听他人的意见,注意发挥其它人员的作用。
(6) 具有良好的书面和口头交流表达能力。
【2-4】可行性研究主要做4个方面的研究:
经济可行性 :进行成本∕效益分析。从经济角度判断系统开发是否“合算”。
技术可行性 :进行技术风险评价。从开发者的技术实力、以往工作基础、问题的复杂性等出发,判断系统开发在时间、费用等限制条件下成功的可能性。
法律可行性 :确定系统开发可能导致的任何侵权、妨碍和责任。
方案的选择 :评价系统或产品开发的几个可能的候选方案。最后给出结论意见。
【2-5】什么是信息?广义地讲,信息就是消息。宇宙三要素(物质、能量、信息)之一。它是现实世界各种事物在人们头脑中的反映。此外,人们通过科学仪器能够认识到的也是信息。信息的特征为:可识别、可存储、可变换、可处理、可传递、可再生、可压缩、可利用、可共享。我们通常讲的信息域就是对信息的多视角考虑。信息域包含3个不同的视图:信息内容和关系、信息流和信息结构。为了完全理解信息域,必须了解每一个视图。
信息结构:它是信息在计算机中的组织形式。一般表示了各种数据和控制对象的内部组织。数据和控制对象是被组织成n维表格,还是组织成有层次的树型结构? 在结构中信息与其它哪些信息相关? 所有信息是在一个信息结构中,还是在几个信息结构中? 一个结构中的信息与其它结构中的信息如何联系? 这些问题都由信息结构的分析来解决。
信息流:表示数据和控制在系统中传递时的变化方式。输入对象首先被变换成中间信息(数据或控制),然后再变换成输出结果信息。沿着变换路径,可能从已有的数据存储(如磁盘文件或内存缓冲区)中引入附加的信息。对数据进行变换是程序中应有的功能或子功能。两个变换功能之间的数据传递就确定了功能间的接口。
所以,没有信息流的系统相当于没有功能的系统,这样的系统的存在是毫无意义的。而没有信息结构的系统是没有信息的系统,这样的系统不是计算机能够处理的系统。
【2-6】所有的需求分析方法都与一组操作性原则相关联:
必须理解和表示问题的信息域。
必须定义软件将完成的功能。
必须表示软件的行为(作为外部事件的结果)。
必须对描述信息、功能和行为的模型进行分解,能够以层次方式揭示其细节。
分析过程应当从要素信息转向细节的实现。
通过使用这些原则,分析员可以系统地处理问题。首先检查信息域以便更完整地理解目标软件的功能,再使用模型以简洁的方式表达目标软件的功能和行为,并利用自顶向下、逐层分解的手段来降低问题的复杂性。在这些处理过程中,因处理需求带来的逻辑约束和因其它系统元素带来的物理约束需要通过软件要素和视图的实现加以检验和确认。
除此以外,Davis建议了一组针对“需求工程”的指导性原则:
在开始建立分析模型之前应当先理解问题。如果问题没有很好理解就急于求成,常常会产生一个解决错误问题的完美的软件。
强力推荐使用原型。这样做可以使用户了解将如何与计算机交互,而人们对软件质量的认识常常是基于对界面“友好性”的切身体会。
记录每一个需求的起源和原因。这是建立对用户要求的可追溯性的第一步。
使用多个视图,建立系统的数据、功能和行为模型。这样做可帮助分析员从多方面分析和理解问题,减少遗漏,识别可能的不一致之处。
给需求赋予优先级。因为过短的时限会减少实现所有软件需求的可能性。因此,对需求排定一个优先次序,标识哪些需求先实现,哪些需求后实现。
注意消除歧义性。因为大多数需求都是以自然语言描述,存在叙述的歧义性问题,造成遗漏和误解。采用正式的技术评审是发现和消除歧义性的好方法。
遵循以上原则,就可能开发出较好的软件需求规格说明,为软件设计奠定基础。
【2-7】数据流图可以用来抽象地表示系统或软件。它从信息传递和加工的角度,以图形的方式刻画数据流从输入到输出的移动变换过程,同时可以按自顶向下、逐步分解的方法表示内容不断增加的数据流和功能细节。因此,数据流图既提供了功能建模的机制,也提供了信息流建模的机制,从而可以建立起系统或软件的功能模型。
数据流图的基本成份有4种:

【2-8】答案:A. ② B. ⑤ C. ① D. ⑥ E. ⑧ F. ②
其中,D与E的答案可互换。
应注意的问题:
① 适当地为数据流、加工、文件、数据的源∕汇点命名。名字应反映该元素的实际含义,避免空洞的名字。如数据、信息处理、计算等名字都不好。
② 画数据流时不要夹带控制流。数据流图中各种数据的加工没有考虑时序关系,引入控制流后,加工之间就有了时序关系,这与画数据流图不考虑实现细节的初衷相违背。
③ 一个加工的输出数据流不要与该加工的输入数据流重名,即使它们的组成成分相同。例如图©中加工1.1的输入数据流“报名表”与输出数据流“合格报名表”。
④ 允许一个加工有多个数据流流向另一个加工,也允许一个加工有两个相同的输出数据流流向两个不同的加工。
⑤ 保持父图与子图的平衡。就是说,父图与它的子图的输入数据流与输出数据流应当在数量与名字上都相同。特别的是,如果父图的一个输入(或输出)数据流对应于子图中几个输入(或输出)数据流,但子图中这几个数据流中的数据项合起来正好是父图中的那个数据流,这时它们还算是平衡的。例如,图(b)中加工2的输出数据流“统计分析表”是由“难度分析表”和“分类统计表”组成,那么图(b)与图(d)仍满足父图与子图平衡的条件。
⑥ 在自顶向下的分解过程中,若一个文件首次出现时只与一个加工有关,那么这个文件应作为这个加工的内部文件而不必画出。例如,图(d)中的文件“试题得分表”就是图(b)中加工的内部文件,所以在图(b)中没有画出。
⑦ 保持数据守恒。就是说,一个加工的所有输出数据流中的数据必须能从该加工的输入数据流中直接获得,或者是通过该加工产生的数据。
【2-9】采用条件∕事件网(C∕E网,C―Condition, E―Event)式Petri网。其定义如下:
①当事件e激发时条件c开始成立,则称c是e的后继。此关系用“”表示;
②当事件e激发时条件c消失成立,则称c是e的前驱。此关系用“”表示;
③当事件e激发时条件c不受影响,则c和e之间没有前驱、后继关系,无边。
根据定义,给定程序的C∕E网如下:

【2-10】分析模型中包含了对数据对象、功能和控制的表示。在每一种表示中,数据对象和控制项都扮演一定的角色。为表示每个数据对象和控制项的特性,建立了数据词典。数据词典精确地、严格地定义了每一个与系统相关的数据元素,并以字典式顺序将它们组织起来,使得用户和分析员对所有的输入、输出、存储成分和中间计算有共同的理解。
在数据词典的每一个词条中应包含以下信息:
① 名称:数据对象或控制项、数据存储或外部实体的名字。
② 别名或编号。
③ 分类:数据对象?加工?数据流?数据文件?外部实体?控制项(事件∕状态)?
④ 描述:描述内容或数据结构等。
⑤ 何处使用:使用该词条(数据或控制项)的加工。
【2-11】传统软件生存期范型的典型代表是“瀑布模型”。这种模型的核心是将软件生存期划分为软件计划、需求分析、软件设计、编码、测试和运行维护等阶段,根据不同阶段工作的特点,运用不同的方法、技术和工具来完成该阶段的任务。软件开发人员遵循严格的规范,在每一阶段工作结束时都要进行严格的阶段评审和确认,以得到该阶段的一致、完整、正确和无歧义性的文档资料,并以它们做为下一阶段工作的基础。
传统思想强调每一阶段的严格性,尤其是开发初期要有良好的软件规格说明,主要是源于过去软件开发的经验教训,即在开发的后期或运行维护期间来修改不完善的规格说明要付出巨大的代价。但是,要想得到一个完整准确的规格说明不是一件容易的事。特别是对于一些大型的软件项目,在开发的早期用户往往对系统只有一个模糊的想法,很难完全准确地表达对系统的全面要求,软件开发人员对于所要解决的应用问题认识更是模糊不清。经过详细的讨论和分析,也许能得到一份较好的规格说明,但却很难期望该规格说明能将系统的各个方面都描述得完整、准确、一致,并与实际环境相符。很难通过它在逻辑上推断出(不是在实际运行中判断评价)系统运行的效果,以此达到各方对系统的共同理解。随着开发工作向前推进,用户可能会产生新的要求,或因环境变化,要求系统也能随之变化;开发人员又可能在设计与实现的过程中遇到一些没有预料到的实际困难,需要以改变需求来解脱困境。因此规格说明难以完善、需求的变更、以及通信中的模糊和误解,都会成为软件开发顺利推进的障碍。尽管在传统软件生存期管理中通过加强评审和确认,全面测试,甚至依靠维护阶段能够缓解上述问题,但不能从根本上解决这些问题。
为了解决这些问题,逐渐形成了软件系统的快速原型的概念。由于运用原型的目的和方式不同,原型可分为以下两种不同的类型:
① 废弃型:先构造一个功能简单而且质量要求不高的模型系统,针对这个模型系统反复进行分析修改,形成比较好的设计思想,据此设计出更加完整、准确、一致、可靠的最终系统。系统构造完成后,原来的模型系统就被废弃不用。
② 追加型或演化型:先构造一个功能简单而且质量要求不高的模型系统,作为最终系统的核心,然后通过不断地扩充修改,逐步追加新要求,最后发展成为最终系统。
建立快速原型进行系统的分析和构造,有以下的优点:
① 增进软件者和用户对系统服务需求的理解,使比较含糊的具有不确定性的软件需求(主要是功能)明确化。由于这种方法能在早期就明确了用户的要求,因此可防止以后由于不能满足用户要求而造成的返工,从而避免了不必要的经济损失,缩短了开发周期。
② 软件原型化方法提供了一种有力的学习手段。通过原型演示,用户可以亲身体验早期的开发过程,获得关于计算机和被开发系统的专门知识。软件开发人员也可以获得用户对系统的确切要求,学习到应用范围的专业知识。
③ 使用原型化方法,可以容易地确定系统的性能,确认各项主要系统服务的可应用性,确认系统设计的可行性,确认系统作为产品的结果。因而它可以作为理解和确认软件需求规格说明的工具。
④ 软件原型的最终版本,有的可以原封不动地成为产品,有的略加修改就可以成为最终系统的一个组成部分,这样有利于建成最终系统。
【2-12】原型的开发和使用过程叫做原型生存期。下图是原型生存期的模型及其细化。
① 快速分析 :在分析者和用户的紧密配合下,快速确定软件系统的基本要求。
② 构造原型 :根据基本规格说明,尽快实现一个可运行的原型系统。
③ 运行和评价原型 :用户试用原型,考核评价原型的特性。纠正过去交互中的误解和分析中的错误,增补新的要求,提出全面的修改意见。
④ 修正和改进 :根据修改意见进行修改。如果用修改原型的过程代替快速分析,就形成了原型开发的迭代过程。在一次次的迭代过程中不断将原型完善,以接近系统的最终要求。
⑤ 判定原型完成 :经过修改或改进的原型,达到参与者一致认可,则原型开发的迭代过程可以结束。为此,应判断有关应用的实质是否已经掌握,判定的结果有两个不同的转向,一是继续迭代验证,一是进行详细说明。
⑥ 判断原型细部是否说明 :判断组成原型的细部是否需要严格地加以说明。
⑦ 原型细部的说明 :通过文件加以说明那些不能通过原型说明的项目。
⑧ 判定原型效果 :考察新加入的需求信息和细部说明信息,看其对模型有什么影响? 是否会影响模块的有效性? 如果模型受到影响,则要进行修正和改进。
⑨ 整理原型和提供文档
快速原型方法的提出使得传统的软件生存期在思想方法上受到了影响。如果只是在局部运用原型化方法,若将原型开发过程用于软件生存期的某一个阶段内,那么传统的软件生存期依然不变,只是阶段内部的软件定义或开发活动采用了新的方法。但若原型开发过程代替了传统生存期中的多个阶段,则软件开发过程就成为一种新的形式。

图(a)表示了使用原型方法的软件生存期模型。原型开发过程处于核心,表示可在生存期的任何阶段中引入原型开发过程,也可合并若干阶段,用原型开发过程代替。图(b)详细描述了在各个阶段可能引入原型开发过程的软件开发过程。其中在原型开发过程的最后加上了一个“是否构造新原型”的判断,这是针对在系统开发的过程中有可能为不同的目的而要使用多个原型的情况而设。
① 辅助或代替分析阶段 :在分析阶段利用快速原型方法可以得到良好的需求规格说明。在整体上仍然采用传统的模式,但使用原型化方法来补充和完善需求说明以达到一致、准确、完整、无多义性地反映用户要求,从而代替了传统的仅由复审和确认来提高需求规格说明质量的方法。并能在早期克服潜在的误解、遗漏和错误,尽量不让潜在的问题遗留到开发的后期,减少将来维护的代价。
② 辅助设计阶段 :在设计阶段引入原型,可根据需求分析得到的规格说明进行快速分析,得到实现方案后立即构造原型,通过运行,考察设计方案的可行性与合理性。在这个阶段引入原型,可以迅速得到完善的设计规格说明。原型可能成为设计的总体框架,也可能成为最终设计的一部分或补充的设计文档。
③ 代替分析与设计阶段 :这时不再遵循传统的严格按阶段进行软件开发的要求,而是把原型方法直接应用到软件开发的整体过程。在实施原型开发的过程中,不再考虑完善的需求说明,把分析、定义和设计交织在一起,通过原型的构造、评价与改进的迭代过程,逐步向最终系统的全面要求靠近。由于在分析的同时也考虑了设计与实现的要求,能更有效地确定系统的需求和设计规格说明。
④ 代替分析、设计和实现阶段 :在软件开发环境的支持下,通过原型生存期的反复迭代,直接得到软件的程序,交付系统测试。这属于进化型的原型开发,由初始的基本需求得到最初的原型开始,一直进化到软件的整体系统,并满足用户的一切可能的要求。
⑤ 代替全部定义与开发阶段 :这是典型的进化型原型开发方法。完全摆脱了传统的软件生存期模式,通过反复的原型迭代过程,直接得到最终的软件产品。系统测试作为原型评价工作的一部分,融入原型的开发过程。
【2-13】软件需求规格说明是分析任务的最终产物,通过建立完整的信息描述、详细的功能和行为描述、性能需求和设计约束的说明、合适的验收标准,给出对目标软件的各种需求。
软件需求规格说明的框架如下:
Ⅰ. 引言 A.系统参考文献 B.整体描述 C.软件项目约束
Ⅱ. 信息描述 A.信息内容表示 B.信息流表示 ⅰ数据流 ⅱ控制流
Ⅲ. 功能描述 A.功能划分 B.功能描述 ⅰ处理说明 ⅱ限制∕局限 ⅲ 性能需求
ⅳ 设计约束 ⅴ 支撑图 C.控制描述 ⅰ控制规格说明 ⅱ 设计约束
Ⅳ. 行为描述 A.系统状态 B.事件和响应
Ⅴ. 检验标准 A.性能范围 B.测试种类 C.期望的软件响应 D.特殊的考虑
Ⅵ. 参考书目
Ⅶ. 附录
【2-14】不能接受的3条意见是 (5)、(6)、(10)。人机交互界面首先考虑的是用户如何使用起来方便,与编程习惯、设计技巧无关。此外,屏幕上信息应很清晰易懂,安全保密与屏幕显示无关。

软件设计
例题分析

【例1】从下列有关系统结构图的叙述中选出正确的叙述。
(1) 系统结构图中反映的是程序中数据流的情况。
(2) 系统结构图是精确表达程序结构的图形表示法。因此,有时也可将系统结构当作程序流程图使用。
(3) 一个模块的多个下属模块在系统结构图中所处的左右位置是无关紧要的。
(4) 在系统结构图中,上级模块与其下属模块之间的调用关系用有向线段表示。这时,使用斜的线段和水平、垂直的线段具有相同的含义。
答案: (4)
分析:系统结构图反映的是系统中模块的调用关系和层次关系,谁调用谁,有一个先后次序(时序)关系。所以系统结构图既不同于数据流图,也不同与程序流程图。数据流图仅描述数据在系统中如何流动,如何处理和存储,它不考虑时序关系。图中的有向线段表示了数据流。程序流程图描述程序中控制流的情况,即程序中处理的执行顺序和执行序列所依赖的条件,图中的有向线段(流线)表示的是控制流,从一个处理走到下一个处理。但在系统结构图中的有向线段表示调用时程序的控制从调用模块移到被调用模块,并隐含了当调用结束时控制将交回给调用模块。
如果一个模块有多个下属模块,这些下属模块的左右位置可能与它们的调用次序有关。例如,在用结构化设计方法依据数据流图建立起来的变换型系统结构图中,主模块的所有下属模块按逻辑输入、中心变换、逻辑输出的次序自左向右一字排开,左右位置不是无关紧要的。所以只有最后的一个叙述是正确的。
【例2】软件的开发工作经过需求分析阶段,进入( A )以后,就开始着手解决“怎么做”的问题。常用的软件设计方法有( B )、( C )、( D )和( E )等方法。
供选择的答案:
A B.① 程序设计 ② 设计阶段 ③ 总体设计 ④ 定义阶段
⑤ SD方法 ⑥ SP方法
C. ① Jackson方法 ② 瀑布法 ③ 快速原型法 ④ 回溯法
D E. ① LCP(Wanier)方法 ② 递归法 ③ Parnas方法
④ 自下而上修正 ⑤ 逐步求精法 ⑥ 检测校正法
答案:A. ②, B. ⑤, C. ①, D. ①, E. ③。其中,D与E的答案可互换。
分析:进入设计阶段之后,就开始着手解决“怎么做”的问题。一般把设计阶段的工作分成两步:即概要设计和详细设计。在概要设计阶段应着重解决实现需求的程序模块划分问题,在详细设计阶段则要决定每个模块的具体算法。
常见的软件概要设计方法有三大类:
以数据流图为基础构造模块结构的结构化设计方法(SD);
以数据结构为基础构造模块结构的Jackson方法和LCP(Wanier)逻辑构造方法;
以对象、类、继承和通信为基础的面向对象设计方法(OOD)。
此外,以信息隐蔽为原则的Parnas方法虽然没有给出系统化的设计方法,但它提出了一组原则,要求预先估计未来生存周期中可能发生的种种情况,并采取相应措施以提高软件系统的可维护性和可靠性。
这里对面向数据结构的Jackson方法和LCP方法再多说几句。
Jackson方法是一种典型的面向面向数据结构开发软件的方法。它的基本思想是首先根据实际问题,给出处理问题所需要和产生的数据结构,一旦搞清了问题的输入∕输出数据结构,就可以以简单的方式直接导出程序的处理结构,然后应用Jackson的描述符号,将这个处理结构转换为程序的过程性描述。
LCP方法是另一种面向数据结构的方法,它也要先给出用Wanier图表示的处理问题所需要和产生的数据结构,再在Wanier图上直接将数据结构转换为加工处理的形式化表示,最后生成描述加工过程的伪代码,进行验证和优化。
【例3】请将下述有关模块独立性的各种模块之间的耦合,按其耦合度从低到高排列起来。
① 内容耦合 ② 控制耦合 ③ 非直接耦合 ④ 标记耦合
⑤ 数据耦合 ⑥ 外部耦合 ⑦ 公共耦合
答案:③、⑤、④、②、⑥、⑦、①
分析:参看“内容提要”中有关模块独立性的介绍。
【例4】请将下述有关模块独立性的各种模块内聚,按其内聚度(强度)从高到低排列起来。
① 巧合内聚 ② 时间内聚 ③ 功能内聚 ④ 通信内聚
⑤ 逻辑内聚 ⑥ 信息内聚 ⑦ 过程内聚
答案: ③、⑥、④、⑦、②、⑤、①
分析:在状态―迁移图中,由一个状态和一个事件所确定的下一状态可能会有多个。实际会迁移到哪一个状态,是由更详细的内部状态和更详细的事件信息来决定的,此时在状态―迁移图中可能需要使用加进判断框和处理框的记法。状态―迁移图的优点:第一,状态之间的关系能够直观地捕捉到,这样用眼睛就能看到是否所有可能的状态迁移都已纳入图中,是否存在不必要的状态等。第二,由于状态―迁移图的单纯性,能够机械地分析许多情况,可很容易地建立分析工具。
【例5】在结构化分析方法中,用实体―关系图表达系统中的对象及其关系。在实体―关系图中,表达对象的实例之间的关联有三种类型: 一对一联系、( )联系、多对多联系。
供选择的答案:
A. 多对一 B. 一对多
答案:B
分析:使用实体―关系图,可以建立系统中各个数据对象及对象之间的关系。对象的实例间的关联称为“基数”,共有3种类型的基数:一对一,一对多,多对多。它反映了现实世界中实体之间的联系,多对一的情况可以归入一对多的关联中去。
【例6】 软件需求分析的任务不应包括( A )。进行需求分析可使用多种工具,但( B )是不适用的。在需求分析中,分析员要从用户那里解决的最重要的问题是( C )。需求规格说明书的内容不应当包括( D )。该文档在软件开发中具有重要的作用,但其作用不应当包括( E )。
供选择的答案:
A. ① 问题分析 ② 信息域分析 ③ 结构化程序设计 ④ 确定逻辑模型
B. ① 数据流图 ② 判定表 ③ PAD图 ④ 数据词典
C. ① 要让软件做什么 ② 要给该软件提供哪些信息
③ 要求软件工作效率如何 ④ 要让软件具有什么样的结构
D. ① 对重要功能的描述 ② 对算法的详细过程性描述
③ 软件确认准则 ④ 软件的性能
E. ① 软件设计的依据 ② 用户和开发人员对软件要“做什么”的共同理解
③ 软件验收的依据 ④ 软件可行性分析的依据
答案:A. ③ B. ③ C. ① D. ② E. ④
分析:软件需求分析的任务是通过与用户的合作,了解用户对待开发系统的要求;根据对用户要求的系统所在的信息域的调查、分析,确定系统的逻辑模型;并对求解的问题做适当的分解,使之适合于计算机求解。需求分析的结果是软件需求规格说明书。
结构化程序设计是在详细设计和编码阶段所采用的技术,而不是需求分析阶段要采用的技术。在需求分析阶段,分析人员可以用数据流图描述系统的数据流的变换和流向,用数据词典定义在数据流图中出现的数据流、数据文件、加工或处理,用判定表表示复杂条件和动作组合的情况。但PAD图是在详细设计阶段使用的描述加工逻辑的工具,不适用于需求分析。此外,软件需求分析阶段只确定软件系统要“做什么”,完成对重要功能、性能、确认准则的描述,至于“怎么做”由后续的设计阶段完成,对算法的详细过程性描述也是在设计阶段给出。软件可行性分析应在需求分析之前,所以需求分析规格说明不能成为可行性分析的依据。
【例7】原型化方法是用户和软件开发人员之间进行的一种交互过程,适用于( A )系统。它从用户界面的开发入手,首先形成( B ),用户( C ),并就( D )提出意见,它是一种( E )型的设计过程。
供选择的答案:
A. ① 需求不确定性高的 ② 需求确定的 ③ 管理信息 ④ 决策支持
B. ① 用户界面使用手册 ② 用户界面需求分析说明书
③ 系统界面原型 ④ 完善的用户界面
C. ① 改进用户界面的设计 ② 阅读文档资料
③ 模拟用户界面的运行 ④ 运行用户界面原型
D. ① 同意什么和不同意什么 ② 使用和不使用哪一种编程语言
③ 程序的结构 ④ 执行速度是否满足要求
E. ① 自外向内 ② 自顶向下 ③ 自内向外 ④ 自底向上
答案:A. ①, B. ③, C. ④, D. ①, E. ①
分析:通常,原型是指模拟某种产品的原始模型。在软件开发中,原型是软件的一个早期可运行的版本,它反映最终系统的部分重要特性。
使用原型的原型化方法特别适用于需求不确定性较高的软件系统的开发。它的基本思想是根据用户给出的基本需求,通过快速实现构造出一个小型的可执行的模型,满足用户的基本要求,这就是系统界面原型。让用户计算机上实际运行这个用户界面原型,在试用的过程中得到亲身感受和受到启发,做出反应和评价,提出同意什么和不同意什么。然后开发者根据用户的意见对原型加以改进。随着不断试验、纠错、使用、评价和修改,获得新的原型版本,如此周而复始,逐步减少分析和通信中的误解,弥补不足之处,进一步确定各种需求细节,适应需求的变更,从而提高了最终产品的质量。它是一种自外向内型的设计过程。

习题

【3-1】逐步求精、分层过程与抽象等概念之间的相互关系如何?
【3-2】完成良好的软件设计应遵循哪些原则?
【3-3】如何理解模块独立性?用什么指标来衡量模块独立性?
【3-4】模块独立性与信息隐蔽(反映模块化有效程度的属性)有何关系?
【3-5】模块的内聚性程度与该模块在分层结构中的位置有关系吗?说明你的论据。
【3-6】耦合性的概念和软件的可移植性有什么关系?请举例说明你的论述。
【3-7】从供选择的答案中选出正确的答案填入下列叙述中的( ) 内。
模块内聚性用于衡量模块内部各成份之间彼此结合的紧密程度。
(1) 一组语句在程序中多处出现,为了节省内存空间把这些语句放在一个模块中,该模块的内聚性是( A )的。
(2) 将几个逻辑上相似的成分放在同一个模块中,通过模块入口处的一个判断决定执行哪一个功能。该模块的内聚性是( B )的。
(3) 模块中所有成分引用共同的数据,该模块的内聚性是( C )的。
(4) 模块内的某成份的输出是另一些成份的输入,该模块的内聚性是( D )的。
(5) 模块中所有成份结合起来完全一项任务,该模块的内聚性是( E )的。它具有简明的外部界面,由它构成的软件易于理解、测试和维护。
供选择的答案:
A E: ① 功能内聚 ② 信息内聚 ③ 通信内聚 ④ 过程内聚
⑤ 巧合内聚 ⑥ 时间内聚 ⑦ 逻辑内聚
【3-8】从供选择的答案中选出正确的答案填入下面的( )中。
块间联系和块内联系是评价程序模块结构质量的重要标准。联系的方式、共用信息的作用、共用信息的数量和接口的( A )等因素决定了块间联系的大小。在块内联系中,( B )的块内联系最强。
SD方法的总的原则是使每个模块执行( C )功能,模块间传送( D )参数,模块通过( E )语句调用其它模块,而且模块间传送的参数应尽量( F )。
此外,SD方法还提出了判定的作用范围和模块的控制范围等概念。SD方法认为,( G )应该是( H )的子集。
供选择的答案:
A: ① 友好性 ② 健壮性 ③ 简单性 ④ 安全性
B: ① 巧合内聚 ② 功能内聚 ③ 通信内聚 ④ 信息内聚
C: ① 一个 ② 多个
D: ① 数据型 ② 控制型 ③ 混合型
E: ① 直接引用 ② 标准调用 ③ 中断 ④ 宏调用
F: ① 少 ② 多
G H:① 作用范围 ② 控制范围
【3-9】从供选择的答案中选出应该填入下列关于软件设计的叙述的( )内的正确答案。
在众多的设计方法中,SD方法是最受人注意的,也是最广泛应用的一种,这种方法可以同分析阶段的( A )方法及编程阶段的( B )方法前后衔接,SD方法是考虑如何建立一个结构良好的程序结构,它提出了评价模块结构质量的两个具体标准——块间联系和块内联系。SD方法的最终目标是( C ),用于表示模块间调用关系的图叫( D )。
另一种比较著名的设计方法是以信息隐蔽为原则划分模块,这种方法叫( E )方法。
供选择的答案:
A B:① Jackson ② SA ③ SC ④ Parnas ⑤ SP
C: ① 块间联系大,块内联系大 ② 块间联系大,块内联系小
③ 块间联系小,块内联系大 ④ 块间联系小,块内联系小
D: ① PAD ② HCP ③ SC ④ SADT ⑤ HIPO ⑥ NS
E: ① Jackson ② Parnas ③ Turing ④ Wirth ⑤ Dijkstra
【3-10】递归模块(即自己调用自己的模块)的概念如何能够与本章所介绍的设计原理与方法相适应?
【3-11】举例说明你对概要设计与详细设计的理解。有不需要概要设计的情况吗?
【3-12】软件详细设计工具可分为三类,即:图示工具、设计语言和表格工具。图示工具中,( A )简单而应用广泛、( B )表示法中,每一个处理过程用一个盒子表示,盒子可以嵌套。( C )可以纵横延伸,图形的空间效果好。
( D ) 是一种设计和描述程序的语言,它是一种面向( E )的语言。
供选择的答案:
A C:① NS图 ② 流程图 ③ HIPO图 ④ PAD图
D: ① C ② PDL ③ RPOLOG ④ PASCAL
E: ① 人 ② 机器 ③ 数据结构 ④ 对象
【3-13】如何用PDL语言来实施逐步求精的设计原理?
【3-14】从供选择的答案中选出应该填入下列关于软件设计的叙述的( )内的正确答案。
在完成软件概要设计,并编写出相关文档之后,应当组织对概要设计工作的评审。评审的内容包括:
分析该软件的系统结构、子系统结构,确认该软件设计是否覆盖了所有已确定的软件需求,软件每一成份是否可( A )到某一项需求。分析软件各部分之间的联系,确认该软件的内部接口与外部接口是否已经明确定义。模块是否满足( B )和( C )的要求。模块( D )是否在其( E )之内。
供选择的答案
A: ① 覆盖 ② 演化 ③ 追溯 ④ 等同 ⑤ 连接
B: ① 多功能 ② 高内聚 ③ 高耦合 ④ 高效率 ⑤ 可读性
C: ① 多入口 ② 低内聚 ③ 低耦合 ④ 低复杂度 ⑤ 低强度
D E:① 作用范围 ② 高内聚 ③ 低内聚 ④ 取值范围 ⑤ 控制范围

习题解答

【3-1】“自顶向下,逐步求精”是Niklaus Wirth提出的设计策略:即将软件的体系结构按自顶向下方式,对各个层次的过程细节和数据细节逐层细化,直到用程序设计语言的语句能够实现为止,从而最后确立整个的体系结构。
这样的结构实际就是一个模块的分层结构,即分层的过程。在实施时,采用抽象化的方法,自顶向下,给出不同的抽象层次。在最高的抽象层次上,可以使用问题所处环境的语言概括地描述问题的解法。而在较低的抽象层次上,则采用过程化的方法。在描述问题的解法时,我们可以配合使用面向问题的术语和面向现实的术语。但最后在最低的抽象层次上,我们应使用能够直接实现的方式来描述这个解法。
【3-2】软件设计既是过程又是模型。设计过程是一系列迭代的步骤,使设计人员能够描述被开发软件的方方面面。设计模型体现了自顶向下、逐步细化的思想,首先构造事物的整体,再逐步细化,引导人们构造各种细节。为了给软件设计人员提供一些指导,1995年Davis提出了一系列软件设计的原则如下,其中有些修改和补充:
·设计过程不应受“隧道视野”的限制。一位好的设计者应当考虑一些替代的手段。根据问题的要求,可以用基本的设计概念,如抽象、逐步求精、模块化、软件体系结构、控制层次、结构分解、数据结构、软件过程、信息隐蔽等,来决定完成工作的资源。
·设计应能追溯到分析模型。由于设计模型中每一单个成份常常可追溯到多个需求上,因此有必要对设计模型如何满足需求进行追踪。
·设计不应当从头做起。系统是使用一系列设计模式构造起来的,很多模式很可能以前就遇到过。这些模式通常被称为可复用的设计构件。可以使用它们代替那些从头开始做的方法。时间短暂而资源有限!设计时间应当投入到表示真正的新思想和集成那些已有的设计模式上去。
·设计应当缩短软件和现实世界中问题的“智力差距”,就是说,软件设计的结果应尽可能模拟问题领域的结构。
·设计应具有一致性和集成性。如果一个设计从整体上看像是一个人完成的,那它就是一致的。在设计工作开始之前,设计小组应当定义风格和格式的规则,如果仔细定义了设计构件之间的接口,则该设计就是集成的。
·使用上述的基本的设计概念,将设计构造得便于将来的修改。
·应将设计构造得即使遇到异常的数据、事件或操作条件,也能平滑地、轻松地降级。设计良好的计算机程序应当永不“彻底停工”,它应能适应异常的条件,并且当它必须中止处理时也能以从容的方式结束。
·设计不是编码,编码也不是设计。即使在建立程序构件的详细的过程设计时,设计模型的抽象级别也比源代码要高。在编码级别上作出的唯一设计决策是描述如何将过程性设计转换为程序代码的小的实现细节。
·在开始着手设计时就应当能够评估质量,而不是在事情完成之后。利用上述的基本的设计概念和已有的设计方法,可以帮助设计者评估质量。
·应当坚持设计评审以减少概念上(语义上)的错误。有时人们在设计评审时倾向于注重细节,只见树木不见森林。在关注设计模型的语法之前,设计者应能确保设计的主要概念上的成份(的遗漏、含糊、不一致)都已检查过。
【3-3】如果两个模块互相独立,那么对其中一个模块进行编码、测试或修改时可以完全不考虑另一个模块对它的影响。因此,用模块独立性作为衡量模块结构是否容易编码、容易测试、容易修改的标准是合适的。但是,在一个系统的模块结构中没有哪两个模块可以完全独立,所以,要力争模块之间尽量独立,以得到一个质量良好的模块结构。
一般采用两个准则度量模块独立性。即模块间的耦合和模块的内聚。模块间的耦合是模块之间的相对独立性(互相连接的紧密程度)的度量。模块之间的连接越紧密,联系越多,耦合性就越高,而其模块独立性就越弱。内聚是模块功能强度(一个模块内部各个成份彼此结合的紧密程度)的度量。一个模块内部各个成份之间的联系越紧密,则它的内聚性就越高,相对地,它与其它模块之间的耦合性就会减低,而模块独立性就越强。因此,模块独立性比较强的模块应是高内聚低耦合的模块。
一般模块之间可能的连接方式有七种,构成耦合性的七种类型。它们之间的关系为

低耦合的情形有非直接耦合、数据耦合和标记耦合,它们都是比较好的模块间的连接。特点是模块间的接口简单、规范。中度耦合的情形有控制耦合,它通过参数表传递控制参数。相对高的耦合情形有外部耦合和公共耦合,它们都是通过全局数据传递模块间的信息,不是说它们一定“坏”,但一定要注意使用这类耦合可能产生的后果,特别要防范这种后果。
一般模块的内聚性分为七种类型,它们的关系如下图所示。

在上面的关系中可以看到,位于高端的几种内聚类型最好,位于中段的几种内聚类型是可以接受的,但位于低端的内聚类型很不好,一般不能使用。因此,人们总是希望一个模块的内聚类型向高的方向靠。模块的内聚在系统的模块化设计中是一个关键的因素。
内聚和耦合是相互关联的。在程序结构中各模块的内聚程度越高,模块间的耦合程度就越低。但这也不是绝对的。我们的目标是力求增加模块的内聚,尽量减少模块间的耦合,但增加内聚比减少耦合更重要,应当把更多的注意力集中到提高模块的内聚程度上来。
【3-4】所谓“模块独立性”是指软件系统中每个模块只涉及软件要求的具体的子功能,而和软件系统中其它的模块的接口是简单的。所谓的“信息隐蔽”是指每个模块的实现细节对于其它模块来说是隐蔽的。也就是说,模块中所包含的信息(包括数据和过程)不允许其它不需要这些信息的模块使用。
如果软件系统做到了信息隐蔽,即定义和实施了对模块的过程细节和局部数据结构的存取限制,那么这些模块相互间的接口就是简单的。这组模块的独立性就比较强。事实上,衡量模块独立性的一个准则就是模块内聚,达到信息隐蔽的模块是信息内聚模块,它是高内聚情形,模块独立性当然很强了。
一个对象的抽象数据类型,就是信息隐蔽的示例。例如,对于栈stack, 可以定义它的操作makenull(置空栈)、push(进栈)、pop(退栈)、gettop(取栈顶)和empty(判栈空)。这些操作所依赖的数据结构是什么样的? 它们是如何实现的? 都被封装在其实现模块中。软件的其它部分可以直接使用这些操作,不必关心它的实现细节。一旦实现栈stack的模块里内部过程或局部数据结构发生改变,只要它相关操作的调用形式不变, 则软件中其它所有使用这个栈stack的部分都可以不修改。 这样的模块结构具有很强的模块独立性。
【3-5】模块的内聚性与该模块在分层模块结构中的位置无关。事实上,一个好的模块化的程序系统,它所有的模块可以都是功能内聚的,即每一个模块就只干了一件事。用结构化设计方法建立起来的模块结构中的每一个模块都符合这个要求。把讨论范围再拓宽点,在纯面向对象范型的软件系统中,整个系统看作是一个类,它的子类可以看作是系统的子系统或高层模块,它们还可以有子类,……,这就形成一个类的层次结构。类的构造可以看成是一个抽象数据类型,实际上是信息内聚的。所以整个系统中从上到下,所有模块(对象类)都是信息内聚的模块。
【3-6】所谓“耦合性”是指模块之间联系的紧密程度的一种度量,而软件的“可移植性”是指将一个软件系统从一个计算机系统或环境移植到另一个计算机系统或环境中运行时所需工作量的大小。可移植性是用一组子特性,包括简明性、模块独立性、通用性、可扩充性、硬件独立性和软件系统独立性等,来衡量的。如果一个软件具有可移植性,它必然耦合性低,这样模块独立性要强。例如,有一个图形处理软件,它应具有二维几何图形处理、三维几何图形处理、图形显示、外设控制、数据库管理、用户界面控制、设计分析等模块。如果这些模块之间都是通过参数表来传递信息,那么它们之间的的耦合就是数据耦合或标记耦合等,都是低耦合。将来如果想要把它们移植到另一个外部环境中,这些模块容易修改(功能内聚),且接口清晰,修改可局部化。反言之,如果这些模块都是功能内聚或信息内聚的模块,模块之间的耦合都是低耦合,也对可移植性有促进。但不能讲具有低耦合性模块结构的软件一定具有可移植性,因为是否具有可移植性还有其它因素的影响。
【3-7】 A. ⑤, B. ⑦, C. ③, D. ④, E. ①
【3-8】 A. ③, B. ②, C. ①, D. ①, E. ②, F. ①, G. ①, H. ②
模块之间的耦合(块间联系)和模块的内聚(块内联系)是评价程序模块结构质量的重要标准。联系的方式、共用信息的作用、共用信息的数量和接口的简单性等因素决定了块间联系的大小。在块内联系中,以功能内聚模块的块内联系最强。
SD方法的总的原则是使每个模块只做一件事,就是说,只执行一个功能。模块之间尽可能传送简单的数据型参数。模块要通过标准调用语句调用其它模块,不要直接引用另一个模块内部的数据。同时模块之间传送的参数应尽量少。此外,SD方法还提出了判定的作用范围和模块的控制范围等概念。SD方法认为,模块的作用范围应该是其控制范围的子集。
【3-9】 A. ②, B. ⑤, C. ③, D. ③, E. ②
结构化设计方法(SD)是一种应用非常广泛的软件设计方法,它以结构化分析方法(SA)得到的数据流图和数据词典为依据,建立软件的模块结构,然后对每一个模块用结构化程序设计(SP)方法设计它的内部逻辑。这几种方法是前后衔接的。用SD方法建立的模块结构用模块间的耦合(块间联系)和模块的内聚(块内联系)来度量,要求一个好的模块结构应满足高内聚,低耦合。在SD方法中表示模块间调用关系的图叫做系统结构图(SC)。
另一种著名的设计方法是以信息隐蔽为原则划分模块,这种方法是Parnas提出来的,叫做Parnas方法。
【3-10】递归过程在求解复杂的大型问题时非常有效。常用的求解问题的方法,一种叫做“分而治之”的策略和“回溯”的策略,都可以用递归方法来解决。所谓“分而治之”的方法即是把大而复杂的问题化为规模稍小的子问题,用同样方法求解。如果分解后的子问题能够直接解决,就直接解出,然后再回推得到原来问题的解。所谓“回溯”方法就是如果一个大的问题在求解过程中从某一步出发有可选的多种解决方案,先选择一种解决方案,试探求解。如果求解失败,撤消原来的选择,再选一种解决方案,试探求解,……。如果用某一方案求解成功,则退回上一步并报告这一步求解成功;如果所有可选方案都试过,都求解失败,则退回上一步并报告求解失败。
软件设计过程中的“自顶向下,逐层分解”的做法与上述求解问题的策略是一致的。如果分解出来的子问题(子功能、子模块)相互独立性比较强,这种分解可以降低模块的复杂性,做到模块化。所以,只要分解出来的子问题与原来问题满足递归的情况,用递归方法建立模块结构也是可行的。
【3-11】软件设计是一个把软件需求变换成软件表示的过程。最初这种表示只是描绘出软件的总的框架,然后进一步细化,在此框架中填入细节,把它加工成在程序细节上非常接近于源程序的软件表示。正因为如此,所以从工程管理的角度来看,软件设计分两步完成。首先做概要设计,将软件需求转化为数据结构和软件的系统结构。然后是详细设计,即过程设计。通过对结构表示进行细化,得到软件的详细的数据结构和算法。
由于概要设计建立起整个系统的体系结构框架,并给出了系统中的全局数据结构和数据库接口,人机接口,与其它硬、软件的接口。此外还从系统全局的角度,考虑处理方式、运行方式、容错方式、以及系统维护等方面的问题,并给出了度量和评价软件质量的方法,所以它奠定了整个系统实现的基础。没有概要设计,直接考虑程序设计,就不能从全局把握软件系统的结构和质量,实现活动处于一种无序状态,程序结构划分不合理,导致系统处于一种不稳定的状态,稍一做改动就会失败。所以,不能没有概要设计。
【3-12】A. ②, B. ①, C. ④, D. ②, E. ①
【3-13】使用PDL语言,可以做到逐步求精:从比较概括和抽象的PDL程序起,逐步写出更详细的更精确的描述。下面举一个例子。
  PROCEDURE spellcheck IS 查找错拼的单词
BEGIN
split document into single words 把整个文档分离成单词
lood up words in dictionary 在字典中查这些单词
display words which are not in dictionary 显示字典中查不到的单词
create a new dictionary 造一新字典
END spellcheck
这个例子只是搭起一个处理问题的框架。为进一步表明查找拼错的单词的4个步骤如何实现,可以对它每一步进行细化:
PROCEDURE spellcheck
BEGIN
–* split document into single words
LOOP get next word
add word to word list in sortorder
EXIT WHEN all words processed
END LOOP
–* look up words in dictionary
LOOP get word from word list
IF word not in dictionary THEN
–* display words not in dictionary
display word,prompt on user terminal
IF user response says word OK THEN
add word to good word list
ELSE
add word to bad word list
ENDIF
ENDIF
EXIT WHEN all words processed
END LOOP
–* create a new words dictionary
dictionary:=merge dictionary and good word list
END spellcheck
【3-14】A. ③, B. ②, C. ③, D. ①, E. ⑤

程序编码
例题分析

【例1】下图是使用Basic语言编写的一个打印A,B,C三数中最小者的程序的流程图。其中出现了6个GOTO语句,一个向前,5个向后,程序可读性很差。
if ( A < B ) goto 120;
if ( B < C ) goto 110;
100 print C;
goto 140;
110 print B;
goto 140;
120 if ( A < C ) goto 130;
goto 100;
130 print A;
140
试利用基本控制结构,将程序中的GOTO语句消去。
答案:使用if - then - else结构化构造,则上述程序段可改成如下形式。
if ( A < B and A < C ) then
print A
else if ( A >= B and B < C ) then
print B
else
print C;
分析:理清程序流程图中每一条执行路径,适当利用复合的条件测试于判断语句,对每一个最终的打印处理,保留一个分支进入它。这样可以消除众多的GOTO语句,甚至可以消除嵌套的判断语句结构。这种程序结构清晰,可读性好。
【例2】设在闭区间 [a…b] 上函数F(X) 有唯一的一个零点,如下图所示。下面给出一个用C语言写出的程序段,用二分法求方程F(X)=0 在区间 [a…b] 中的根。程序段中X0、X1 是当前求根区间 [X0…X1] 的下上界,Xm是该区间的中点,eps 是一个给定的很小正数,用于迭代收敛的判断。在程序中采取了用goto语句和标号finish控制在循环中途转出循环。
〖程序〗 F0 = F (a); F1 = F (b);
if ( F0 * F1 <= 0 ) {
X0 = a; X1 = b;
for ( i = 1; i <= n; i++) {
Xm = (X0 + X1) / 2; Fm = F(Xm);
if ( abs (Fm) < eps || abs (X1-X0) < eps )
goto finish;
if ( F0 * Fm > 0 )
{ X0 = Xm; F0 = Fm; }
else
X1 = X;
}
finish: printf (“\n The root of this equation is %d\n”,Xm );
}
这类循环结构出现了两个循环出口。一个是for循环的正常出口:当循环控制变量i超出了循环终值n时退出循环;另一个是for循环的非正常出口:当某种条件满足时,从循环中间某处转出循环,执行循环后面的语句。它不满足结构化的要求。
试利用结构化程序设计要求的几种基本控制结构,消除其中的goto语句,使得每一个部分都是单入口单出口。
答案:利用一个布尔变量finished(该变量的初值为false),当循环中求到了要求的结果时,将此变量的值改变为true,表示循环应结束,while 循环测试到finished为true,就自动退出循环,执行后续的语句。
〖程序〗 F0 = F(a); F1 = F(b);
if ( F0 * F1 <= 0 ) {
X0 = a; X1 = b; i = 1; finished = 0; /* 设置控制循环结束的布尔变量 /
while ( i <= n && finished == 0 ) { /
单入口单出口 */
Xm = (X0 + X1) / 2; Fm = F(Xm);
if ( abs(Fm) < eps | | abs(X1-X0) < eps ) finished = 1;
if ( finished == 0) {
if ( F0 * Fm > 0 )
{ X0 = Xm; F0 = Fm; }
else
X1 = X;
}
}
}
分析:此程序段中各种结构均为单入口与单出口,且没有goto语句,可以说它是一个结构化的程序。它的结构很简单,只有一重循环,但由于引入一个布尔变量来控制循环结束,可读性比较差。在只有一重循环的情形,差的程度还不很明显,在多重循环的情形,引入多个布尔变量,可读性就很差了。理解程序的时间差几倍到几十倍不止。因此,对于这种单入口多出口的循环,R.S.Pressman明确说明,用goto语句可得到较好的清晰性。在C中提供了一个可直接转向循环之后的语句break,使用它可获得较好的效果。
〖程序〗 F0 = F(a); F1 = F(b); /区间两端点的函数值/
if ( F0 * F1 <= 0 ) { /区间中没有根,不做/
X0 = a; X1 = b; /设置当前求根区间的两个端点/
for ( i = 1; i <= n; i++) { /最多允许迭代n次/
Xm = (X0 + X1) / 2; Fm = F(Xm); /求中点及中点的函数值/
if ( abs(Fm) < eps | | abs(X1-X0) < eps) break; /求到, 转出循环/
if ( F0 * Fm > 0 ) /没有求到,缩小求根区间/
{ X0 = Xm; F0 = Fm; } /向右缩小区间/
else
X1 = X; /向左缩小区间/
}
}
这段程序不是结构化的程序,利用了C语言中的一个语句break,它的功能是将控制转移到它所在循环的后面第一个后续语句处。由于将转移语句与转出条件的判断直接联系在一起,可读性较好。
【例3】试说明下面的两个程序段的功能是什么?可否用另一些等效的程序段来代替它,以提高其可读性。
(1) A[I] = A[I] + A[T]; (2) for ( i = 1; i <= n; i ++ )
A[T] = A[I] - A[T]; for ( j = 1; j <= n; j ++ )
A[I] = A[I] - A[T]; V[i][j] = ( i / j ) * ( j / i );
答案:(1) 的功能是对换A[I] 与A[T] 的内容。等效的程序段可以是:
WORK = A[T]; A[T] = A[I]; A[I] = WORK;
(2) 的功能是建立一个单位矩阵V。等效的程序段可以是:
for ( i = 1; i <= n; i ++ )
for ( j = 1; j <= n; j ++ )
if ( i == j ) V[i][j] = 1;
else V[i][j] = 0;
分析:阅读这两段程序,读者可能不易看懂,有时还需用实际数据试验一下。对于(1),如果我们给A[I] 赋值3,给A[T] 赋值5,在运算后发现A[I] 中变成了5,A[T] 中变成了3。这段程序就是交换A[I] 和A[T] 中的内容。目的是为了节省一个工作单元。如果改一下:
WORK = A[T]; A[T] = A[I]; A[I] = WORK;
就能让读者一目了然。
对于(2),除法运算(/)在除数和被除数都是整型量时,其结果只取整数部分,而得到整型量。因此, i / j为0, 当i < j时
j / i为0, 当j < i时
得到的数组 V[i][j] = ( i/j ) * ( j/i ) = 0,当i ≠ j时
( i/j ) * ( j/i ) = 1,当i = j时
这样得到的结果V是一个单位矩阵。如果直截了当地说明作者的意图:
if ( i == j ) V[i][j] = 1; else V[i][j] = 0;

让读者可以很容易地理解。所以,在程序设计时,应当首先考虑清晰性,不要玩技巧。
【例4】有一种循环结构,叫做N+1/2循环。其流程图如下所示。这种控制结构不属于基本控制结构:它既不是先判断型循环,又不是后判断型循环。试修改此流程图,将它改为用基本控制结构表示的等效的流程图。
答案:等效的控制流程图如下图中 (a) 所示。
分析:先判断型循环要求在进入循环体之前,先判断是否要继续执行此循环。因此,在这种控制结构的入口处应是一个判断语句。这种循环的循环体可能一次也不执行。参看图 (b)。

(a) 修改N+1/2循环 (d) 另一个方案

【例5】下面是两个程序流程图,试分别用N-S图和PAD表示之,并计算它们的McCabe复杂性度量。

答案:对应的N-S图如下。
对应PAD图如下。

McCabe复杂性度量都为3。
习题
【4-1】从下列关于模块化程序设计的叙述中选出5条正确的叙述。
① 程序设计比较方便,但比较难以维护。
② 便于由多个人分工编制大型程序。
③ 软件的功能便于扩充。
④ 程序易于理解,也便于排错。
⑤ 在主存储器能够容纳得下的前提下,应使模块尽可能大,以便减少模块的个数。
⑥ 模块之间的接口叫做数据文件。
⑦ 只要模块之间的接口关系不变,各模块内部实现细节的修改将不会影响别的模块。
⑧ 模块间的单向调用关系叫做模块的层次结构。
⑨ 模块越小,模块化的优点越明显。一般来说,模块的大小都在10行以下。
【4-2】结构化程序设计有时被错误地称为“无GOTO语句”的程序设计。请说明为什么会出现这样的说法,并讨论环绕着这个问题的一些争论。
【4-3】从下面关于程序编制的叙述中,选出三条正确的叙述。
① 在编制程序之前,首先必须仔细阅读给定的程序说明书。然后,必须如实地依照说明书编写程序。说明书中常会有含糊不清或难以理解的地方。程序员在作业时应该对这些地方作出适当的解释。
② 在着手编制程序时,重要的是采用既能使程序正确地按设计说明书进行处理,又易于出错的编写方法。
③ 在编制程序时,首先应该对程序的结构充分考虑,不要急于开始编码,而要象写软件文档那样,很好地琢磨程序具有什么样的功能,这些功能如何安排等等。
④ 考虑到以后的程序变更,为程序编写完整的说明书是一项很重要的工作。只要有了完整的程序说明书,即使程序的编写形式难以让他人看懂也没有什么关系。
⑤ 编制程序时不可缺少的条件是,程序的输入和输出数据的格式都应确定。其他各项规定都是附带的,无足轻重。
⑥ 作为一个好的程序,不仅处理速度要快,而且易读易修改等等也都是重要的条件。为了能得到这样的程序,不仅要熟悉程序设计语言的语法,还要注意采用适当的规程和单纯的表现方法,注意使整个程序的结构简洁。
【4-4】从下列叙述中选出5条符合程序设计风格指导原则的叙述。
① 嵌套的重数应加以限制。
② 尽量多使用临时变量。
③ 不滥用语言特色。
④ 不用可以省略的括号。
⑤ 使用有意义的变量名。
⑥ 应尽可能把程序编得短些。
⑦ 把常见的局部优化工作留给编译程序去做。
⑧ 注解越少越好。
⑨ 程序的格式应有助于读者理解程序。
⑩ 应尽可能多用GOTO语句。
【4-5】从供选择的答案中选出应该填入下面 ( ) 中的正确答案。
A. 允许用户建立、修改、存储正文的计算机程序是 ( )。
① BOOtstrap ② Editor ③ Loader ④ Textformatter
B. 程序语言的编译系统和解释系统相比,从用户程序的运行效率来看 ( )。
① 前者运行效率高 ② 两者大致相同
③ 后者运行效率高 ④ 不能确定
C. FORTRAN语言的源程序是 ( ) 结构。
① 块状 ② 分程序嵌套
③ 既是块状,又是嵌套 ④ 既不是块状,又不是嵌套的
D. 国际上最广泛使用的商用及行政管理语言是 ( )。
① COBOL ② BASIC ③ FORTRAN ④ PL/1
E. 国际上最流行的数值计算的程序设计语言是 ( )。
① BASIC ② ALGOL ③ FORTRAN ④ C
F. 美国国防部主持开发了高级程序设计语言Ada,在它研制开始时,经反复比较,确定以高级语言 ( ) 作为Ada研究的出发点。
① LISP ② ALGOL ③ ALGOL68 ④ PL/1
G. 在人工智能领域,目前最广泛使用的高级语言是 ( )。
① Ada ② FORTRAN ③ COBOL ④ LISP
【4-6】从供选择的答案中选出应该填入下面 ( ) 中的正确答案。
A. 汇编程序是指 ( )。
① 用汇编语言写的程序 ② 符号程序 ③ 汇编语言的处理程序
B. 为了实现递归子程序的正确调用,人们必须用 ( ) 来保存 ( ) 及有关信息。
① 堆栈 ② 线性表 ③ 队列 ④ 树
⑤ 入口点 ⑥ 返回地址 ⑦ 断点
C. UNIX操作系统是 ( ) 研制的,它是用程序语言 ( ) 书写实现的。
① Bell实验室 ② DEC公司 ③ IBM公司 ④ PASCAL
⑤ 并发PASCAL ⑥ MODULA ⑦ C
【4-7】下面给出一个求实函数方程F(x)在自变量区间 [a, b] 中的全部实根的算法。首先阅读此程序,然后
(1) 画出消去全部goto语句的结构化程序流程图。
(2) 将它改成N_S图。
(3) 计算该程序的McCabe复杂性度量。
在算法中,a与b是区间[a, b]的两端点值;eps1与eps2是用户要求的求解精度。如果区间中点的函数值的绝对值小于eps1或新的小区间的长度小于eps2,就认为这个中点为根。
float BinRoot ( float a, float b, float eps1, float eps2 ) {
float low= a, high = b, mid, fmid;
float flow = Func(low), fhigh := Func(high);
label L1, L2, L3; //标号说明,给定某些程序地址
if ( flow * fhigh > 0.0 ) { BinRoot = 0; goto L3; } //无实根
L1: mid = (low + high) / 2; fmid = Func(mid);
if ( abs ( fmid ) <= eps1 ) {
L2: BinRoot = mid; goto L3;
}
else if ( high - mid <= eps2 ) goto L2;
else if ( flow * fmid > 0.0 ) { low = mid; flow = fmid; goto L1; }
else { high = mid; goto L1 };
L3:
}
【4-8】从供选择的答案中选出适当的字句填入下面关于程序生产率的描述中的 ( ) 内。
(1) 1960年底Dijkstra提倡的 ( A ) 是一种有效的提高程序设计效率的方法。
(2) Dijkstra为了使程序结构易于理解,把基本控制结构限于顺序、( B )、( C ) 3种,应避免使用 ( D )。
(3) ( A ) 不仅提高程序设计的生产率,同时也容易进行程序的 ( E )。
供选择的答案:
A. ① 标准化程序设计 ② 模块化程序设计 ③ 多道程序设计
④ 宏语言 ⑤ 结构化程序设计 ⑥ 汇编语言
⑦ 表格处理语言
B, C. ① 分支 ② 选择 ③ 重复 ④ 计算 ⑤ 输入输出
D. ① GOTO语句 ② DO语句 ③ IF语句 ④ REPEAT语句
E. ① 设计 ② 调试 ③ 维护 ④ 编码
【4-9】用某种软件复杂性度量算法来度量不同类型的程序时,得出的度量值是否真正反映了它们的复杂性? 如果对同类型的程序进行度量,其结果是否就比较有价值?
【4-10】软件复杂性有哪几类?软件复杂性度量模型应遵循哪些基本原则?
习题解答
【4-1】正确的叙述有②、③、④、⑦、⑧。如果程序结构的模块化满足评价的标准(高内聚,低耦合),这样的结构是容易编码,容易测试,容易理解,容易修改,容易维护的。程序的功能也容易扩充。特别适合于大型程序编制时,多人分工合作,协同完成任务的情形。因为是采用自顶向下,逐层分解来划分模块结构的,所以模块之间的调用关系是分层次的模块结构,就叫做模块的层次结构。模块之间的信息传递叫做模块的接口,模块之间传递信息可以通过参数表、全局变量或全局数据结构、数据文件、专门的通信模块,不是专指数据文件。划分模块时,模块大小要适中。模块太大,控制路径数目多、涉及的范围广、变量的数目多、总体复杂性高,可理解性、可修改性、可靠性就会变差。模块太小,模块个数增大,调用的系统开销就会增大。所以要有一个权衡。
【4-2】早在1963年,针对当时流行的ALGOL语言,Peter Naur指出,在程序中大量地,没有节制地使用GOTO语句,会使程序结构变得非常混乱。但是很多人还不太注意这一问题。以致许多人写出来的程序仍然是纷乱如麻的。
1965年,E.W.Dijkstra在一次会议上提出,应当把GOTO语句从高级语言中取消。并指出,程序的质量与程序中包含的GOTO语句的数量成反比。在这种思想的影响下,当时新开发的几种高级程序设计语言,例如LISP、ISWIM、BLISS等,都把GOTO语句取消了。
1966年,Bohm与Jacopini证明了任何单入口单出口的没有“死循环”的程序都能由三种最基本的控制结构构造出来。这三种基本控制结构就是“顺序结构”、“选择IF-THEN-ELSE结构”、“重复DO-WHILE或DO-UNTIL结构”。
1968年,Dijkstra在写给(美国计算机协会通讯)杂志编辑部的信中再次建议从一切高级语言中取消GOTO语句,只使用三种基本控制结构编写程序。他的建议引起了激烈的争论。争论集中在如何看待GOTO语句的问题上。赞成取消 GOTO 语句的一方认为,GOTO语句对程序清晰性有很大破坏作用,凡是使用GOTO语句多的程序,其控制流时而GOTO向前,时而GOTO向后,常使程序变得很难理解,从而增加查错和维护的困难,降低程序的可维护性。但以D.E.Knuth为代表的另一方认为,GOTO 语句虽然存在着破坏程序清晰性的问题,但不应完全禁止。因为GOTO语句概念简单,使用方便,在某些情况下,保留GOTO语句反能使写出的程序更加简洁,并且GOTO语句可直接得到硬件指令的支持。经过争论,人们认识到,不是简单地去掉GOTO语句的问题,而是要创立一种新的程序设计思想、方法和风格,以显著提高软件生产率和软件质量,降低软件维护的成本。
70年代初N.Wirth在设计Pascal语言时对GOTO语句的处理可被当做对GOTO 语句争论的结论。在Pascal语言中设置了支持上述三种基本控制结构的语句;另一方面,GOTO语句仍然保留在该语言中。不过,N.Wirth解释说,通常使用所提供的几种基本控制结构已经足够,习惯于这样做的人不会感到GOTO语句的必要。也就是说,在一般情况下,可以完全不使用GOTO语句。如果在特殊情况下,由于特定的要求,偶然使用GOTO语句能解决问题,那也未尝不可,只是不应大量使用罢了。
事实上,大量采用GOTO语句实现控制路径,会使程序路径变得复杂而且混乱,从而使程序变得不易阅读,给程序的测试和维护造成困难,还会增加出错的机会,降低程序的可靠性。因此要控制GOTO语句的使用。但有时完全不用GOTO语句进行程序编码,比用GOTO语句编出的程序可读性差。例如,在查找结束时,文件访问结束时,出现错误情况要从循环中转出时,使用布尔变量和条件结构来实现就不如用GOTO语句来得简洁易懂。
【4-3】①、④、⑥。编制程序的过程实际上是根据设计的结果,用某种机器能够识别的程序设计语言,将设计翻译成机器代码的过程。因此,必须如实地按照设计说明书编写程序。至于设计说明书中含糊不清的地方,应当在编程时与分析人员或设计人员协商,对这些地方做出适当的解释。另外,考虑到将来的程序修改,必须为程序编写完整的说明书,同时程序必须编写得容易让别人看得懂,这样程序才容易修改,修改时不容易出错,而且容易验证修改后得结果。还有,编写程序的人不须重新考虑程序要完成什么功能,这些已经在软件分析与设计过程中充分考虑过了。
【4-4】①、③、⑤、⑦、⑨是正确的。因为 ① 条件语句和循环语句嵌套得过多会增加程序的复杂性,从而增加程序的出错率。③ 虽然国际以至国内已经发表了编程语言的标准,但各个计算机厂商在推出自己的计算机系统的同时,也推出了针对自己机器特色的程序设计语言的非标准版本,如果利用这些语言的非标准特性编写程序,就会给将来程序的移植带来困难。为了提高程序的可移植性,应当只使用语言的标准版本,不要滥用语言的非标准特色。⑤ 给在程序中使用的变量赋予与实际含义相符的名字,可以提高程序的可读性,从而提高程序的可维护性。⑦ 程序优化的工作最好交给编译程序来做,程序员应把主要注意力放在提高程序的可读性、清晰性、简洁性、正确性、一致性等方面,从而保证软件的可靠性和可维护性。⑨ 程序的可读性是至关重要的。所以程序的格式应有助于读者理解程序。
【4-5】A. ②, B. ①, C. ①, D. ①, E. ③, F. ②, G. ④
除以上几种论述外,其它的叙述都不对。例如,程序中加入临时变量,可能会改变程序执行中的时序关系,造成程序出错。在表达式中加入括号,可以明确标明表达式的运算优先关系,避免因语言方面的原因可能潜藏的错误。程序模块的大小要适中,不是编得越短越好。注解加多少,由问题得难度来决定,但决不是可有可无的。最后要限制GOTO 语句的使用,因为它可能会造成思路混乱、极易出错。
A.计算机用户通常是使用“编辑程序(Editor)”对源程序文本进行录入、编辑、存储的,不用自举程序(Bootstrap)、连接程序(Loader)或文本格式化程序(Textformatter)。
B. 解释系统是边解释源程序边执行该源程序,编译程序是先编译出源程序的对应目标代码,再执行这些目标代码。所以编译程序编出的目标代码运行效率高。
C. FORTRAN程序是以SUBROUTINE为单元的块状结构,对每一个SUBROUTINE进行编译后通过连接形成整个程序系统。它不是嵌套的。
D. 国际上最流行的商业和行政管理语言是COBOL语言。
E. 国际上最流行的用于数值计算的语言是FORTRAN语言。
F. 美国国防部主持开发高级程序设计语言Ada时,曾确定以ALGOL语言作为Ada研究的出发点。所以,Ada、ALGOL、Pascal、BASIC和C都是ALGOL系的一些程序语言。
G. 在人工智能领域,目前最广泛使用的高级语言是Lisp。
【4-6】A. ③ B. ①、⑥ C. ①、⑦
A. 汇编程序实际是指汇编语言的处理程序。而用汇编语言写成的源程序一般称为汇编语言程序。
B. 为了实现递归子程序的正确调用,一般使用堆栈来保存每次调用后返回到上一层程序的返回地址、本次递归调用时的形式参数、局部变量等。
C. UNIX操作系统是Bell实验室研制的,用C语言写出来的。
【4-7】(1) 结构化的程序流程图:

(2) N-S图:

(3) 环路复杂性度量 V(G) = 6
【4-8】A. ⑤, B. ②, C. ③, D. ①, E. ③
1960年Dijkstra提倡的结构化程序设计方法,反对(后来改为尽量避免)在程序中使用GOTO语句,认为使用顺序、选择、重复等三种基本控制结构进行组合、嵌套,就可以构造出整个程序。这种程序设计方法可以提高程序的生产率,还有利于将来程序的维护。

【4-9】开发规模相同,但复杂性不同的软件,花费的成本和时间会有很大的差异。因此到目前为止,还没有一个软件复杂性度量的方法能够全面、系统地度量任一软件的复杂性,某一种度量方法只偏重于某一方面。所以,用某一种软件复杂性来度量不同类型的程序,所得到的度量值不一定真正反映它们的复杂性。但对同一类型的程序,按某种视点来度量它们的复杂性,其结果还是比较有价值的。
【4-10】K.Magel从六个方面描述软件复杂性:
① 理解程序的难度;
② 改错及维护程序的难度;
③ 向他人解释程序的难度;
④ 按指定方法修改程序的难度;
⑤ 根据设计文档编写程序的工作量;
⑥ 执行程序时需要资源的程度。
软件复杂性度量模型应遵循的基本原则:
⑴ 软件复杂性与程序大小的关系不是线性的;
⑵ 控制结构复杂的程序较复杂;
⑶ 数据结构复杂的程序较复杂;
⑷ 转向语句使用不当的程序较复杂;
⑸ 循环结构比选择结构复杂,选择结构又比顺序结构复杂;
⑹ 语句、数据、子程序和模块在程序中的次序对软件复杂性都有影响;
⑺ 全程变量、非局部变量较多时程序较复杂;
⑻ 参数按地址传递比按值传递更复杂;
⑼ 函数副作用比显式参数传递更难以琢磨;
⑽ 具有不同作用的变量共用一个名字时较难理解;
⑾ 模块间或过程间联系密切的程序较复杂;
⑿ 嵌套深度越深程序越复杂。
最典型的两种程序复杂性度量的方法中,McCabe环路复杂性度量就是针对基本原则(2)制定的度量模型;Halstead软件科学则是针对程序中操作符和操作数的出现频度而制定的度量模型。

软件测试

例题分析

【例1】为了把握软件开发各个环节的正确性和协调性,人们需要进行( A )和( B )工作。( A )的目的是想证实在一给定的外部环境中软件的逻辑正确性。它包括( C )和( D ),( B )则试图证明在软件生存期各个阶段,以及阶段间的逻辑( E )、( F )和正确性。
供选择的答案:
A, B. ① 操作 ② 确认 ③ 验证 ④ 测试 ⑤ 调试
C, D. ① 用户的确认 ② 需求规格说明的确认
③ 程序的确认 ④ 测试的确认
E, F. ① 可靠性 ② 独立性 ③ 协调性 ④ 完备性 ⑤ 扩充性
答案:A. ② B. ③ C. ② D. ③ E. ③ F. ④
分析:到程序的测试为止,软件开发工作已经经历了许多环节,每个环节都可能发生问题。为了把握各个环节的正确性,人们需要进行各种确认和验证工作。
所谓确认,是一系列的活动和过程,其目的是想证实在一个给定的外部环境中软件的逻辑正确性。它包括需求规格说明的确认和程序的确认,而程序的确认又分为静态确认与动态确认。静态确认一般不在计算机上实际执行程序,而是通过人工分析或者程序正确性证明来确认程序的正确性;动态确认主要通过动态分析和程序测试来检查程序的执行状态,以确认程序是否有问题。
所谓验证,则试图证明在软件生存期各个阶段,以及阶段间的逻辑协调性、完备性和正确性。下图中所示的就是软件生存期各个重要阶段之间所要保持的正确性。它们就是验证工作的主要对象。

确认与验证工作都属于软件测试。在对需求理解与表达的正确性、设计与表达的正确性、实现的正确性以及运行的正确性的验证中,任何一个环节上发生了问题都可能在软件测试中表现出来。
【例2】测试过程需要三类输入:( A )、( B )和( C )。请选择正确的答案填入下图中以完成测试信息处理的全过程。

供选择的答案:
A C. ① 接口选择 ② 软件配置 ③ 硬件配置
④ 测试配置 ⑤ 测试环境 ⑥ 测试工具
D F. ① 排错 ② 可靠性分析 ③ 结果分析 ④ 数据分类
答案:A. ② B. ④ C. ⑥ D. ③ E. ① F. ②
分析:测试信息流如图所示。测试过程需要三类输入:
(1)软件配置:包括软件需求规格说明、软件设计规格说明、源代码等;
(2)测试配置:包括表明测试工作如何进行的测试计划、给出测试数据的测试用例、控制测试进行的测试程序等;实际上,测试配置是软件配置的一个子集。
(3)测试工具:为提高软件测试效率,测试工作需要有测试工具的支持,它们的工作就是为测试的实施提供某种服务。例如,测试数据自动生成程序、静态分析程序、动态分析程序、测试结果分析程序、以及驱动测试的测试数据库等等。
测试之后,要对所有测试结果进行分析,即将实测的结果与预期的结果进行比较。如果发现出错的数据,就意味着软件有错误,然后就需要开始排错(调试)。即对已经发现的错误进行错误定位和确定出错性质,并改正这些错误,同时修改相关的文档。修正后的文档一般都要经过再次测试,直到通过测试为止。通过收集和分析测试结果数据,开始对软件建立可靠性模型。

                          测试信息流

最后,如果测试发现不了错误,那么几乎可以肯定,测试配置考虑得不够细致充分,错误仍然潜伏在软件中。这些错误最终不得不由用户在使用中发现,并在维护时由开发者去改正。但那时改正错误的费用将比在开发阶段改正错误的费用要高出40倍到60倍。
【例3】软件测试是软件质量保证的主要手段之一,测试的费用已超过( A )的30%以上。因此,提高测试的有效性十分重要。“高产”的测试是指( B )。根据国家标准GB 8566–88《计算机软件开发规范》的规定,软件的开发和维护划分为8个阶段,其中,单元测试是在( C )阶段完成的,集成测试的计划是在( D )阶段制定的,确认测试的计划是在( E )阶段制定的。
供选择的答案:
A. ① 软件开发费用 ② 软件维护费用 ③ 软件开发和维护费用
④ 软件研制费用 ⑤ 软件生存期全部
B. ① 用适量的测试用例运行程序,证明被测程序正确无误
② 用适量的测试用例运行程序,证明被测程序符合相应的要求
③ 用少量的测试用例运行程序,发现被测程序尽可能多的错误
④ 用少量的测试用例运行程序,纠正被测程序尽可能多的错误
C E. ① 可行性研究和计划 ② 需求分析 ③ 概要设计
④ 详细设计 ⑤ 实现 ⑥ 集成测试
⑦ 确认测试 ⑧ 使用和维护
答案:A. ① B. ③ C. ⑤ D. ③ E. ②
分析:由于原始问题的复杂性,软件本身的复杂性和抽象性,软件开发各个阶段工作的多样性,以及参加开发各种层次人员之间工作的配合关系等因素,使得开发的每个环节都可能产生错误。这些错误和缺陷如果在软件交付投入生产性运行之前不能加以排除的话,在运行中迟早会暴露出来。但到那时,不仅改正这些错误的代价更高,而且往往造成很恶劣的后果。因此,为了保证所开发软件的质量,测试是不可少的。测试的任务就是设计出测试用例,运行被测程序,发现软件中隐藏的的各种错误。目前在大中型软件项目的开发中。测试占据着重要的地位,测试所花费费用已超过软件开发费用的70%以上。
如何组织好测试,特别是如何选择好的测试用例,对于降低测试费用,保障测试质量有着重要的意义。一个高效的、或“高产”的测试,是指设计少量的测试用例,用它们来运行程序,发现被测程序的尽可能多的问题。
测试按照测试的内容可分为三种:单元测试(对程序单元或模块单独进行测试)、集成测试(把已通过单元测试的模块按照设计的要求组装起来,测试模块间的接口以及设计中的问题)和确认测试(对软件的有效性做全面测试,以确定开发的软件是否符合验收标准)。
根据国标GB 8566–88《计算机软件开发规范》的规定,软件的开发和维护划分为8个阶段:可行性研究及计划、需求分析、概要设计、详细设计、实现、集成测试、确认测试、使用维护,并且规定,单元测试是在实现阶段完成的,集成测试的计划应当在概要设计阶段制定的,确认测试的计划应当在需求分析阶段制定的。
【例4】集成测试也叫做( A )或( B )。通常,在( C )的基础上,将所有模块按照设计要求组装成为系统。子系统的集成测试特别称为( D ),它所做的工作是要找出子系统和系统需求规格说明之间的( E )。需要考虑的问题是:在把各个模块连接起来的时侯,穿越模块接口的数据是否会( F ); 一个模块的功能是否会对另一个模块的功能产生不利的影响;各个( G )组合起来,能否达到预期要求的( H );( I )是否有问题;单个模块的误差累积起来是否会放大。
供选择的答案:
A D. ① 单元测试 ② 部件测试 ③ 组装测试
④ 系统测试 ⑤ 确认测试 ⑥ 联合测试
E I. ① 子功能 ② 丢失 ③ 父功能 ④ 局部数据结构
⑤ 全局数据结构 ⑥ 不一致 ⑦ 一致
答案:A. ③ B. ⑥ C. ① D. ② E. ⑥ F. ② G. ① H. ③ I. ⑤
A、B的答案可互换
分析:集成测试也叫做组装测试或联合测试。通常,在单元测试的基础上,将所有模块按照设计要求组装成为系统。子系统的集成测试特别称为部件测试,它所做的工作是要找出子系统和系统需求规格说明之间的不一致。这时需要考虑的问题是:
(1) 在把各个模块连接起来的时侯,穿越模块接口的数据是否会丢失;
(2) 一个模块的功能是否会对另一个模块的功能产生不利的影响;
(3) 各个子功能组合起来,能否达到预期要求的父功能;
(4) 全局数据结构是否有问题;
(5) 单个模块的误差累积起来是否会放大。
【例5】如图所示的程序有四条不同的路径。分别表示为L1(a→c→e)、L2(a→b→d)、L3(a→b→e) 和L4(a→c→d),或简写为ace、abd、abe及acd。由于覆盖测试的目标不同,逻辑覆盖方法可以分为语句覆盖、判定覆盖、条件覆盖、判定–条件覆盖、条件组合覆盖和路径覆盖。
从备选的答案中选择适当的测试用例与之匹配。( A )属于语句覆盖;( B )、( C )属于判定覆盖;( D )、( E )属于条件覆盖;( F )、( G )属于判定–条件覆盖;( H )属于条件组合覆盖;( I )属于路径覆盖。
供选择的答案:
A. I. ① 【(2,0,4),(2,0,3)】覆盖 ace;
【(1,1,1),(1,1,1)】覆盖 abd;
判断条件覆盖
② 【(1,0,3),(1,0,4)】覆盖abe;
【(2,1,1),(2,1,2)】覆盖 abe;条件覆盖
③ 【(2,0,4),(2,0,3)】覆盖 ace;
④ 【(2,1,1),(2,1,2)】覆盖 abe; 【(3,0,3),(3,1,1)】覆盖 acd;
⑤ 【(2,0,4),(2,0,3)】覆盖 ace; 【(1,0,1),(1,0,1)】覆盖 abd;
【(2,1,1),(2,1,2)】覆盖 abe; 条件覆盖, 判条
⑥ 【(2,0,4),(2,0,3)】覆盖 ace 【(1,1,1),(1,1,1)】覆盖 abd;路径覆盖
【(1,1,2),(1,1,3)】覆盖 abe; 【(3,0,3),(3,0,1)】覆盖acd;
⑦ 【(2,0,4),(2,0,3)】覆盖 ace; 【(1,1,1),(1,1,1)】覆盖 abd; 条件组合
【(1,0,3),(1,0,4)】覆盖 abe; 【(2,1,1),(2,1,2)】覆盖 abe;
答案:A. ③ B. ① C. ④ D. ② E. ⑤ F. ① G. ⑤ H. ⑦ I. ⑥
B、C的答案可互换。D、E的答案可互换。F、G的答案可互换。
分析:(1) 语句覆盖
所谓语句覆盖就是设计若干个测试用例,运行被测程序,使得每一可执行语句至少执行一次。例如在本题所给出的例子中,正好所有的可执行语句都在路径L1上, 所以选择路径L1设计测试用例,就可以覆盖所有的可执行语句。
测试用例的设计格式如下
【输入的(A, B, x),输出的(A, B, x)】
本题满足语句覆盖的测试用例是:
【(2, 0, 4),(2, 0, 3)】 覆盖 ace【L1】 (答案③)
从程序中每个可执行语句都得到执行这一点来看,语句覆盖的方法似乎能够比较全面地检验每一个可执行语句。但需要注意的是,这种覆盖也绝不是完美无缺的。假设图中两个判断的逻辑运算有问题,例如,第一个判断中的逻辑运算符“”错写成了“”, 或者第二个判断中的逻辑运算符“”错写成了“”,利用上面的测试用例,仍可覆盖所有4个可执行语句。这说明虽然做到了语句覆盖,但可能发现不了判断中逻辑运算中出现的错误。与后面所介绍的其它覆盖相比,语句覆盖是最弱的逻辑覆盖准则。
(2) 判定覆盖
所谓判定覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判断的取真分支和取假分支至少经历一次。判定覆盖又称为分支覆盖。对于本题所给出的例子,如果选择路径L1和L2,就可得满足要求的测试用例:
【(2, 0, 4),(2, 0, 3)】 覆盖 ace【L1】 (答案①)
【(1, 1, 1),(1, 1, 1)】 覆盖 abd【L2】
如果选择路径L3和L4,还可得另一组可用的测试用例:
【(2, 1, 1),(2, 1, 2)】 覆盖 abe【L3】 (答案④)
【(3, 0, 3),(3, 1, 1)】 覆盖 acd【L4】
所以,测试用例的取法不唯一。注意有例外情形,例如,若把图例中第二个判断中的条件x>1错写成x<1,那么利用上面两组测试用例,仍能得到同样结果。这表明,只是判定覆盖,还不能保证一定能查出在判断的条件中存在的错误。因此,还需要更强的逻辑覆盖准则去检验判断内部条件。
(3) 条件覆盖
所谓条件覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判断的每个条件的可能取值至少执行一次。例如在本题所给出的例子中,我们事先可对所有条件的取值加以标记。例如,
对于第一个判断:条件 A>1 取真值为,取假值为
条件 B=0 取真值为,取假值为
对于第二个判断:条件 A=2 取真值为,取假值为
条件 x>1 取真值为,取假值为
则可选取测试用例如下:
测试用例(答案⑤) 通过路径 条件取值 覆盖分支
【(2, 0, 4),(2, 0, 3)】
【(1, 0, 1),(1, 0, 1)】
【(2, 1, 1),(2, 1, 2)】 ace (L1)
abd (L2)
abe (L3)

	   c,e

b,d
b,e


测试用例(答案②) 通过路径 条件取值 覆盖分支
【(1, 0, 3),(1, 0, 4)】 abe (L3) b,e
【(2, 1, 1),(2, 1, 2)】 abe (L3) b,e
注意,前一组测试用例不但覆盖了所有判断的取真分支和取假分支,而且覆盖了判断中所有条件的可能取值。但是后一组测试用例虽满足了条件覆盖,但只覆盖了第一个判断的取假分支和第二个判断的取真分支,不满足判定覆盖的要求。为解决这一矛盾,需要对条件和分支兼顾,有必要考虑以下的判定-条件覆盖。
(4) 判定-条件覆盖
所谓判定-条件覆盖就是设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次,同时每个判断本身的所有可能判断结果至少执行一次。换言之,即是要求各个判断的所有可能的条件取值组合至少执行一次。例如,对于本题所给例子中的各判断,若、、、及、、、 的含意如前所述,则只需设计以下两个测试用例便可覆盖图中的8个条件取值以及4个判断分支。
测试用例(答案①) 通过路径 条件取值 覆盖分支
【(2, 0, 4),(2, 0, 3)】 ace (L1) c,e
【(1, 1, 1),(1, 1, 1)】 abd (L2) b,d
判定-条件覆盖也有缺陷。从表面上来看,它测试了所有条件的取值。但是事实并非如此。因为往往某些条件掩盖了另一些条件。对于条件表达式(A>1)and(B=0)来说,若(A>1)的测试结果为真,则还要测试(B=0),才能决定表达式的值;而若(A>1)的测试结果为假,可以立刻确定表达式的结果为假。这时,往往就不再测试(B=0)的取值了。因此,条件(B=0)就没有检查。同样,对于条件表达式(A=2)or(X>1)来说,若(A=2)的测试结果为真,就可以立即确定表达式的结果为真。这时,条件(X>1)就没有检查。因此,采用判定-条件覆盖,逻辑表达式中的错误不一定能够查得出来。
(5) 条件组合覆盖
所谓条件组合覆盖就是设计足够的测试用例,运行被测程序,使得每个判断的所有可能的条件取值组合至少执行一次。现在我们仍来考察本题所给出的例子,先对各个判断的条件取值组合加以标记。例如,
记 ① A>1,B=0 作 ,属第一个判断的取真分支;
② A>1,B≠0 作 ,属第一个判断的取假分支;
③ A≯1,B=0 作 ,属第一个判断的取假分支;
④ A≯1,B≠0 作 ,属第一个判断的取假分支;
⑤ A=2,x>1 作 ,属第二个判断的取真分支;
⑥ A=2,x≯1 作 ,属第二个判断的取真分支;
⑦ A≠2,x>1 作 ,属第二个判断的取真分支;
⑧ A≠2,x≯1 作 ,属第二个判断的取假分支;
对于每个判断,要求所有可能的条件取值的组合都必须取到。在图中的每个判断各有两个条件,所以各有4个条件取值的组合。我们取4个测试用例,就可用以覆盖上面8种条件取值的组合。必须明确,这里并未要求第一个判断的4个组合与第二个判断的4个组合再进行组合。要是那样的话,就需要42=16个测试用例了。
测试用例(答案⑦) 通过路径 覆盖条件 覆盖组合号
【(2, 0, 4),(2, 0, 3)】 ace (L1) ①,⑤
【(2, 1, 1),(2, 1, 2)】 abe (L3) ②,⑥
【(1, 0, 3),(1, 0, 4)】 abe (L3) ③,⑦
【(1, 1, 1),(1, 1, 1)】 abd (L2) ④,⑧
这组测试用例覆盖了所有条件的可能取值的组合,覆盖了所有判断的可取分支,但路径漏掉了L4。测试还不完全。
(6) 路径测试
路径测试就是设计足够的测试用例,覆盖程序中所有可能的路径。若还是看本题所给出的例子,则可以选择如下的一组测试用例来覆盖该程序段的全部路径。
测试用例(答案⑥) 通过路径 覆盖条件
【(2, 0, 4),(2, 0, 3)】 ace (L1)
【(1, 1, 1),(1, 1, 1)】 abd (L2)
【(1, 1, 2),(1, 1, 3)】 abe (L3)
【(3, 0, 3),(3, 0, 1)】 acd (L4)
【例6】下面是快速排序算法中的一趟划分算法,其中datalist是数据表,它有两个数据成员:一是元素类型为Element的数组V,另一个是数组大小n。算法中用到两个操作,一是取某数组元素V[i]的关键码操作getKey ( ),一是交换两数组元素内容的操作Swap( ):
int Partition ( datalist &list, int low, int high ) {
//在区间[ low, high ]以第一个对象为基准进行一次划分,k返回基准对象回放位置。
int k = low; Element pivot = list.V[low]; //基准对象
for ( int i = low+1; i <= high; i++ ) //检测整个序列,进行划分
if ( list.V[i].getKey ( ) < pivot.getKey( ) && ++ k != i )
Swap ( list.V[k], list.V[i] ); //小于基准的交换到左侧去
Swap ( list.V[low], list.V[k] ); //将基准对象就位
return k; //返回基准对象位置
}
(1) 试画出它的程序流程图;
(2) 试利用路径覆盖方法为它设计足够的测试用例(循环次数限定为0次,1次和2次)。
答案(1)流程图如下。

(2) 测试用例设计

循环
次数 输 入 条 件 输 出 结 果 执 行 路 径
low high k i V[0] V[1] V[2] k i V[0] V[1] V[2]
0 0 0 0 1 0 1 ①③
1 0 1 0 1 1 2 0 2 1 2 ①②⑤⑥③
0 1 0 1 2 1 1 2 1 2 ①②④⑥③
0 1 0 1 1 1 0 2 1 1 ①②⑤⑥③
2 0 2 0 1 1 2 3 0 3 1 2 3 ①②⑤⑥②⑤⑥③
0 2 0 1 1 2 1 0 3 1 2 1 ①②⑤⑥②⑤⑥③
0 2 0 1 2 3 1 1 3 1 2 3 ①②⑤⑥②④⑥③
0 2 0 1 3 2 1 2 3 1 2 3 ①②④⑥②④⑥③
0 2 0 1 2 1 2 1 3 1 2 2 ①②④⑥②⑤⑥③
0 2 0 1 2 1 3 1 3 1 2 3 ①②④⑥②⑤⑥③
0 2 0 1 1 1 2 0 3 1 1 2 ①②⑤⑥②⑤⑥③
0 2 0 1 2 2 1 1 3 1 2 2 ①②⑤⑥②④⑥③
0 2 0 1 2 2 2 0 3 2 2 2 ①②⑤⑥②⑤⑥③

分析:画程序流程图是设计测试用例的关键。从以往同学解题的经验来看,在画流程图时就出错了。所以首先要把流程图中的逻辑关系搞清楚再画出正确的流程图。考虑测试用例设计需要首先有测试输入数据,还要有预期的输出结果。对于此例,控制循环次数靠循环控制变量i和循环终值high。循环0次时,取low = high,此时一次循环也不做。循环一次时,取low +1 = high,循环二次时,取low+2 = high。
根据BRO策略,条件V[i] < pivot && ++k≠i的约束集合为 { (<, < ), (<, = ), (=, <), (>, < ) },因此,测试用例设计为:
循环
次数 输 入 条 件 输 出 结 果
low high k i V[0] V[1] V[2] pivot k i V[0] V[1] V[2]
0 0 0 0 1 0 1
1 0 1 0 1 1 2 1 0 2 1 2 (>, <)
0 1 0 1 2 1 2 1
2
2
1 1
2 (<, =)
0 1 0 1 1 1 1 0 2 1 1 (=, <)
0 1 0 1 不 可 达 (<, <)
2
0 2 0 1 1 2 3 1 0
0 2
3 1
1 2
2 3
3 (>, <) (>, <)
0 2 0 1 1 2 1 1 0
0 2
3 1
1 2
2 1
1 (>, <) (=, <)
0 2 0 1 2 3 1 2 0
1 2
3 2
2
1 3
1
2 1
3
3 (>, <) (<, <)
0 2 0 1 不 可 达 (>, <) (<, =)
0 2 0 1 3 2 1 3 1
2 2
3 3
3
1 2
2
2 1
1
3 (<, =) (<, =)
0 2 0 1 2 1 2 2 1
1 2
3 2
2
1 1
1
2 2
2
2 (<, =) (=, <)
0 2 0 1 2 1 3 2 1
1 2
3 2
2
1 1
1
2 3
3
3 (<, =) (>, <)
0 2 0 1 不 可 达 (<, =) (<, <)
0 2 0 1 1 1 2 1 0
0 2
3 1
1 1
1 2
2 (=, <) (>, <)
0 2 0 1 2 2 1 2 0
1 2
3 2
2
1 2
1
2 1
2
2 (=, <) (<, <)
0 2 0 1 2 2 2 2 0
0 2
3 2
2 2
2 2
2 (=, <) (=, <)
0 2 0 1 不 可 达 (=, <) (<, =)
0 2 0 1 不 可 达 (<, <) (*, *)

习题

【5-1】从供选择的答案中选出应填入下列( )中的字句。
软件测试的目的是( A )。为了提高测试的效率,应该( B )。使用白盒测试方法时,确定测试数据应根据( C )和指定的覆盖标准。与设计测试数据无关的文档是( D )。
软件的集成测试工作最好由( E )承担,以提高集成测试的效果。
供选择的答案:
A. ① 评价软件的质量 ② 发现软件的错误
③ 找出软件中的所有错误 ④ 证明软件是正确的
B. ① 随机地选取测试数据
② 取一切可能的输入数据作为测试数据
③ 在完成编码以后制定软件的测试计划
④ 选择发现错误的可能性大的数据作为测试数据
C. ① 程序的内部逻辑 ② 程序的复杂程度
③ 使用说明书 ④ 程序的功能
D. ① 该软件的设计人员 ② 程序的复杂程度
③ 源程序 ④ 项目开发计划
E. ① 该软件的设计人员 ② 该软件开发组的负责人
③ 该软件的编程人员 ④ 不属于该软件开发组的软件设计人员
【5-2】请从供选择的答案中选出应填入下列( )中的字句。
程序的三种基本控制结构是( A )。它们的共同点是( B )。结构化程序设计的一种基本方法是( C )。软件测试的目的是( D )。软件调试的目的是( E )。
供选择的答案:
A. ① 过程,子程序,分程序 ② 顺序,条件,循环
③ 递归,堆栈,队列 ④ 调用,返回,转移
B. ① 不能嵌套使用 ② 只能用来写简单的程序
③ 已经用硬件实现 ④ 只有一个入口和一个出口
C. ① 筛选法 ② 递归法 ③ 归纳法 ④ 逐步求精法
D. ① 证明程序中没有错误 ② 发现程序中的错误
③ 测量程序的动态特性 ④ 检查程序中的语法错误
E. ① 找出错误所在并改正之 ② 排除存在错误的可能性
③ 对错误性质进行分类 ④ 统计出错的次数
【5-3】从下列关于软件测试的叙述中,选出5条正确的叙述。
(1) 用黑盒法测试时,测试用例是根据程序内部逻辑设计的。
(2) 尽量用公共过程或子程序去代替重复的代码段。
(3) 测试是为了验证该软件已正确地实现了用户的要求。
(4) 对于连锁型分支结构,若有n个判定语句,则有2n条路径。
(5) 尽量采用复合的条件测试,以避免嵌套的分支结构。
(6) GOTO语句概念简单,使用方便,在某些情况下,保留GOTO语句反能使写出的程序更加简洁。
(7) 发现错误多的程序模块,残留在模块中的错误也多。
(8) 黑盒测试方法中最有效的是因果图法。
(9) 在做程序的单元测试时,桩(存根)模块比驱动模块容易编写。
(10) 程序效率的提高主要应通过选择高效的算法来实现。
【5-4】从供选择的答案中选出同下列关于软件测试的各条叙述关系最密切的字句。
(1) 对可靠性要求很高的软件,例如操作系统,由第三者对源代码进行逐行检查。
(2) 已有的软件被改版时,由于受到变更的影响,改版前正常的功能可能发生异常,性能也可能下降。因此,对变更的软件进行测试是必要的。
(3) 在意识到被测试模块的内部结构或算法的情况下进行测试。
(4) 为了确认用户的需求,先做出系统的主要部分,提交给用户试用。
(5) 在测试具有层次结构的大型软件时,有一种方法是从上层模块开始,由上到下进行测试。此时,有必要用一些模块替代尚未测试过的下层模块。
供选择的答案:
A E: ① 仿真器 ② 代码审查 ③ 模拟器 ④ 桩 ⑤ 驱动器
⑥ 域测试 ⑦ 黑盒测试 ⑧ 原型 ⑨ 白盒测试 ⑩ 退化测试
【5-5】对小的程序进行穷举测试是可能的,用穷举测试能否保证程序是百分之百正确呢?
【5-6】在任何情况下单元测试都是可能的吗?都是需要的吗?
【5-7】从供选择的答案中选出应填入下面有关软件测试的叙述的( )内的正确答案。
软件测试方法可分为黑盒测试法和白盒测试法两种。
黑盒测试法是通过分析程序的( A )来设计测试用例的方法。除了测试程序外,它还适用于对( B )阶段的软件文档进行测试。
白盒测试法是根据程序的( C )来设计测试用例的方法。除了测试程序外,它也适用于对( D )阶段的软件文档进行测试。
白盒法测试程序时常按照给定的覆盖条件选取测试用例。( E )覆盖比( F )覆盖严格,它使得每一个判定的每一条分支至少经历一次。( G )覆盖既是判定覆盖,又是条件覆盖,但它并不保证使各种条件都能取到所有可能的值。( H )覆盖比其他条件都要严格,但它不能保证覆盖程序中的每一条路径。
单元测试一般以( I )为主,测试的依据是( J )。
供选择的答案:
A, C:① 应用范围 ② 内部逻辑 ③ 功能 ④ 输入数据
B, D:① 编码 ② 软件详细设计 ③ 软件总体设计 ④ 需求分析
E, F, G, H:① 语句 ② 判定 ③ 条件 ④ 判定/条件
⑤ 多重条件 ⑥ 路径
I:① 白盒法 ② 黑盒法
J:① 模块功能规格说明 ② 系统模块结构图 ③ 系统需求规格说明
【5-8】从供选择的答案中选出应该填入下列关于软件测试的叙述的( )内的正确答案。
软件测试中常用的静态分析方法是( A )和( B )。( B )用于检查模块或子程序间的调用是否正确。分析方法(白盒方法)中常用的方法是( C )方法。非分析方法(黑盒方法)中常用的方法是( D )方法和( E )方法。( E )方法根据输出对输入的依赖关系设计测试用例。
供选择的答案:
A B: ① 引用分析 ② 算法分析 ③ 可靠性分析 ④ 效率分析
⑤ 接口分析 ⑥ 操作分析
C ~ E: ① 路径测试 ② 等价类 ③ 因果图 ④ 归纳测试
⑤ 综合测试 ⑥ 追踪 ⑦ 深度优先 ⑧ 调试
⑨ 相对图
【5-9】下面是选择排序的程序,其中datalist是数据表,它有两个数据成员:一是元素类型为Element的数组V,另一个是数组大小n。算法中用到两个操作,一是取某数组元素V[i]的关键码操作getKey ( ),一是交换两数组元素内容的操作Swap( )::
void SelectSort ( datalist & list ) {
//对表list.V[0]到list.V[n-1]进行排序, n是表当前长度。
for ( int i = 0; i < list.n-1; i++ ) {
int k = i; //在list.V[i].key到list.V[n-1].key中找具有最小关键码的对象
for ( int j = i+1; j < list.n; j++)
if ( list.V[j].getKey ( ) < list.V[k].getKey ( ) ) k = j; //当前具最小关键码的对象
if ( k != i ) Swap ( list.V[i], list.V[k] ); //交换
}
}
(1) 试计算此程序段的McCabe复杂性;
(2) 用基本路径覆盖法给出测试路径;
(3) 为各测试路径设计测试用例。
【5-10】根据下面给出的规格说明,利用等价类划分的方法,给出足够的测试用例。
“一个程序读入三个整数。把此三个数值看成是一个三角形的三个边。这个程序要打印出信息,说明这个三角形是三边不等的、是等腰的、还是等边的。”
【5-11】设要对一个自动饮料售货机软件进行黑盒测试。该软件的规格说明如下:
“有一个处理单价为1元5角钱的盒装饮料的自动售货机软件。若投入1元5角硬币,按下“可乐”、“雪碧”或“红茶”按钮,相应的饮料就送出来。若投入的是2元硬币,在送出饮料的同时退还5角硬币。”
(1) 试利用因果图法,建立该软件的因果图;
(2) 设计测试该软件的全部测试用例。
【5-12】对一个长度为100,000条指令的程序进行测试,记录下来的数据如下:
测试开始, 发现错误个数为0;
经过160小时的测试, 累计改正100个错误, 此时, MTTF = 0.4小时;
又经过160小时的测试, 累计改正300个错误, 此时, MTTF = 2小时;
(1) 估计程序中固有的错误总数;
(2) 为使MTTF达到10小时, 必须测试和调试这个程序多长时间?
(3) 给出MTTF与测试时间t之间的函数关系。
【5-13】应该由谁来进行确认测试?是软件开发者还是软件用户?为什么?

习题解答

【5-1】A. ② B. ④ C. ① D. ④ E. ④
软件测试的目的是软件中的错误。因为不可能把所有可能的输入数据都拿来测试(时间花费不起),为了提高测试的效率,应该选择发现错误的可能性大的数据作为测试数据。
使用白盒测试方法时,确定测试数据应根据程序的内部逻辑和指定的覆盖标准,可以不考虑程序的功能。与设计测试数据无关的文档是项目开发计划。
软件的集成测试工作最好由不属于该软件开发组的软件设计人员承担,以提高集成测试的效果。
【5-2】A. ② B. ④ C. ④ D. ② E. ①
1966年,Bohm与Jacopini提出任何单入口单出口的没有“死循环”的程序都能由三种最基本的控制结构构造出来。这三种基本控制结构就是“顺序结构”、“选择IF-THEN-ELSE结构”、“重复DO-WHILE或DO-UNTIL结构”。 它们的共同点是只有一个入口和一个出口。E.W.Dijkstra提出了程序要实现结构化的主张,并将这一类程序设计称为结构化程序设计。这种方法的一个重要原则就是采用自顶向下、逐步求精的方法编写程序。N.Wirth曾做过如下说明:“我们对付一个复杂问题的最重要的方法就是抽象。 因此,对于一个复杂的问题,不要急于马上用计算机指令、数字和逻辑符号来表示它,而应当先用较自然的抽象的语句来表示,从而得到抽象的程序。抽象程序对抽象的数据类型进行某些特定的运算,并用一些合适的记号(可以是自然语言)来表示。下一步对抽象程序再做分解,进入下一个抽象的层次。这样的细化过程一直进行下去,直到程序能被计算机接受为止。此时的程序已经是用某种高级语言或机器指令书写的了。”
软件调试则是在进行了成功的测试之后才开始的工作。它与软件测试不同,软件测试的目的是尽可能多地发现软件中的错误,但进一步诊断和改正程序中潜在的错误,则是调试的任务。调试活动由两部分组成:① 确定程序中可疑错误的确切性质和位置。② 对程序(设计,编码)进行修改,排除这个错误。
【5-3】正确的叙述有(4)、(5)、(6)、(7)、(10)。
黑盒测试主要是根据程序的有关功能规格说明和覆盖准则来设计测试用例,进行测试的,不是根据程序的内部逻辑来设计测试用例,这是白盒测试做的事情。在所有黑盒测试方法中,最有效的不是因果图法,而是边界值分析方法。测试的目的是尽可能多地发现软件中的错误,其附带的收获才是验证该软件已正确地实现了用户的要求。测试的一条重要原则是:发现错误多的程序模块,残留在模块中的错误也多。软件可靠性模型(Shooman)就是依据这个原则建立它的公式的。对于连锁型分支结构,若有n个判定语句,则有2n条路径。因此,随着n的增大,路径数增长非常快。单元测试时,因为桩模块要模拟子模块的功能,这不是一件容易的事情,而驱动模块只是控制被测模块的执行,所以桩模块的编写比驱动模块的编写要难得多。
在程序设计风格方面,如果重复的代码段没有明显的功能,不可以抽取出来形成独立的公共过程或子程序,只有在这些代码段表现出独立的功能时,才可把它们抽取出来形成独立的公共过程或子程序。另外,程序效率的提高主要应通过选择高效的算法或使用高效的语言编译器来实现。GOTO语句概念简单,使用方便,在某些情况下,保留GOTO语句反能使写出的程序更加简洁,这句话是正确的。
【5-4】(1) ② (2) ⑩ (3) ⑨ (4) ⑧ (5) ④
(1) 对可靠性要求很高的软件,由第三者对源代码进行逐行检查,这是代码审查。
(2) 软件变更时可能发生退化现象:原来正常的功能可能发生异常,性能也可能下降。因此,对变更的软件要进行退化测试。
(3) 基于被测试模块的内部结构或算法设计测试用例进行测试,这是白盒测试。
(4) 为了确认用户的需求,先做出系统的原型,提交给用户试用。
(5) 自顶向下对具有层次结构的大型软件进行集成测试时,需要设计一些虚拟模块来替代尚未测试过的下层模块,这些模块叫做桩模块。
【5-5】对小程序进行穷举测试,不见得能保证程序百分之百正确。所谓穷举测试是拿所有可能的输入数据来作为测试用例(黑盒测试),或覆盖程序中所有可能的路径(白盒测试)。对于小程序来说,实际上并不能真正作到穷举测试。例如前面讲过,一个小程序P只有两个输入X和Y及输出Z,在字长为32位的计算机上运行。如果X、Y只取整数,考虑把所有的X、Y值都做为测试数据,按黑盒方法进行穷举测试,这样做可能采用的测试数据组(Xi,Yi),基数(radix)i的最大可能数目为:232×232=264。如果程序P测试一组X、Y数据需要1毫秒,而且假定一天工作24小时,一年工作365天,要完成264组测试,需要5亿年。
【5-6】单元测试又称模块测试,是针对软件设计的最小单位─程序模块,进行正确性检验的测试工作。其目的在于发现各模块内部可能存在的各种差错。单元测试需要从程序的内部结构出发设计测试用例。多个模块可以平行地独立进行单元测试。
单元测试是在编码阶段完成的,每编写出一个程序模块,就开始做这个模块的单元测试,所以只要采用模块化方法开发软件,单元测试都是必需的。它可由编写程序的人来完成。因为它需要根据程序的内部结构设计测试用例,对于那些不了解程序内部细节的人,这种测试无法进行。
【5-7】A. ③ B. ④ C. ② D. ② E. ② F. ① G. ④ H. ⑤ I. ① J. ①
软件测试方法可分为黑盒测试法和白盒测试法两种。黑盒测试法是基于程序的功能来设计测试用例的方法。除了测试程序外,它还适用于对需求分析阶段的软件文档进行测试。白盒测试法是根据程序的内部逻辑来设计测试用例的方法。除了测试程序外,它也适用于对软件详细设计阶段的软件文档进行测试。
白盒法测试程序时常按照给定的覆盖条件选取测试用例。判定覆盖比语句覆盖严格,它使得每一个判定的每一条分支至少经历一次。判定/条件覆盖既是判定覆盖,又是条件覆盖,但它并不保证使各种条件都能取到所有可能的值。多重条件覆盖,也叫组合条件覆盖,比其他条件都要严格,但它不能保证覆盖程序中的每一条路径。
单元测试一般以白盒法为主,测试的依据是系统的模块功能规格说明。
【5-8】A. ① B. ⑤ C. ① D. ② E. ③
软件测试中常用的静态分析方法是引用分析和接口分析。接口分析用于检查模块或子程序间的调用是否正确。分析方法(白盒方法)中常用的方法是路径测试方法。非分析方法(黑盒方法)中常用的方法是等价类(划分)方法和因果图方法。因果图方法根据输出对输入的依赖关系设计测试用例。
【5-9】(1) McCabe环路复杂性 = 5
(2) 独立路径有5条:
①③
①②⑤⑧……
①②⑤⑨……
①②④⑥……
①②④⑦……
(3) 为各测试路径设计测试用例:
路径①③:取n = 1
路径①②⑤⑧……:取n = 2,
预期结果:路径⑤⑧③不可达
路径①②⑤⑨……:取n = 2,
预期结果:路径⑤⑨③不可达
路径①②④⑥⑤⑧③:
取n = 2, V[0] = 2, V[1] = 1, 预期结果:k = 1, V[0] = 1, V[1] = 2
路径①②④⑥⑤⑨③:
取n = 2, V[0] = 2, V[1] = 1, 预期结果:k = 1, 路径⑨③不可达
路径①②④⑦⑤⑧③:
取n = 2, V[0] = 1, V[1] = 2, 预期结果:k = 0, 路径⑧③不可达
路径①②④⑦⑤⑨③:
取n = 2, V[0] = 1, V[1] = 2, 预期结果:k = 0, V[0] = 1, V[1] = 2
【5-10】设三角形的三条边分别为A, B, C。如果它们能够构成三角形的三条边,必需满足:
A > 0,B > 0,C > 0,且A + B > C,B + C > A,A + C > B。
如果是等腰的,还要判断是否A = B,或B = C,或A = C。
对于等边的,则需判断是否A = B,且B = C,且A = C。
列出等价类表:

 输入条件	        有效等价类	           无效等价类

是否三角形的三条边 (A > 0) (1), (B > 0) (2),
(C > 0) (3), (A + B > C), (4)
(B + C > A) (5), (A + C > B) (6) A 0 (7), B 0 (8), C 0 (9),
A + B C (10), A + C B (11),
B + C A (12)
是否等腰三角形 (A = B) (13), (B = C) (14),
(A = C) (15) (A B) and (B C) and (A C) (16)
是否等边三角形 (A = B) and (B = C) and (A = C)
(17) (A B) (18), (B C) (19),
(A C) (20)

设计测试用例:输入顺序是〖A,B,C〗
〖3,4,5〗覆盖等价类 (1), (2), (3), (4), (5), (6)。满足即为一般三角形。

〖0,1,2〗覆盖等价类 (7)。不能构成三角形。 若不考虑特定A, B, C,
〖1,0,2〗覆盖等价类 (8)。同上。 三者取一即可
〖1,2,0〗覆盖等价类 (9)。同上。
〖1,2,3〗覆盖等价类 (10)。同上。 若不考虑特定A, B, C,
〖1,3,2〗覆盖等价类 (11)。同上。 三者取一即可
〖3,1,2〗覆盖等价类 (12)。同上。
〖3,3,4〗覆盖等价类 (1), (2), (3), (4), (5), (6), (13)。 满足即为等腰三角形,
〖3,4,4〗覆盖等价类 (1), (2), (3), (4), (5), (6), (14)。 若不考虑特定A, B, C,
〖3,4,3〗覆盖等价类 (1), (2), (3), (4), (5), (6), (15)。 三者取一即可
〖3,4,5〗覆盖等价类 (1), (2), (3), (4), (5), (6), (16)。不是等腰三角形。
〖3,3,3〗覆盖等价类 (1), (2), (3), (4), (5), (6), (17)。是等边三角形
〖3,4,4〗覆盖等价类 (1), (2), (3), (4), (5), (6), (14), (18)。 不是等边三角形,
〖3,4,3〗覆盖等价类 (1), (2), (3), (4), (5), (6), (15), (19)。 若不考虑特定A, B, C,
〖3,3,4〗覆盖等价类 (1), (2), (3), (4), (5), (6), (13), (20)。 三者取一即可
【5-11】(1) 因果图

    输入条件(原因)                                   输出条件(结果)

投入1元5角硬币   (1)                                (21) 退还5角硬币
               E                    
投入2元硬币       (2)          (11)                   (22) 送出“可乐”饮料
                       
按“可乐”按钮     (3)                                (23) 送出“雪碧”饮料
                     
按“雪碧”按钮 E   (4)         (12)                  (24) 送出“红茶”饮料
                   
按“红茶”按钮     (5)

(2) 测试用例设计

1	2	3	4	5	6	7	8	9	10	11

入 投入1元5角硬币 (1) 1 1 1 1 0 0 0 0 0 0 0
投入2元硬币 (2) 0 0 0 0 1 1 1 1 0 0 0
按“可乐”按钮 (3) 1 0 0 0 1 0 0 0 1 0 0
按“雪碧”按钮 (4) 0 1 0 0 0 1 0 0 0 1 0
按“红茶”按钮 (5) 0 0 1 0 0 0 1 0 0 0 1
中间
结点 已投币 (11) 1 1 1 1 1 1 1 1 0 0 0
已按钮 (12) 1 1 1 0 1 1 1 0 1 1 1


出 退还5角硬币 (21) 0 0 0 0 1 1 1 0 0 0 0
送出“可乐”饮料 (22) 1 0 0 0 1 0 0 0 0 0 0
送出“雪碧”饮料 (23) 0 1 0 0 0 1 0 0 0 0 0
送出“红茶”饮料 (24) 0 0 1 0 0 0 1 0 0 0 0
测试用例 每一纵列为一个测试用例
【5-12】由shooman公式:

其中,IT = 105,MTTF1 = 0.4,T1 = 160,n1 = 100,MTTF2 = 2,T2 = 320,n2 = 300。得:

(1) 解得程序中固有得错误总数ET = 350,此外K = 1000。
(2) 设MTTF = 10,有

	解得x = 340。由可靠性累积曲线EC (t) = ET (1-e-K1 t ),
	得     100 = 350 (1-e-160K1)
       300 = 350 (1-e-320K1)
	解得 K1  0.01。代入:340 = 350 (1-e-K1 t ) = 350(1-e-0.01 t )
    t = (ln(35))/0.01  356 (小时)
	因此求得为使MTTF = 10,测试和调试该程序需要花费356小时。

(3) MTTF与测试时间t之间的函数关系:
因为EC (t) = ET (1-e-K1 t ),则ET - EC (t) = ET e-K1 t。代入shooman公式:
【5-13】在对照需求做有效性测试和软件配置审查时,是由软件开发者在开发环境下进行的测试。而接下来做验收测试时则以用户为主。软件开发人员和QA(质量保证)人员也应参加。由用户参加设计测试用例,使用用户界面输入测试数据,并分析测试的输出结果。一般使用生产中的实际数据进行测试。
如果软件是为多个客户开发的,则需要进行α测试和β测试。α测试是由一个用户在开发环境下进行的测试,也可以是公司内部的用户在模拟实际操作环境下进行的测试。软件在一个自然设置状态下使用。开发者坐在用户旁边,随时记下错误情况和使用中的问题。这是在受控制的环境下进行的测试。
β测试是由软件的多个用户在一个或多个用户的实际使用环境下进行的测试。这些用户是与公司签定了支持产品预发行合同的外部客户,他们要求使用该产品,并愿意返回有关错位错误信息给开发者。与α测试不同的是,开发者通常不在测试现场。因而,β测试是在开发者无法控制的环境下进行的软件现场应用。

面向对象的软件工程
例题分析

【例1】请说明下面有关范型的叙述的正确答案。
(1) 问题的解决是基于规则的,它把有关问题的知识分解成一组具体规则,用语言的if_then等结构来表示这些规则。
(2) 问题的解决把软件视为由一系列步骤构成的算法。每一步骤都是带有预定输入和特定输出的一个过程,连贯起来产生合理的稳定的贯通于整个程序的控制流。
(3) 把一个问题分解成独立执行的模块。让不只一个程序(进程)同时运行。这些进程互相配合,解决问题。
(4) 把标识和模型化问题论域中的主要实体做为系统开发的起点,主要考虑对象的行为而不是必须执行的一系列动作。
供选择的答案:
① 面向存取 ② 面向对象 ③ 过程性 ④ 逻辑性 ⑤ 函数型
⑥ 面向进程 ⑦ 说明型 ⑧ 原型
答案:(1) ④ (2) ③ (3) ⑥ (4) ②
分析:逻辑性的范型把有关问题的知识分解成一组具体的规则,这些规则常常是用一种语言的“if_then”结构等来表示。过程性的范型产生过程的抽象,这些抽象把软件视为处理流,定义构成一系列步骤的算法,每一步骤都是带有预定义输入和特定输出的一个过程,把这些步骤串联在一起可产生合理的稳定的贯通于整个程序的控制流。面向对象范型把标识和模型化问题论域中的主要实体做为系统开发的起点,主要考虑对象的行为而不是必须执行的一系列动作。面向对象系统中的对象是数据抽象与过程抽象的一个混合体。表示这些实体的数据抽象是面向对象设计过程的主要产品,系统的状态保存在各个数据抽象的核心所定义的数据存储中。控制流被分成块,并被包括在各个在数据抽象上的各个操作里面。不像在过程性范型里那样,把数据从一个过程传送到另一个过程,而是控制流从一个数据抽象被传送到另一个数据抽象。面向进程的范型把一个问题分解成独立执行的模块。在某种意义上来说,就是让不只一个程序同时运行。这些程序,或更正确地说,这些进程互相配合,从而解决问题。可以认为一个进程在面向进程开发模式中就是一个模块(构件)。面向进程开发模式产生的主要的块是进程。在一个进程中的活动总的来说是独立于其它进程的那些活动的,除非要求从其它进程得到信息,或为其它进程提供信息。甚至可以异步处理,仅需要进程暂停发送或接收信息。在面向对象范型中,各个对象是相对独立的,但也存在单线索控制。面向进程范型支持与面向对象范型相同的封装,但可继续提供多线索(线程)执行。
【例2】对象是面向对象范型的( A )。每个对象可用它自己的一组( B )和它可以执行的一组( C )来表征。应用执行对象的( C )可以改变该对象的( B )。它的应用必须通过( D )的传递。可以认为,这种( D )的传递大致等价于过程性范型中的函数调用。某些语言提供了特殊功能,允许对象引用自己。若一个对象没有显式地被引用,则可让该对象( E )。
供选择的答案:
A. ① 基本单位 ② 最小单位 ③ 最大单位 ④ 语法单位
B C. ① 行为 ② 功能 ③ 操作 ④ 数据 ⑤ 属性
D. ① 接口 ② 消息 ③ 信息 ④ 操作 ⑤ 过程
E. ① 撤消 ② 歇着 ③ 缺省 ④ 隐式引用 ⑤ 引用自己
答案:A. ①, B. ⑤, C. ③, D. ② E. ③
分析:对象是面向对象范型的基本单位。每个对象可用它自己的一组属性和它可以执行的一组操作来表征。应用执行对象的操作可以改变该对象的属性,属性一般只能通过操作来改变。它的应用必须通过消息的传递。发送给一个对象的消息定义了一个方法名和一个参数表(可能是空的),并指定某一个对象。而由一个对象接收的消息则引用消息中指定的方法的代码,并将实际参数与参数表中相应的形式参数结合起来。接收对象对消息的处理可能会改变对象中的状态,即改变接收对象的属性,并发送一个消息给自己或另一个对象。可以认为,这种消息的传递大致等价于过程性范型中的函数调用。然而,执行方法得到消息结果的目的是想修改相关对象的内部状态,而不只是修改变元并返回它们。某些语言提供了特殊功能,像smalltalk提供了self,允许对象引用自己。若一个对象没有显式地被引用,则可让该对象缺省。
【例3】在面向对象软件开发过程中特别重视复用。软件构件应独立于当初开发它们的应用而存在。在以后的应用开发中,可以调整这些独立构件以适应新问题的需要。因此,应使得类成为一个( A )的单元。这样就有一个( B )生存期问题。( B )生存期有自己的步骤,与任一特定应用的开发( C )。按照这些步骤,可以完整地描述一个基本( D )。而不仅仅考虑当前正在开发的系统。系统开发的各个阶段都可能会标识新的类。随着各个新类的标识,( B )生存期引导开发工作逐个阶段循序渐进。
在设计与实现类时,应尽可能利用既存类提供为当前应用所需要的功能,利用既存类的三个可能途径是:( E )复用既存类;对既存类进行( F )以得到满足要求的类;重新开始进行开发。
供选择的答案:
A. ① 可复用 ② 可测试 ③ 可适用 ④ 可靠
B. ① 应用 ② 寿命 ③ 类 ④ 软件
C. ① 相关 ② 密切相关 ③ 负相关 ④ 无关
D. ① 概念 ② 实体 ③ 事件 ④ 事情
E, F. ① 修改 ② 更新 ③ 照原样 ④ 演化
答案:A. ①, B. ③, C. ④, D. ②, E. ③, F. ④
分析:在面向对象软件开发过程中特别重视复用。软件构件应独立于当初开发它们的应用而存在。构件的开发瞄准某些局部的设计和实现,它们可用于当前问题的解决,但为了在以后的项目中使用,它们还应当足够通用。在以后的应用开发中,可以调整这些独立构件以适应新问题的需要。为使得类成为一个可复用的单元,有一个类生存期的问题。
类生存期与应用生存期交叉。在应用生存期的每一个阶段都可做类的标识。类生存期有自己的步骤,与任一特定应用的开发无关。按照这些步骤,可以完整地描述一个基本实体。而不仅仅考虑当前正在开发的系统。系统开发的各个阶段都可能会标识新的类。随着各个新类的标识,类生存期引导开发工作逐个阶段循序渐进。
在设计与实现类的时候,有三种利用既存类的途径:
原封不动地复用既存类。
对既存类进行演化以得到满足要求的类。
重新开始进行开发。
【例4】类常常被看做是一个抽象数据类型的实现,更合适的是把类看做是某种( A )的一个模型。事实上,类是单个的( B )语义单元。类的用户能够操纵的操作叫做类的( C )。类定义的其余部分给出数据定义和辅助功能定义,包括类的实现。
类的实现常常包括了其它类的实例,这些实例( D )被其它对象存取,包括同一个类的其它实例。类的实现可能还包括某些私有方法,实现它们的类可以使用,而其它任何对象都不能使用。
类,就它是一个数据值的聚合的意义上来看,与Pascal中的记录或C中的结构类似,但又有差别。类扩展了通常的记录语义,可提供各种级别的( E )。类不同于记录,因为它们包括了操作的定义,这些操作与类中声明的数据值有相同的地位。
供选择的答案:
A. ① 功能 ② 概念 ③ 结构 ④ 数据
B. ① 语法 ② 词法 ③ 语义 ④ 上下文环境
C. ① 界面 ② 操作 ③ 行为 ④ 活动
D. ① 可自由地 ② 可有控制地 ③ 可通过继承 ④ 应受保护不
E. ① 可移植性 ② 可重复性 ③ 可访问性 ④ 继承性
答案:A. ②, B. ③, C. ①, D. ④, E. ③
分析:类常常被看做是一个抽象数据类型的实现。这个定义不太够,更合适的是把类看做是某种概念的一个模型。事实上,类是单个的语义单元,它可以更自然地管理系统中的对象,匹配数据定义与操作。许多面向对象范型的语言都提供数据抽象机制。这个机制为类定义提供了一个手段,以指明类的用户能够操纵的操作。这组操作叫做类的界面。类定义的其余部分给出数据定义和辅助功能定义,包括类的实现。这种分离把类的用户与类内部修改的影响隔离开来。类的实现常常包括了其它类的实例,它们往往提供了新类所需要的服务。这些实例应受保护不被其它对象存取,包括同一个类的其它实例。类的实现可能还包括某些私有方法,实现它们的类可以使用,而其它任何对象都不能使用。
类,就它是一个数据值的聚合的意义上来看,与Pascal中的记录或C中的结构类似,但又有差别。类扩展了通常的记录语义,可提供各种级别的可访问性。也就是说,记录的某些成份可能是不可访问的,而这些成份对于本记录型来说具有可访问性。类不同于记录,因为它们包括了操作的定义,这些操作与类中声明的数据值有相同的地位。
【例5】 有一种非形式的技术,对于捕获信息有时很有用,它就是CRC卡片。CRC是( A )、( B )和( C )的缩写。它可以用来组织在每一个子系统中的类。以CRC卡片为辅助工具的设计有以下几个步骤:识别( A )和( B ),分配( B ),找寻( C ), 细化。CRC的作者强调模拟在执行每个基本功能时系统内部出现的( D ),以此推动细化工作的进行。在这个过程中,CRC卡片是十分重要的一个工具。
用CRC卡片来进行设计,既不是传统的“自上而下”,也不是“自下而上”,而是从已知到未知的逐步( E )的过程。
供选择的答案:
A C. ① 控制 ② 协作者 ③ 可靠性 ④ 类
⑤ 计算 ⑥ 职责 ⑦ 比较 ⑧ 上下文环境
D. ① 场景 ② 算法 ③ 进程 ④ 变换
E. ① 演化 ② 进展 ③ 展开 ④ 认识
答案:A. ④, B. ⑥, C. ②, D. ①, E. ③
分析:CRC是Class(类)、Responsibility(职责)和Collaborators(协作者)的缩写。这个技术是由Beck等开发的,在标识实体和定义界面的行为时在小组对话中使用。
由于它的简单有效,以后逐渐成为职责驱动方法的一部分,并结合到其它的面向对象方法中去。CRC卡片的特点是用人格化的方法,将软件系统中的每个部件,即类,看成一个独立的个体,在探索如何与其它个体携手合作完成某一系统功能中逐步完成自身的定位。为了便于分组,查找,以及修改。每张卡片分为三个部分,分别记录一个类的名称,它的主要职责和协作者。以CRC卡片为辅助工具的设计有以下几个步骤:
(1) 识别类和职责:CRC卡片的作者建议使用自然语言的分析方法(LIA),从用户需求规格说明书中的名词,物理和概念的实体中发现有用的类。引进适当的基类或子类,以形成继承关系的层次结构。然后,从用户的需求说明书中寻找对有关的信息和行为的描述,以发现职责。职责就是需做的事,必须完成的任务,后要解决的问题,但不涉及如何实现。
(2) 分配职责:将职责分配到类,并记录在相应的卡片上。CRC的作者指出应尽可能确保行为与有关信息不要分开,与同一实体的信息要集中在一起。如有必要,有些职责可以由几个类共同承担。
(3) 找寻协作者:任何一个类在完成自己的职责时往往都需要其它类的协作。这一步就是要找到与每一个类协作的伙伴,并记录在相应的卡片上。具体做法是依次检查每一类所承担的每一项责任,看是否需要其它类的帮助来完成。如果需要,进一步确定需要其做什么。
(4) 细化:CRC的作者强调模拟在执行每个基本功能时系统内部出现的场景,以此推动细化工作的进行。
通常在模拟一个场景的过程中,每当一个类开始“执行”时,它的卡片就被拿出来加以讨论,当“控制”传送到另一个类时,我们的注意力就从前一张卡片转移到另一张上去了。不同的场景,包括例外和出错状况,都应逐一加以模拟。在这个过程中,可以验证已有的定义,不断发现新的类、职责以及伙伴。
用CRC卡片来进行设计,既不是传统的“自上而下”,也不是“自下而上”,而是从已知到未知的逐步展开的过程。场景模拟有助于以试验性的方式来从事设计。由于检索卡片灵活、易于修改,不同的设计方案可以很快地形成,并进行检验。
【例6】论域分析的( A )和对应用分析和高层设计的( B )就构成问题论域的模型。已有许多建立这种模型的技术,一种特别适用的技术就是语义数据模型。
语义数据模型来源于Codd的( C )数据模型和实体—联系模型,并对这类模型进行了扩充和一般化。语义数据模型可以表达问题论域的内涵,还可以表示复杂对象和对象之间的联系。语义数据模型与( C )数据模型本来都是在( D )设计时使用的,但它们的范围已经扩展到系统的开发。作为( D )结构标准的ANSI/SPARC建议提出了三层模型:外部模型、概念模型和( E )模型。这三层可以被映象到面向对象设计的三个层次上去。外部模型与概念模型层相当于高层设计阶段。
供选择的答案:
A, B. ① 控制 ② 输出 ③ 输入 ④ 处理
⑤ 计算 ⑥ 解释 ⑦ 比较 ⑧ 创建
C. ① 变换 ② 关系 ③ 抽象 ④ 事务
D. ① 网络 ② 程序 ③ 算法 ④ 数据库
E. ① 低层 ② 底层 ③ 内部 ④ 存储
答案:A. ③, B. ②, C. ②, D. ④, E. ③
分析:论域分析的输出和对应用分析和高层设计的输入就构成问题论域的模型。一种特别适用的技术就是语义数据模型。语义数据模型来源于Codd的关系数据模型和实体—联系模型,并对这类模型进行了扩充和一般化。语义数据模型可以表达问题论域的内涵,还可以表示复杂对象和对象之间的联系。语义数据模型与关系数据模型本来都是在数据库设计时使用的,但它们的范围已经扩展到系统的开发。作为数据库结构标准的ANSI/SPARC建议提出了三层模型:外部模型、概念模型和内部模型。这三层可以被映象到面向对象设计的三个层次上去,如表所示。外部模型与概念模型层相当于高层设计阶段。
语义数据模型 主 要 特 征 面向对象设计
外部模型 数据的用户视图 类的定义(规格说明)
概念模型 实体及其之间联系的内涵 类之间的应用联系
内部模型 数据的物理模型 类的实现
(1) 外部模型层。外部模型是来自应用的外部现实世界的视图,它反映了用户对问题的理解而不是实现者对问题的理解。在这一层开发的类应具有对应于用户活动的操作规格说明。
(2) 概念模型层。概念模型层考虑在外部模型层所标识的实体之间的联系。这些联系就是可直接观察到的交互关系。在这一层,通常可以由系统的用户来识别和理解这些联系。
联系的重要属性是实例连接。一个联系的实例连接是指在该联系中一个实体的实例对应于该联系中其它实体的实例的数目。
(3) 内部模型层。这一层考虑实体的物理模型,这是我们生存期中的类设计阶段。物理模型包括两类属性:数据和方法。方法属性对实体的行为模型化,而数据属性对实体的状态模型化。在模型中方法分为两种:一种可作为共有界面来使用,而另一种是私有的。
【例7】从分析到设计的过程流如图所示。一旦已经开发完成一个合理完整的( A )模型后,就要着手( B )的设计。这需要描述( B )的特征,以准确表达待实现的用户需求,以及实现需求所必须的支持环境。一旦定义了各个( B ),就开始( C )设计,这时,可利用CRC卡片,将属性转换为( D ),将关系转换为( E )。
供选择的答案:
A C. ① 分析 ② 系统设计 ③ 模块设计 ④ 子系统设计
⑤ 对象设计 ⑥ 数据设计 ⑦ 操作设计 ⑧ 行为设计
D E. ① 对象 ② 数据结构 ③ 算法 ④ 消息传递
⑤ 控制 ⑥ 并发处理 ⑦ 进程 ⑧ 过程
答案:A. ①, B. ④, C. ⑤, D. ②, E. ④
分析:从分析到设计的过程流如图所示。一旦已经开发完成一个合理完整的分析模型后,就要着手子系统的设计。这需要描述子系统的特征,以准确表达待实现的用户需求,以及实现需求所必须的支持环境。
在定义子系统时,需要在用户需求的整个上下文环境中互相协调:各个用户需求分配给哪个子系统?在OOA中定义的对象驻留在哪个子系统内?哪些子系统并发运行?负责协调和控制它们的系统构件是谁?全局资源在子系统中如何管理?等。
在设计子系统的过程中,需要定义4种重要的设计构件:
问题论域:负责实现用户需求的子系统。
人机交互:实现用户界面的子系统(包括可复用的GUI子系统)。
任务管理:负责控制和协调各种事件驱动、时钟驱动、并发执行任务的子系统。
数据管理:负责对象存储和检索的子系统。
每一个构件都可以用一些类、必须的关系和行为来定义。构件之间的关系可以通过建立每个构件的消息模型来建立。
一旦定义了各个子系统,包括上述的各个设计构件,就开始对象(类)设计,这时,可利用CRC卡片,将类转换为设计实现,转换工作如图所示。

习题

【6-1】 什么叫面向对象?面向对象方法的特点是什么?为什么要用面向对象方法开发软件?
【6-2】什么是“对象”?识别对象时将潜在对象分成7类,试给出这7类对象的名称,并举例说明。
【6-3】什么是“类”? “类”与传统的数据类型有什么关系?有什么区别?
【6-4】基于复用的面向对象开发过程分为哪几个阶段?每一个阶段需要做哪些事情?
【6-5】按照类生存期,类的开发有哪几种方式?每一种方式需要做哪些事情?
【6-6】面向对象开发方法与面向数据流的结构化开发方法有什么不同?使用面向对象开发方法的优点在什么地方?
【6-7】面向对象的程序设计语言具有数据抽象、信息隐蔽、( A )等特征。作为运算单位的对象应具有下列特性:( B )、( C )、( D )。( E )是面向对象的语言。
供选择的答案:
A:① 对象调用 ② 对象变换 ③ 非过程性
④ 信息继承 ⑤ 并发性
B D:① 对象把数据和处理数据的操作结合为一体
② 在程序运行时对象都处于活动状态
③ 对象在计算中可向其他对象发送消息
④ 接受消息的对象必须给消息发送者以回答
⑤ 对象的内部状态只根据外部送来的消息才操作
E: ① C++,SMALLTALK,objectC ② C,Ada,Modula2
③ PASCAL,C++,APL ④ Ada,objectC,C
【6-8】基于复用的面向对象的需求分析过程主要分为两个阶段:论域分析和应用分析。试讨论它们各自承担什么任务?如何衔接?
【6-9】 建立分析和设计模型的一种重要方法是UML。试问UML是一种什么样的建模方法?它如何表示一个系统?
【6-10】使用面向对象设计方法进行高层设计,以建立系统的体系结构。这样的结构有哪几种主要的组成部分?每一部分所承担的职责是什么?
【6-11】由RumBaugh等人提出的一种面向对象方法叫做对象模型化技术(OMT),即三视点技术,它要求把分析时收集的信息建立在三个模型中。第一个模型是( A ),它的作用是描述系统的静态结构,包括构成系统的对象和类,它们的属性和操作,以及它们之间的联系。第二个模型是( B ),它描述系统的控制逻辑,主要涉及系统中各个对象和类的时序及变化状况。( B )包括两种图, 即( C )和( D )。( C )描述每一类对象的行为,( D )描述发生于系统执行过程中的某一特定场景。第三个模型是( E ),它着重于描述系统内部数据的传送与处理,它由多个数据流图组成。
供选择的答案:
A, B, E:① 数据模型 ② 功能模型 ③ 行为模型 ④ 信息模型
⑤ 原型 ⑥ 动态模型 ⑦ 对象模型 ⑧ 逻辑模型
⑨ 控制模型 ⑩ 仿真模型
C, D: ① 对象图 ② 概念模型图 ③ 状态迁移图 ④ 数据流程图
⑤ 时序图 ⑥ 事件追踪图 ⑦ 控制流程图 ⑧ 逻辑模拟图 ⑨ 仿真图 ⑩ 行为图
【6-12】在类的设计中需要遵循的方针是什么?三个主要的设计准则:抽象、信息隐蔽和模块化如何才能作到?
【6-13】在面向对象软件设计过程中,应按如下要求进行类的设计:只有类的共有界面的成员才能成为使用类的操作,这就是软件设计的( A )原则。当且仅当一个操作对类的实例的用户有用时,它才是类公共界面的一个成员,这是软件设计的( B )原则。由同属一个类的操作负担存取或加工类的数据,这是软件设计的( C )原则。两个类之间的交互应当仅涉及参数表,这是软件设计的( D )原则。每个派生类应该当做基类的特殊化来开发,而基类所具有的公共界面成为派生类的共有界面的一个子集,这是软件设计的( E )原则。
供选择的答案:
A:① 过程抽象 ② 功能抽象 ③ 信息隐蔽 ④ 共享性 ⑤ 连通性
B:① 标准调用 ② 最小界面 ③ 高耦合 ④ 高效率 ⑤ 可读性
C:① 数据抽象 ② 低内聚 ③ 高内聚 ④ 低复杂度 ⑤ 低强度
D:① 显式信息传递 ② 高内聚 ③ 低内聚 ④ 相互操作性 ⑤ 连接性
E: ① 动态联编 ② 异质表 ③ 信息隐蔽 ④ 多态性 ⑤ 继承性
【6-14】在类的通过复用的设计中,主要的继承关系有哪几种?试举例说明。

习题解答

【6-1】关于“面向对象”,有许多不同的看法。Coad和Yourdon给出了一个定义:“面向对象 = 对象 + 类 + 继承 + 消息通信”。如果一个软件系统是使用这样4个概念设计和实现的,则认为这个软件系统是面向对象的。面向对象方法的特点是:
方法的唯一性,即方法是对软件开发过程所有阶段进行综合考虑而得到的。
从生存期的一个阶段到下一个阶段的高度连续性,即生存期后一阶段的成果只是在前一阶段成果的补充和修改。
把面向对象分析(OOA)、面向对象设计(OOD)和面向对象程序设计(OOP)集成到生存期的相应阶段。
使用面向对象方法开发软件的好处是:
开发方法的唯一性,开发阶段的高度连续性,表示方式的一致性;
问题空间实体的自然表示,减轻了设计者的负担,在设计系统之初不必考虑一个很完整的解决方案。
建立稳定的系统结构,可促进复用性,易于维护,易于修改,可合理利用共同性,减少复杂性。
【6-2】对象的定义:
对象是面向对象开发模式的基本成分,是现实世界中个体或事物的抽象表示。
每个对象可由一组属性和它可以执行的一组操作来定义。
可能的潜在对象有7类:
外部实体:它们产生或接受为目标系统所使用的信息。如各种物理设备、使用人员、其它相关的子系统。
事物:问题的信息域所涉及的概念实体。如各种报告、显示、文字、信号、规格说明等。
事件:系统运行时发生的并需要系统记忆的事件。如状态转换、物理运动等。
角色:与系统有交互的各种人员所扮演的角色。如经理、工程师、销售人员等。
场所或位置:建立系统整体环境或问题上下文的场所、位置。如基于计算机的系统的安装场所等。
组织机构:与应用有关的组织机构。如组织,部门等。
结构:定义由一组成分对象组成的聚合对象,或在极端情况下,定义对象的相关类。如传感器、四轮驱动车、计算机等。
【6-3】把具有相同特征和行为的对象归在一起就形成了类。类成为某些对象的模板,抽象地描述了属于该类的全部对象的属性和操作。属于某个类的对象叫做该类的实例。对象的状态则包含在它的实例变量,即实例的属性中。类定义了各个实例所共有的结构,类的每一个实例都可以使用类中定义的操作。实例的当前状态是由实例所执行的操作定义的。
类,就它是一个数据值的聚合的意义上来看,与Pascal中的记录或C中的结构类似,但又有差别。类扩展了通常的记录语义,可提供各种级别的可访问性。也就是说,记录的某些成份可能是不可访问的,而这些成份对于本记录型来说具有可访问性。类不同于记录,因为它们包括了操作的定义,这些操作与类中声明的数据值有相同的地位。

【6-4】基于复用的面向对象开发过程分为6个阶段,如右图中虚线框所围。
① 论域分析 :论域分析开发问题论域的模型。论域分析应当在应用分析之前进行,我们在了解问题之前应当对问题敞开思想考虑,考察问题论域内的一个较宽的范围,分析覆盖的范围应比直接要解决的问题更多。
② 应用分析 :应用(或系统)分析细化在论域分析阶段所开发出来的信息,并且把注意力集中于当前要解决的问题。因为通过论域分析,分析人员具有了较宽的论域知识,因而能开发出更好的抽象。
③ 高层设计 :在一个纯面向对象环境中,软件体系结构设计与类设计常常是同样的过程,但还是应当把体系结构设计与类的设计分开。在高层设计阶段,设计应用系统的顶层视图。这相当于开发一个代表系统的类,通过建立该类的一个实例并发送一个消息给它来完成系统的“执行”。
④ 类的开发 :根据高层设计所标识的对各个类的要求和类的规格说明,进行类的开发。因为一个应用系统往往是一个类的继承层次。对这些类的开发是最基本的设计活动。
⑤ 实例的建立:建立各个对象的实例,实现问题的解决方案。
⑥ 组装测试:按照类与类之间的关系组装一个完整的应用系统的过程中进行的测试。各个类的封装和类测试的完备性可减少组装测试所需要的时间。
【6-5】按照右图所示的类生存期,类的开发有三种方式。
(1) 既存类的复用
只要有可能就应复用既存类。为了达到此目的,开发人员必须能够找到这样一些类,它们都能选用来提供所需要的行为。有时应用要解决的问题与以前遇到的一些问题密切相关,因此那些问题中定义和实现的类可以复用。然而,多数照原样复用被限制在低层上最基本的类,像基本数据结构。对于较一般的结构,可以在实例化时,使用参数来规定它们的行为。
(2) 从既存类进行演化
多数复用情况是一个类已经存在,它提供的行为类似于要为新类定义的行为。开发人员可以使用既存类做为定义新类的起点。新类将根据既存类渐进式地演变而成。这样,在开发一个新类时,只需要花费较少的工作量就能复用许多既存类,得到所需要的新类。演化可以是横向的,也可以是纵向的。横向的演化导致既存类的一个新的版本,而纵向的演化将从既存类导出新类。我们在这里将主要讨论类的纵向渐进式开发。
① 渐进式设计。设计既存类的一个特殊化类。通过确定新类中打算要的所有成员,设计者可以确定哪些追加的行为可以加到类中去,哪些既存的行为应当重新实现。
② 渐进式实现。许多实现可以从既存类直接继承;有时可以仅使用很少的新代码就能利用既存类的实现,而这些新代码必须当做老方法的上文或下文进行编写。此外,在渐进式设计阶段增加的那些行为也必须实现。
③ 渐进式测试。在测试中最花费时间的就是测试用例的生成。许多新类的测试用例可以从既存类的测试用例组中得到。新类的某些部分因为在测试既存类时已经测试过,因此可以不再需要测试。
(3) 从废弃型进行开发
这个分支仅在不得已的情况下使用。任何一个类,只要它的开发不涉及既存类,就可看做是一个新的继承结构的开始。因此,将建立两种类:一种是抽象类,它概括了将要表达的概念;另一种是具体类,它要实现这个概念。
① 设计。设计阶段需把分析阶段所产生的界面当做输入,并确定类的其它属性。设计给出类的所有细节。这个阶段的输出是有关类的属性的足够的细节,可支持它们的实现。单个类的设计包括构造数据存储,它是类定义的核心。其内部表示还包括一些私有函数,它们实现了共有操作的某些部分。单个类的低层设计还涉及一些重要联系,如继承和组装关系。
② 实现。通过变量的声明、操作界面的实现及支持界面操作的函数的实现,可实现一个类的预期行为和状态。在变量中存储的数据通常是其它类的实例,它们提供了为该类的开发所需的服务。
③ 测试。单个的类为测试提供了自然的单元。如果类的定义提供的界面比较狭窄,那么穷举测试就有可能实现。类的测试在最抽象的层次开始,沿继承联系继续向下进行,新的类可以很容易地完全地被测试,而已经测试过的部分就不需要从新测试了。
(4) 求精和维护
传统的维护活动是针对应用的,而求精过程则是针对类,并把类链接在一起的结构的。因为我们利用抽象进行开发,因此,维护部分在任一时间都能修改这些抽象。随着经验的增长,我们可以够标识抽象的抽象,使得继承结构通过泛化增加新的层次,即在既存的根类之上增加新的层次。
【6-6】结构化开发方法是使用最广泛、历史最长的过程化开发方法。结构化开发方法产生过程的抽象,这些抽象把软件视为处理流,定义构成一系列步骤的算法,每一步骤都是带有预定义输入和特定输出的一个过程,把这些步骤串联在一起可产生合理的稳定的贯通于整个程序的控制流。这将最终导致一个很简单的具有静态结构的体系结构。
在结构化开发方法中,数据结构是应算法步骤的要求而开发的。数据结构贯穿于过程,提供过程需要传送给它的操作的信息。系统的状态是一组全局变量,这组全局变量保持了状态的值,把它们从一个过程传送到另一个过程。
结构化开发方法是一种成熟的应用开发过程。对这种方法已经存在许多支持。然而,在大型系统的开发上和在面向用户系统的构造上存在一些问题。改进大型系统开发的技术主要集中在开发数据抽象。日益增多的考虑是使用抽象数据类型,把过程化系统开发过程包括到数据驱动的方法中。随着大型系统的开发,接踵而来的问题就是要把过程抽象与数据抽象方法组合起来,这种需要导致了面向对象开发方法的诞生。
面向对象开发方法是我们分解问题所使用方法演化的结果。在结构化开发方法中过程抽象是优先的,而面向对象开发方法中优先的是实体,即问题论域的对象。在面向对象开发方法中,把标识和模型化问题论域中的主要实体做为系统开发的起点,主要考虑对象的行为而不是必须执行的一系列动作。
面向对象系统中的对象是数据抽象与过程抽象的一个混合体。表示这些实体的数据抽象是面向对象设计过程的主要产品,系统的状态保存在各个数据抽象的核心所定义的数据存储中。控制流被分成块,并被包括在各个在数据抽象上的各个操作里面。不像在结构化开发方法里那样,把数据从一个过程传送到另一个过程,而是控制流从一个数据抽象被传送到另一个数据抽象。完成的系统体系结构更复杂但也更灵活。在块中分离的控制流允许把复杂的动作视为局部的相互影响。
【6-7】A. ④ B. ① C. ③ D. ④ E. ① 其中,B、C、D的答案可互换
面向对象的程序设计语言应具备面向对象方法所要求的4个成分:类、对象、继承和消息通信。类与对象由数据抽象和信息隐蔽得到,此外语言应具有信息继承的机制。
对象由一组属性和它可以执行的一组操作来定义。面向对象的软件系统通过对象间的消息通信和对象执行消息所要求的服务完成系统预定的功能。所以,对象在计算中可向其他对象发送消息,接受消息的对象必须通过响应消息∕执行服务,给消息发送者以回答。
C++,Smalltalk,object C是面向对象的程序设计语言,Ada、Modula2是基于对象的程序设计语言,因为它缺少继承的机制,而Pascal,C,APL等都不是面向对象或基于对象的程序设计语言。
【6-8】论域分析是软件开发方法中一个基本组成部分,它给出的一组抽象是论域的知识的高层表示,用来做为特定系统需求开发的参考。由于分析常常是在超出当前应用的范围进行的,所以这个技术仅在扩充当前系统或将来建立其它系统时找寻复用信息时才是实用的。
论域分析是对与应用开发问题有关的范围十分广泛的知识的学习。论域的边界是模糊的,它们很多是凭借经验和实际考虑(如可用资源)定义的。主要思想是想把考虑的领域放宽一些,把相关的概念都标识到,以帮助更好地掌握应用的核心知识。当用户改变他们对系统需求的想法时,范围广泛的分析可以帮助预测这些变化,并不再需要进一步的分析。
论域分析的目的是标识基本概念,识别论域的特征,把这些概念集成到论域的模型中。这个模型必须包含概念之间的关系和关于每个单独概念的完全信息。这个信息起着一种胶合作用,把所有相关概念并入论域综合视图中去。
论域分析是一个持续的活动,它可以在软件开发的任一时刻进行标识,它还是一个可在超出特定应用的开发范围时仍然能够正常继续的处理。当论域变化时,必须更新那些抽象和关系。论域分析的结果使得我们可积累更多的有关论域的经验和细化我们的抽象。
使用论域抽象做为系统开发基础的好处是可适应性,客户可能改变需求,而且问题环境也可能改变。基于论域信息的应用更容易适应论域内知识的改变和用户需求的变化。
论域分析是一个长期的投资。如果在初始开发时需求发生变化,它可能有更直接的效益;在很大的项目上可能更快地得到回报。最大的价值是抽象的开发,这些抽象表示了一个问题论域中的基本概念,它们形成的软件库还可支持许多应用的开发。
应用(或系统)分析细化在论域分析阶段所开发出来的信息,并且把注意力集中于要解决的精确的问题。应当把客户对系统的需求当做限制来使用,它们缩减了论域的信息量,而这种信息将是主动设计过程的一部分。就这一点来说,保留的信息受到论域分析视野的影响。论域分析产生的模型并不需要用任何基于计算机系统的程序设计语言来表示,而应用分析阶段产生影响的条件则伴随着某种基于计算机系统的程序设计语言的表示。响应时间需求、用户界面需求和某些特殊的需求,如数据安全等,在这一层都被分解提取。
许多模型识别的要求是针对不止一个应用的。通常我们着重考虑两个方面:应用视图和类视图。必须对每个类的规格说明和操作详细化,还必须对形成应用结构的类之间的相互作用加以表示。
【6-9】 UML叫做统一的建模语言,它把Booch、Rumbaugh和Jacobson等各自独立的OOA和OOD方法中最优秀的特色组合成一个统一的方法。UML允许软件工程师使用由一组语法的语义的实用的规则支配的符号来表示分析模型。
在UML中用5种不同的视图来表示一个系统,这些视图从不同的侧面描述系统。每一个视图由一组图形来定义。这些视图概述如下:
用户模型视图 :这个视图从用户(在UML中叫做参与者)角度来表示系统。它用使用实例(use case)来建立模型,并用它来描述来自终端用户方面的可用的场景。
结构模型视图 :从系统内部来看数据和功能性。即对静态结构(类、对象和关系)模型化。
行为模型视图 :这种视图表示了系统动态和行为。它还描述了在用户模型视图和结构模型视图中所描述的各种结构元素之间的交互和协作。
实现模型视图 :将系统的结构和行为表达成为易于转换为实现的方式。
环境模型视图 :表示系统实现环境的结构和行为。
通常,UML分析建模的注意力放在系统的用户模型和结构模型视图,而UML设计建模则定位在行为模型、实现模型和环境模型。
【6-10】一个典型的高层设计是在Smalltalk中使用的MVC软件体系结构,即模型/视图/控制器(Model/View/Controller)。在这个结构中,模型是软件中的应用论域的各种对象,它们的操作独立于用户界面;视图则管理用户界面的输出;而控制器处理软件的输入。输入事件给出要发送给模型的消息。一旦模型改变了它的状态,就立即通过关联机制通知视图,让视图刷新显示。这个关联机制定义了在模型与各个视图之间的关系,它允许模型的运行独立于与它相关联的视图。类似地,控制器在输入事件发生时将对视图及模型进行控制与调度。
另一个典型的高层设计是Coad与Yourdon提出的OOD模型。这个模型也有 5 层结构,又被划分成了 4 个组成部分:问题论域、用户界面、任务管理和数据管理。

问题论域部分的职责是:组合所有的论域中定义的类,为应用设计适当的类层次,为适应语言要求简化继承层次,细化设计以改善性能,开发与数据管理部分的接口,做细化时加入低层对象,评审设计并审查对分析模型所做的增补。
人机交互部分的职责是:定义用户类型,开发任务场景,设计用户命令层次,建立原型以细化与用户交互,设计相关的类和类层次,适当时集成GUI类。
任务管理部分的职责是:标识任务的类型(如事件驱动,时钟驱动),建立优先级,标识协调者,为每个任务设计适当的类。
数据管理部分的职责是:设计数据结构和布局,设计管理数据结构所需的服务,标识可以协助实现数据管理的工具,设计适当的类和类层次。
【6-11】A. ⑦ B. ⑥ C. ③ D. ⑥ E. ②
在OMT中,把分析时收集的信息建立在三个模型中。第一个模型是对象模型,它的作用是描述系统的静态结构,包括构成系统的对象和类,它们的属性和操作,以及它们之间的联系。第二个模型是动态模型,它描述系统的控制逻辑,主要涉及系统中各个对象和类的时序及变化状况。动态模型包括两种图, 即状态迁移图和事件追踪图。状态迁移图描述每一类对象的行为,事件追踪图描述发生于系统执行过程中的某一特定场景。第三个模型是功能模型,它着重于描述系统内部数据的传送与处理,它由多个数据流图组成。
【6-12】在设计类时需要遵循的方针是:
信息隐蔽:通过信息隐蔽可保护类的存储表示不被其它类的实例直接存取。
消息限制:该类实例的用户应当只能使用界面提供的操作。
狭窄界面:只有对其它类的实例是必要的操作才放到界面上。
强内聚:模块内部各个部分之间应有较强的关系,它们不能分别标识。
弱耦合:一个单独模块应尽量不依赖于其它模块。
显式信息传递:两个类之间的交互应当仅涉及显式信息传递。
派生类当做派生类型:每个派生类应该当做基类的特殊化来开发,而基类所具有的公共界面成为派生类的共有界面的一个子集。
抽象类:某些语言提供了一个类,用它做为继承结构的开始点,所有用户定义的类都直接或间接以这个类为基类。
为了在类的设计中做到抽象、信息隐蔽和模块化:
以类作为系统的基本模块单元,通过一般化―特殊化关系和整体―部分关系,搭建整个系统的类层次结构,实现数据抽象和过程抽象;
将数据和相关的操作封装在类内部,建立共有、私有和子类型等存取级别,将数据表示定义成为类的私有成员,实现信息隐蔽。
通过建立类属性(类模板),将某些有可复用要求的类设计成在数据类型上通用的可复用的软件构件,这样有助于实现模块化。
【6-13】A. ③ B. ② C. ③ D. ① E. ⑤
在面向对象软件设计过程中,应按如下要求进行类的设计:只有类的共有界面的成员才能成为使用类的操作,这就是软件设计的信息隐蔽原则。当且仅当一个操作对类的实例的用户有用时,它才是类公共界面的一个成员,这是软件设计的最小界面原则。由同属一个类的操作负担存取或加工类的数据,这是软件设计的高内聚原则。两个类之间的交互应当仅涉及参数表,这是软件设计的显式信息传递原则。每个派生类应该当做基类的特殊化来开发,而基类所具有的公共界面成为派生类的共有界面的一个子集,这是软件设计的继承性原则。
【6-14】在类的通过复用的设计中,主要的继承关系有两大类:
① 配置:利用既存类来设计类,可能会要求由既存类的实例提供类的某些特性。通过把相应类的实例声明为新类的属性来配置新类。例如,一种仿真服务器可能要求使用一个计时器来跟踪服务时间。设计者不必开发在这个行为中所需的数据和操作,而是应当找到计时器类,并在服务器类的定义中声明它。
但如果使用既存类的内部表示来做为新类的内部表示的一部分,这是一种“针对实现”的继承方式,这种继承方式不好。例如,考虑使用继承来实现一个Circle类。Point类可支持Circle类的一部分实现。为了定义一个圆,我们只需要定义一个点和一个值,做为圆的圆心和半径。把Point当做子类,Circle类不但能得到由x和y提供的圆心,而且还能得到一个操作,让圆能够自由移动。但这样做,我们失去了抽象。
② 演变 :要开发的新类可能与一个既存类非常类似,但不完全相同。此时可以从一个既存类演变成一个新类,可以利用继承机制来表示一般化―特殊化的关系。特殊化处理有三种可能的方式。

如果新的概念是一个既存类所表示概念的一个特殊情况,特殊化运算可以从该既存类的定义产生新类的初始构造,这是典型的类继承的使用。既存类A的数据结构和操作可以成为新类B的一部分,如图(a)所示。既存类A的公共操作成为新类B的共有界面部分。
如果新类比软件库中那些既存类更一般,则新类B不具有既存类A的全部特性,一般化运算把两个类中共同的特性移到新的更高层的类中,高一层的类是B,我们将要设计它。原来的类A成为新类B的子类。如图(b)所示。
一个既存类A与我们设计的新类B共享概念的某一个部分,则两个概念的共同部分形成新类的基础,且既存类与新类两者成为子类,如图©所示。

软件质量、维护

例题分析

【例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)生成源程序代码,就是正向工程的活动。

软件管理

例题分析

【例1】由于软件工程有如下的特点,使软件管理比其它工程的管理更为困难。软件产品( A )。( B )标准的过程。大型软件项目往往是( C )项目。( D )的作用是为有效地定量地进行管理,把握软件工程过程的实际情况和它所产生的产品质量。在制定计划时,应当对人力、项目持续时间、成本作出( E );( H )实际上就是贯穿于软件工程过程中一系列风险管理步骤。最后,每一个软件项目都要制定一个( F ),一旦( G )制定出来,就可以开始着手( H )。
供选择的答案:
A C. ① 可见的 ② 不可见的 ③ “一次性” ④ “多次”
⑤ 存在 ⑥ 不存在
D H. ① 进度安排 ② 度量 ③ 风险分析 ④ 估算
⑤ 追踪和控制 ⑥ 开发计划
答案:A. ②, B. ⑥, C. ③, D. ②, E. ④, F. ①, G. ⑥, H. ⑤。
分析:由于软件工程有如下的特点:软件产品不可见;不存在标准的软件过程;大型软件项目往往是“一次性”的项目,使得软件得管理比其它工程的管理更为困难;通常,软件人员和用户确定了软件项目的目标和范围之后,度量的作用就是为了有效地定量地进行管理。对开发过程进行度量的目的是为了改进开发过程,而对产品进行度量的目的是为了提高产品的质量。在软件项目管理过程中一个关键的活动是制定计划。在做计划时,必须就需要的人力、项目持续时间、成本作出估算;风险分析对于软件项目管理是决定性的,它实际上就是贯穿于软件工程过程中一系列风险管理步骤,其中包括风险识别、风险估计、风险评价和风险驾驭等步骤。每一个软件项目都要制定一个进度安排,但不是所有的进度都要一样地安排。一旦制定了开发计划,就可以开始着手追踪和控制活动。
【例2】在软件项目估算时,将代码行LOC和功能点FP数据在两个方面使用:一是作为一个估算变量,度量软件每一个( A )的大小;一是联合使用从过去的项目中收集到的( B )和其它估算变量,进行成本和( C )估算。LOC和FP是两种不同的估算技术,但两者有许多共同的特征,只是LOC和FP技术对于分解所需要的( D )不同。当用( E )作为估算变量时,功能分解是绝对必要且应达到很详细的程度,而用( F )作为估算变量时,分解程度可以不很详细。( E )是直接估算,( F )是间接估算。若计划人员对每个功能分别按最佳的、可能的、悲观的三种情况给出LOC或FP估计值,记作a, m, b,则LOC或FP 的期望值E的公式为( G ),m是加权的最可能的估计值,遵循( H )。
供选择的答案:
A C. ① 模块 ② 软件项目 ③ 分量 ④ 持续时间
⑤ 工作量 ⑥ 进度 ⑦ 基线数据 ⑧ 改进数据
D. ① 详细程度 ② 分解要求 ③ 改进过程 ④ 使用方法
E, F. ① FP ② LOC
G. ① E = (a+m+b)/3 ② E = (a+4m+b)/6
③ E = (2a+3m+4b)/3 ④
H. ① χ概率 ② γ概率 ③ β概率 ④ 泊松
答案:A. ③, B. ⑦, C. ⑤, D. ①, E. ②, F. ①, G. ②, H. ③。
分析:在软件项目估算时,将代码行LOC和功能点FP数据在两个方面使用:一是作为一个估算变量,度量软件每一个分量的大小;一是联合使用从过去的项目中收集到的基线数据(即对以往项目所做的估算值,保留下来作为后续项目的估算参考)和其它估算变量,进行成本和工作量估算。
LOC和FP是两种不同的估算技术,但两者有许多共同的特征,项目计划人员首先给出一个有界的软件范围的叙述,再由此叙述尝试着把软件分解成一些小的可分别独立进行估算的子功能。然后对每一个子功能估算其LOC或FP(即估算变量)。接着,把基线生产率度量(如,LOC/PM或FP/PM)用做特定的估算变量,导出子功能的成本或工作量。将子功能的估算进行综合后就能得到整个项目的总估算。
LOC或FP估算技术对于分解所需要的详细程度是不同的。当用LOC做为估算变量时,功能分解是绝对必要的且需要达到很详细的程度。而估算功能点所需要的数据是宏观的量,当把FP当做估算变量时所需要的分解程度不很详细。我们还应注意,LOC是直接估算的,而FP是通过估计输入、输出、数据文件、查询和外部接口的数目,以及14种复杂性校正值间接地确定的。避开所用到的估算变量,项目计划人员可对每一个分解的功能提出一个有代表性的估算值范围。利用历史数据或凭实际经验(当其它的方法失效时),项目计划人员对每个功能分别按最佳的、可能的、悲观的三种情况给出LOC或FP估计值。记作a、m、b。当这些值的范围被确定之后,也就隐含地指明了估计值的不确定程度。
接着计算LOC或FP的期望值E。
E = (a + 4m + b) / 6. (加权平均)
其中,可能的估计值m是加权最重的最可能的估算值且遵循β概率分布。
【例3】定义一个人参加劳动时间的长短为( A ),其度量单位为PM(人月)或PY(人年)。而定义完成一个软件项目(或软件任务)所需的( A )为( B ),其度量单位是人月/项目(任务),记作PM(人月)。进一步地,定义单位( A )所能完成的软件( C )的数量为软件( D ),其度量单位为LOC/PM。它表明一般指( E )的一个平均值。例如,一个软件的开发工作量如下表所示。该软件共有源代码2900行,其中, 500行用于测试,2400行是执行( F )的源代码。则劳动生产率是( G ) (LOC/PM)。
表 软件开发所需工作量例
阶 段 软件计划 需求分析 设 计 编 码 测 试 总 计
需要工作量(人月) 1.0 1.5 3.0 1.0 3.5 10.0
供选择的答案:
A, B, D. ① 生产率 ② 工作量 ③ 成本 ④ 劳动量
E. ① 开发全过程 ② 某开发阶段 ③ 软件生存期 ④ 某开发任务
F, C. ① 软件 ② 程序 ③ 进程 ④ 产品
G. ① 520 ② 120 ③ 320 ④ 240
答案:A. ④, B. ②, C. ④, D. ①, E. ①, F. ②, G. ④。
分析:定义一个人参加劳动时间的长短为劳动量,其度量单位为PM(人月),PY(人年)。它不同于工作量。而定义完成一个软件项目(或软件任务)所需的劳动量为工作量,其度量单位是人月/项目(任务),记作PM(人月)。进一步地,定义单位劳动量所能完成的软件产品的数量为软件生产率,其度量单位为LOC/PM。它表明一般指开发全过程的一个平均值。题例所示的软件共有源代码2900行,其中, 500行用于测试,2400行是执行程序的源代码。则劳动生产率是(2900-500)/10 = 240(LOC/PM)。
【例4】对于一个大型的软件项目,由于项目的复杂性,需要进行一系列的估算处理。主要按( A )和( B )手段进行。估算的方法分为三类:从项目的整体出发,进行( B )的方法称为( C )估算法。把待开发的软件细分,直到每一个子任务都已经明确所需要的开发工作量,然后把它们加起来,得到软件开发总工作量的方法称为( D )估算法。而把待开发的软件项目与过去已完成的软件项目做类比,区分出类似部分和不同部分分别处理的方法称为( E )估算法。( F )是由多位专家进行成本估算的方法。
供选择的答案:
A, B. ① 类推 ② 类比 ③ 分解 ④ 综合
C F. ① 差别 ② 自顶向下 ③ 自底向上 ④ 专家判定技术
⑤ 循序渐进 ⑥ 比较
答案:A. ③, B. ①, C. ②, D. ③, E. ①, F. ④。
分析:对于一个大型的软件项目,由于项目的复杂性,开发成本的估算不是一件简单的事,要进行一系列的估算处理。主要靠分解和类推的手段进行。基本估算方法分为三类。
① 自顶向下的估算方法:这种方法的主要思想是从项目的整体出发,进行类推。即估算人员根据以前已完成项目所消耗的总成本(或总工作量),来推算将要开发的软件的总成本(或总工作量),然后按比例将它分配到各开发任务单元中去,再来检验它是否能满足要求。这种方法的优点是估算工作量小,速度快。缺点是对项目中的特殊困难估计不足,估算出来的成本盲目性大,有时会遗漏被开发软件的某些部分。
② 自底向上的估计法:这种方法的主要思想是把待开发的软件细分,直到每一个子任务都已经明确所需要的开发工作量,然后把它们加起来,得到软件开发的总工作量。这是一种常见的估算方法。它的优点是估算各个部分的准确性高。缺点是缺少各项子任务之间相互联系所需要的工作量,还缺少许多与软件开发有关的系统级工作量(配置管理、质量管理、项目管理)。所以往往估算值偏低,必须用其它方法进行检验和校正。
③ 差别估计法:这种方法综合了上述两种方法的优点,其主要思想是把待开发的软件项目与过去已完成的软件项目进行类比,从其开发的各个子任务中区分出类似的部分和不同的部分。类似的部分按实际量进行计算,不同的部分则采用相应的方法进行估算。这种的方法的优点是可以提高估算的准确程度,缺点是不容易明确“类似”的界限。
专家判定技术是由多位专家进行成本估算。由于单独一位专家可能会有种种偏见,最好由多位专家进行估算,取得多个估算值。
【例5】 Putnam模型是一种( A )模型。需要建立一条连续的( B ),称为Rayleigh-Norden曲线。可以由此导出一个( C ),把已交付的源代码(源语句)行数与工作量和开发时间联系起来。请选择合适的答案完成下面大型软件项目的开发工作量分布图。

供选择的答案:
A. ① 单值 ② 多值 ③ 静态多变量 ④ 动态多变量
⑤ 静态单变量
B, C. ① 软件方程 ② 函数曲线 ③ 资源需求曲线 ④ 工作量分布
D H. ① 安装 ② 系统定义 ③ 测试与确认 ④ 设计与编码
⑤ 功能设计∕规格说明
答案:A. ④, B. ④, C. ①, D. ②, E. ⑤, F. ④, G. ③, H. ①。
分析:Putnam模型是1978年Putnam提出的模型,是一种动态多变量模型。它假定在软件开发的整个生存期中工作量有特定的分布。这种模型是依据在一些大型项目(总工作量达到或超过30个人年)中收集到的工作量分布情况而推导出来的,但也可以应用在一些较小的软件项目中。大型软件项目的开发工作量分布可以用下图所示的Rayleigh-Norden曲线表示。

该曲线的典型形状由Lord Rayleigh最早有分析地导出,并由Norden使用收集到的软件开发中的经验数据证实了这条曲线。用Rayleigh-Norden曲线可以导出一个“软件方程”,把已交付的源代码(源语句)行数与工作量和开发时间联系起来。

其中,td是开发持续时间(以年计), K是软件开发与维护在内的整个生存期所花费的工作量(以人年计),L是源代码行数(以LOC计),Ck是技术状态常数,它反映出“妨碍程序员进展的限制”,并因开发环境而异。
【例6】一个规模为10KDSI的商用微机远程通信的嵌入型软件,使用中间COCOMO模型进行软件成本估算。程序的名义工作量MM = ( A );程序实际工作量MM = ( B );开发所用的时间TDEV = ( C );如果软件开发人员的工资都按每月6000美元计算,则该软件项目的开发人员的工资总额 = ( D )。
表1 中间COCOMO模型的名义工作量与进度公式
总体类型 工 作 量 进 度
组 织 型 MM=3.2 (KDSI)1.05 TDEV=2.5 (MM)0.38
半独立型 MM=3.0 (KDSI)1.12 TDEV=2.5 (MM)0.35
嵌 入 型 MM=2.8 (KDSI)1.20 TDEV=2.5 (MM)0.32
表2 影响工作量的因素fi 的取值
影响工作量因素fi 情 况 取 值
1 软件可靠性 只用于局部地区,恢复问题不严重 1.00(正常)
2 数据库规模 20000字节 0.94(低)
3 产品复杂性 用于远程通信处理 1.30(很高)
4 时间限制 使用70%的CPU时间 1.10(高)
5 存储限制 64K中使用45K 1.06(高)
6 机器 使用商用微处理机 1.00(额定值)
7 周转时间 平均2小时 1.00(额定值)
8 分析员能力 优秀人才 0.86(高)
9 工作经验 远程通信工作3年 1.10(低)
10 程序员能力 优秀人才 0.86(高)
11 工作经验 微型机工作6个月 1.00(正常)
12 语言使用经验 12个月 1.00(正常)
13 使用现代程序设计技术 1年以上 0.91(高)
14 使用软件工具 基本的微型机软件 1.10(低)
15 工期 9个月 1.00(正常)

供选择的答案:
A, B. ① 45.8 ② 51.5 ③ 44.38 ④ 54.2
C. ① 8.9月 ② 9.8月 ③ 7.8月 ④ 10.9月
D. ① 26.4万美元 ② 36万美元 ③ 20.96万美元 ④ 30.9万美元
答案:A. ③, B. ②, C. ①, D. ④。
分析:考虑如题中表2的15种影响软件工作量的因素,通过定下乘法因子,修正COCOMO工作量公式和进度公式,可更合理地估算软件(各阶段)的工作量和进度。此时,实际工作量计算公式改成:

由此得到程序名义工作量 MM = 2.8 *(10)1.20 = 44.38 (MM)

开发所用时间 TDEV = 2.5 *(51.5)0.32 = 8.9 (月)
如果分析员与程序员的工资都按每月6,000美元计算,则该项目的开发人员的工资总额为51.5 * 6000 = 309000 (美元)
【例7】在特定情况下,是否必须进行风险分析,是对项目开发的形势进行( A )后确定的。( A )可以按如下步骤进行:明确项目的目标、总策略、具体策略和为完成所标识的目标而使用的方法和资源;保证该目标是( B ),项目成功的标准也是( B );考虑采用某些条目作为项目成功的( C );根据估计的结果来确定是否要进行风险分析。
一般来说,风险分析的方法要依赖于特定问题的需求和有关部门所关心的方面。具体分3步进行。第一步识别潜在的风险项,首先进行( D )过程;第二步估计每个风险的大小及其出现的可能性,选择一种( E ),它可以估计各种风险项的值;第三步进行风险评估。风险评估也有三个步骤:确定( F ),确定( G ),把风险与“参照风险”做比较。
供选择的答案:
A. ① 风险管理 ② 风险估计 ③ 风险评价 ④ 风险测试
B. ① 可度量的 ② 不可度量的 ③ 准确的 ④ 不确定的
C. ① 规范 ② 标准 ③ 过程模型 ④ 设计要求
D, E. ① 信息分类 ② 信息收集 ③ 度量尺度 ④ 标准
⑤ 度量工具 ⑥ 信息获取
F, G. ① 风险的范围 ② 风险的特性 ③ 风险的级别 ④ 风险的评价标准
⑤ 风险的排除策略
答案:A. ②, B. ①, C. ②, D. ②, E. ③, F. ④, G. ③。
分析:在特定的情况下,是否必须进行风险分析,是对项目的开发形势进行风险估计后确定的。因为风险分析需要相当大的费用。只有在软件的费用、软件的作用、软件的性能及软件与系统的关系等各方面对系统有比较大的影响时,即软件的风险对于整个系统的成败,或对系统的风险有关键的影响时,才有必要进行软件的风险分析和管理。风险估计的步骤如下:
明确项目的目标、总策略、具体策略和为完成所标识的目标而使用的方法和资源;
保证该目标是可度量的,项目成功的标准也是可度量的;
考虑采用以下的某些条目作为项目成功的标准:① 最大限度的收益,② 最小的费用,③ 最小的风险损失,④ 最大限度的市场,⑤ 最小的周期性的波动,⑥ 形成有益的形象,⑦ 最佳的服务质量,⑧ 最高的增长率,⑨ 员工的满意度最高,⑩ 公司声望最高;
根据估计的结果来确定是否要进行风险分析。
一般来说,风险分析的方法要依赖于特定问题的需求和有关部门所关心的方面。下面给出一种结构化的、一致的方法来进行风险分析。具体分3步进行。
第一步识别潜在的风险项。当确定要进行风险分析之后,就要收集信息,表明相关的风险。这就需要观察风险的征兆,理解其产生的原因,并列出所有的风险项。
首先进行信息收集。可以从过去完成的项目中收集已有的经验和收集来自群众的经验;可以模拟著名的事例;可以考虑类似的因素和进行常识性的判断;可以进行试验或测试以得到有关的结果,可以用各种方式来获得可能忽略的情况;此外,还可以针对经常发生的错误进行普查统计等。一般来说,通过过去的历史来认识软件项目的风险也许时一种最好的办法。例如,一些数字表明修复一个需求或设计阶段的错误的费用可能比修复一个测试阶段的错误的费用高100倍到1000倍。因此,可以把需求阶段标识为一个软件开发各阶段的风险区域。
然后进行信息分类。必须将收集到的信息以某种方式进行分类。一种简单而有用的方法是把风险项分为三类:有风险、可预见的风险、不可预见的风险。“有风险”是指经常发生的情况;“可预见的风险”是指以较高概率出现的情况;“不可预见的风险”是指不能识别的、未知的、不能观察的风险,是可能发生但事前很难预料的风险。对于每一种类型,还可以按其原因分成三种子类型:缺乏信息、缺乏管理及缺乏时间。其它分类方法可以按直接或间接分类,按运行性或策略性分类,按技术、进度、成本、支持分类。
第二步估计每个风险的大小及其出现的可能性,风险估计要度量所标识的各个风险可能造成的损失,即各种风险项的值(后果及程度),用以减少度量的不确定性。可以按以下步骤进行:
选择某一种度量尺度,用以估计计算各种风险项的值,并具有合适的精度。由于要估计的风险信息可能有3种形式:叙述性、定性或定量,所以可选的尺度可以是命名尺度、序次尺度、坐标尺度或比例尺度。待估计的信息与度量尺度之间要建立对应关系,不同类型的信息有不同的度量尺度。例如,叙述性信息需要有命名尺度或序次尺度,定量的信息需要坐标或比例性的尺度。下表列出定量的风险等级:
风险等级 失效概率 说 明
极高 0.99 0.81 超过当前的技术水平,肯定是技术问题。
很高 0.80 0.61 超过当前的技术水平,极像是技术问题。
高 0.60 0.50 最新的、尚未充分成熟的技术,好像是技术问题。
中等 0.49 0.24 最佳技术,只会有很小的技术问题。
低 0.24 0.10 使用的技术,没有技术问题。
很低 0.09 0.01 在使用中的系统。
在使用不同的方法和技术进行风险估计时常常会出现偏差,这是由于缺少可用来进行判断的信息,从而限制了风险估计的精度。由于信息分散,各人的理解和解释不同,造成“信息可用性偏差”。其次,选择的观念不同、专家的偏爱、采样规模的影响、样本相关的影响,以及修正的偏差等,都会产生估计的偏差。特别要注意的是:对于连续发生的事件和间断的不连续发生事件,这些偏差会造成什么样的影响。
必须采用一些技术来克服或消减风险估计中的不确定性。风险一般可以看成属于以下三种过程之一:行为型、自然型和随机型。
过程种类 克服和消减叙述型的不确定性 克服和消减度量型的不确定性
行为型 受定义人类行为的能力的限制 受理性的人类行为的限制
自然型 理论上无限制,但实际上有一定范围 受度量系统的精度的限制
随机型 理论上无限制,但实际上有一定范围 无法消减不确定性
第三步进行风险评估。因为软件项目所面临的是风险的一个较大的集合及其相互之间的影响,因此,必须针对这一点进行风险评估,以达到以下的目的:
首先,考虑各种风险的综合影响后,对已识别风险发生的可能性及其后果给出最终的量值(如果情况发生变化,也许要重新分析风险发生的可能性和可能的后果);
其次,提供某种机制,对各个风险标明优先次序,以便予以适当安排;
最后,通过考虑其它可替代的方案,寻找避免风险的基本方法,即为高层决策人员提供全部必要的信息,以作出合理的有依据的决策。进行风险评估有三个步骤:
① 确定风险评估的标准。其目的是可用以衡量每个风险的后果,即判定在项目的生存期中各个阶段的风险的后果是否可以接受。此标准应与项目成功的标准相关。
② 确定风险的级别。其目的是把项目作为整体来评估。就是说,人们必须理解各种风险之间的相互作用,以及修改某些因素会如何影响它们之间的相互作用。
为了说明可被评估的风险,引入“参照风险”。“参照风险”可以是一组单个风险的集合,或对项目会造成最大损害的一个或多个风险。必须仔细认清各风险间可能发生的耦合或复合情况。说明在把系统视为一个整体时,风险将导致系统失败的概率。
③ 把风险与“参照风险”做比较。把已评定的风险与在早期确定的“参照风险”相比较,结果可能是以下3种情况之一:
可接受(评定的风险低于“参照风险”);
不可能接受(评定的风险大大高于“参照风险”);
不适合接受(评定的风险大于,但几乎等于“参照风险”)。
【例8】假设开发某个计算机应用系统的投资额为3000元,该计算机应用系统投入使用后,每年可以节约1000元,5年内可能节约5000元。3000元是现在投资的钱,5000元是5年内节省的钱,两者不能简单地比较。
假定年利率为12%,利用计算货币现在价值的公式,可以算出该计算机应用系统投入使用后每年预计节省的金额的现在价值。
年 节省(元) 利率(1 + 0.12)n 现在价值(元) 累计现在价值(元)
1 1000 1.12 892.86 892.86
2 1000 1.25 800.00 1692.86
3 1000 1.40 714.29 2407.15
4 1000 1.57 636.94 3044.09
5 1000 1.76 568.18 3612.27
则该系统的纯收入是( A ),投资回收期是( B ),投资回收率为( C )。
供选择的答案:
A. ① 512.3元 ② 729.28元 ③ 602.4元 ④ 612.27元
B. ① 2. 4年 ② 3.93年 ③ 4.25年 ④ 2.78元
C. ① 25% ② 30% ③ 20% ④ 15%
答案:A. ④, B. ②, C. ③。
分析:效益包括经济效益,也包括社会效益。前者是有形的,后者是无形的。系统的经济效益等于因使用新系统而增加的收入加上使用新系统可以节省的运行费用。运行费用包括操作员人数、工作时间、消耗的物资等。在计算系统的经济效益时,应按照货币的时间价值来计算,这是因为对项目的投资在前,而系统效益的产生在后,且常常有一个较长的过程。
通常,用利率表示货币的时间价值。若设年利率为i,现已存入P元,则n年后可得到的钱数为:F = ( 1 + i ) n,F就是P元钱在n年后的价值。 反之,若n年后能收入F元,那么这些钱现在的价值是:P = F∕( 1 + i ) n。
由此,可从题意得:
该计算机应用系统在5年中的纯收入为:3612.27 - 3000 = 612.27 (元)。
投资回收期约为:3 +(3000-2407.15)/(3044.09-2407.15) ≈ 3.93 (年)。
投资回收率设为r,由下列方程式:
3000 = 1000/(1+ r)+1000/(1+ r)2+1000/(1+ r)3+ …… +1000/(1+ r)5
解得r = 20%。
纯收入就是在整个生存期之内系统的累计经济效益(折合成现在值)与投资之差。投资回收期就是使累计的经济效益等于最初的投资所需的时间。投资回收率时投入资金所获得的利率。
【例9】软件项目管理的主要职能包括:( A ),建立组织,配备人员,( B )和( C )。由于软件项目的特有性质,使得项目管理存在一定困难。第一、( D ),软件工程过程充满了大量高强度的脑力劳动;第二、( E ),在特定机型上,利用特定的硬件配置,由特定的系统软件和支撑软件支持,形成了特定的开发环境;第三、( F ),软件项目经历的各个阶段都深透了大量的手工劳动,远未达到自动化的程度;第四、( G ),用户要经过专门的培训,才能掌握操作步骤,且需要配备专职维护人员进行售后服务;第五、( H ),为高质量地完成软件项目,充分发掘人员的智力才能和创造精神。
在总结和分析足够数量失误的软件项目之后可知,造成软件失误的原因大多与( I )工作有关。在软件项目开始执行时,执行的过程中及项目进行的最后阶段都会遇到种种问题。
供选择的答案:
A C. ① 编码 ② 制定计划 ③ 开发 ④ 指导
⑤ 测试 ⑥ 检验
D H. ① 软件工作渗透了人的因素 ② 智力密集,可见性差
③ 单件生产 ④ 使用方法繁琐,维护困难
⑤ 劳动密集,自动化程度低
I. ① 设计 ② 维护 ③ 测试 ④ 管理
⑤ 实践 ⑥ 指导 ⑦ 审核 ⑧ 分析
答案:A. ②, B. ④, C. ⑥, D. ②, E. ③, F. ⑤, G. ④, H. ①, I. ④。
分析:软件管理的主要职能包括:
制定计划:规定待完成的任务、要求、资源、人力和进度等。
建立组织:为实施计划,保证任务的完成,需要建立分工明确的责任制机构。
配备人员:任用各种层次的技术人员和管理人员。
指导:鼓励和动员软件人员完成所分配的工作。
检验:对照计划或标准,监督和检查实施的情况。
软件项目管理上的困难主要有:
① 智力密集,可见性差:软件工程过程充满了大量高强度的脑力劳动。软件开发的成果是不可见的逻辑实体,软件产品的质量难以用简单的尺度加以度量。对于不深入掌握软件知识或缺乏软件开发实践经验的人员,是不可能领导做好软件管理工作。软件开发任务完成得好也看不见,完成得不好有时也能制造假象,欺骗外行的领导。
② 单件生产:在特定机型上,利用特定的硬件配置,由特定的系统软件或支撑软件的支持,形成了特定的开发环境。再加上软件项目特定的目标,采用特定的开发方法、工具和语言,使得软件具有独一无二的特色,几乎找不到与之完全相同的软件产品。这种建立在内容、形式各异的基础上的研制或生产方式,与其它领域中大规模现代化生产有着很大的差别,也自然会给管理工作造成许多实际困难。
③ 劳动密集,自动化程度低:软件项目经历的各个阶段都渗透了大量的手工劳动,这些劳动又十分细致、复杂和容易出错。尽管近年来开展了软件工具和CASE的研究,但总体来说,仍远未达到自动化的程度。软件产业所处的这一状态,加上软件本身的复杂性,使得软件的开发和维护难于避免多种错误,软件的正确性难于保证,软件产品质量的提高自然受到了很大的影响。
④ 使用方法繁琐,维护困难:用户使用软件需要掌握计算机的基本知识,或者接受专门的培训,否则面对多种使用手册、说明和繁琐的操作步骤,学会使用要花费很大力气。另一方面,如果遇到软件运行出了问题,且没有配备专职维护人员,又得不到开发部门及时的售后服务,软件的使用者更是徒唤奈何。
⑤ 软件工作渗透了人的因素:为高质量地完成软件项目,充分发掘人员的智力才能和创造精神,不仅要求软件人员具有一定的技术水平和工作经验,而且还要求他们具有良好的心理素质。软件人员的情绪和他们的工作环境,对他们工作有很大的影响。与其它行业相比,它的这一特点十分突出,必须给予足够的重视。
造成软件失误的原因:
在总结和分析足够数量失误的软件项目之后,看出其原因大多与管理工作有关。
在软件项目开始执行时,遇到的问题往往是:可供利用的资料太少;项目负责人的责任不明确;项目的定义模糊;没有计划或计划过分粗糙;资源要求未按时做出安排而落空;没有明确规定子项目完成的标准;缺乏使用工具的知识;项目已有更动,但预算未随之改变。
在软件项目执行的过程中可能会发生:项目审查只注意琐事而走过场;人员变动造成对工作的干扰;项目进行情况未能定期汇报;对阶段评审和评审中发现的问题如何处置未做出明确规定;资源要求并不像原来预计的那样大;未能做到严格遵循需求说明书;项目管理人员不足。
项目进行到最后阶段可能会发生:未做质量评价;取得的知识和经验很少交流 ;未对人员工作情况做出评定;未做严格的移交;扩充性建议未写入文档资料。
总之,问题涉及到软件项目研制中的计划制定、进度估计、资源使用、人员配备、组织机构和管理方法等软件管理的许多侧面。

习题

【9-1】软件过程是软件( A )中的一系列相关软件工程( B )的集合。每一个软件过程又是由一组( C )、项目( D )、软件工程产品和交付物以及质量保证(SQA)点等组成。一个软件过程可以用右图的形式来表示。首先建立一个( E )过程框架,其中定义了少量可适用于所有软件项目的框架( B ),再给出各个框架( B )的任务集合,最后是保护伞活动,如软件质量保证、软件配置管理以及测量等。软件过程模型的选择基于项目和应用的特点、采用的( F )和工具、要求的控制和需交付的产品。
供选择的答案:
A F. ① 工程 ② 公共 ③ 活动 ④ 生存期
⑤ 方法 ⑥ 工作任务 ⑦ 功能 ⑧ 里程碑
【9-2】软件的度量包括( A )和( B )。软件产品的( A )包括产生的代码行数、执行速度等。软件产品的( B )则包括若干质量特性。我们还可进一步将软件度量如右图所示那样分类。软件( C )度量主要关注软件工程过程的结果;( D )度量则指明了软件适应明确和不明确的用户要求到什么程度;( E )度量主要关注软件的一些特性而不是软件开发的全过程。从图中还可看到另一种分类方法:面向( F )的度量用于收集与直接度量有关软件工程输出的信息和质量信息。面向( G )的度量提供直接度量的尺度。面向( H )的度量则收集有关人们开发软件所用方式的信息和人们理解有关工具和方法的效率的信息。
供选择的答案:
A B. ① 直接度量 ② 尺度度量 ③ 二元度量 ④ 间接度量
C E. ① 质量 ② 技术 ③ 成本 ④ 生产率
F H. ① 过程 ② 对象 ③ 人 ④ 存取
⑤ 规模 ⑥ 进程 ⑦ 功能 ⑧ 数据
【9-3】估算资源、成本和进度时需要经验、有用的历史信息、足够的定量数据和作定量度量的勇气。通常估算本身带有( A )。项目的复杂性越高,规模越大,开发工作量( B ),估算的( A )就( C )。项目的结构化程度提高,进行精确估算的能力就能( D ),而风险将( E )。有用的历史信息( F ),总的风险会减少。
供选择的答案:
A. ① 风范(范型) ② 风格 ③ 风险 ④ 度量
B F. ① 增加 ② 越多 ③ 降低 ④ 不变
⑤ 越少 ⑥ 越高 ⑦ 越大
【9-4】 在考虑各种软件开发资源时,( A )是最重要的资源。如果把软件开发所需的资源画成一个金字塔形:在塔的上层是最基本的资源( A ),在底部为( B )。( B )包括硬件资源和软件资源。( C )、( D )和其它硬件设备属于硬件资源。IPSE工具属于软件资源中的( E )。为了提高软件的生产率和软件产品的质量,可建立( F )。
供选择的答案:
A, B. ① 方法 ② 人力 ③ 工具 ④ 上下文环境
C, D. ① 虚拟机 ② 目标机 ③ 自动机 ④ 宿主机
E, F. ① 维护工具 ② 分析设计工具 ③ 支持工具 ④ 编程工具
⑤ 可复用构件库 ⑥ 框架工具 ⑦ 原型化模拟工具
【9-5】任何软件项目都必须做好项目管理工作,最常使用的进度管理工具是( A ),当某一开发项目的进度有可能拖延时,应该( B )。对于一个典型的软件开发项目,各开发阶段需投入的工作量的百分比大致是( C )。各阶段所需不同层次的技术人员大致是( D ),而管理人员在各阶段所需数量也不同,相对而言大致是( E )。
供选择的答案:
A. ① 数据流图 ② 程序结构图 ③ 因果图 ④ PERT图
B. ① 增加新的开发人员 ② 分析拖期原因加以补救
③ 从别的小组抽调人员临时帮忙 ④ 推迟预定完成时间
需求分析 设 计 编 码 测 试
投入
工作量 ① 25 25 25 25
C. ② 10 20 30 40
③ 15 30 15 40
④ 5 10 65 30
技术人
员水平 ① 初级 高级 高级 高级
D. ② 中级 中级 高级 中级
③ 高级 中高级 初级 中高级
④ 中级 中高级 中级 初级
管理人
员数量 ① 多 中 少 中
E. ② 中 中 中 中
③ 多 少 多 多
④ 少 多 少 多
【9-6】一个32KDSI的声音输入系统是一个输入原型,或是一个可行性表演模型。所需可靠性非常低,因为它不打算投入生产性使用。把此模型看做半独立型软件。试问该软件的名义工作量和实际工作量。
【9-7】风险分析实际上是4个不同的活动,按顺序依次为( A )、( B )、风险评价和( C )。在风险评价时,应当建立一个三元组:[ ri, li, xi ],ri是风险描述,li是( D ),而xi是风险的影响。一个对风险评价很有用的技术是定义( E )。( F )、( G )、( H )是三种典型的( E )。在做风险分析的上下文环境中一个( E )就存在一个单独的点,叫做参照点或( I )。在这个点上要公正底给出判断。实际上,参照点能在图上表示成一条平滑的曲线的情况很少,多数情况它是一个( J )。
供选择的答案:
A C. ① 风险驾驭和监控 ② 风险识别 ③ 风险估计 ④ 风险消除
D. ① 风险的大小 ② 风险的概率 ③ 风险的时间 ④ 风险的范围
E. ① 风险参照水准 ② 风险度量 ③ 风险监控 ④ 风险工具
F H. ① 生产率 ② 功能 ③ 成本 ④ 进度
⑤ 范围 ⑥ 性能
I, J. ① 凹点 ② 崩溃点 ③ 终点 ④ 区域
⑤ 拐点 ⑥ 原点
【9-8】对于一个小型的软件开发项目,一个人就可以完成需求分析、设计、编码和测试工作。但随着软件项目规模增大,需要有多人共同参与同一软件项目的工作。当几个人共同承担软件开发项目中的某一任务时,人与人之间必须通过交流来解决各自承担任务之间的( A )问题,即通信问题。通信需花费时间和代价,会引起软件错误( B ),( C )软件生产率。如果一个软件开发小组有n个人,每两人之间都需要通信,则共有( D )条通信路径。假设一个人单独开发软件,生产率是5000行/人年,且在每条通信路径上耗费的工作量是250行/人年。若4个人组成一个小组共同开发这个软件,则小组中每个人的软件生产率为( E )。若小组有6名成员,则小组中每个成员的软件生产率为( F )。因此,有人提出,软件开发小组的规模不能太大,人数不能太多,一般在( G )人左右为宜。
供选择的答案:
A. ① 分配 ② 管理 ③ 接口 ④ 协作
B, C. ① 降低 ② 增加 ③ 不变
D. ① n(n+1)/2 ② n(n-1)/2 ③ n(n-1)(n-2)/6 ④ n2/2
E, F. ① 4875 ② 4375 ③ 4625 ④ 5735
G. ① 8~15 ② 1~2 ③ 2~5 ④ 2~8
【9-9】软件项目的进度管理有许多方法,但( A )不是常用的进度控制图示方法。在几种进度控制图示方法中,( B )难以表达多个子任务之间的逻辑关系,使用( C )不仅能表达子任务之间的逻辑关系,而且可以找出关键子任务。在( C )中,用带箭头的边表示( D ),用圆圈结点表示( E ),它标明( D )的( F )。
供选择的答案:
A C. ① 甘特图 ② IPO ③ PERT ④ 时标网状图
D F. ① 数据流 ② 控制流 ③ 事件 ④ 处理
⑤ 起点或终点 ⑥ 任务
【9-10】软件项目组织的原则是( A )、( B )和( C )。一般有( D )、( E )、( F )三种组织结构的模式。( F )实际上是( D )和( E )两种模式的复合。( E )这种模式在小组之间的联系形成的接口较多,但便于软件人员熟悉小组的工作,进而成为这方面的专家。
供选择的答案:
A C. ① 推迟责任的落实 ② 尽早落实责任 ③ 减少接口
④ 增加联系 ⑤ 责权分离 ⑥ 责权均衡
D F. ① 矩阵形模式 ② 主程序员小组模式 ③ 按课题划分的模式
④ 按职能划分的模式 ⑤ 民主制小组模式
【9-11】软件开发小组的目的是发挥集体的力量进行软件研制。因此,小组从培养( A )的观点出发进行程序设计消除软件的( B )的性质。通常,程序设计小组的组织形式有三种,如下图所示的a属于( C ),b属于( D ),c属于( E )。

供选择的答案:
A, B. ① “局部” ② “全局” ③ “集体” ④ “个人”
C E. ① 层次式小组 ② 民主制小组 ③ 主程序员制小组

习题解答

【9-1】A. ④, B. ③, C. ⑥, D. ⑧, E. ②, F. ⑤。
软件过程是软件生存期中的一系列相关软件工程活动的集合。每一个软件过程又是由一组工作任务、项目里程碑、软件工程产品和交付物以及质量保证(SQA)点等组成。一个软件过程可以用右图的形式来表示。首先建立一个公共过程框架,其中定义了少量可适用于所有软件项目的框架活动,而不考虑它们的规模和复杂性。再给出各个框架活动的任务集合,使得框架活动能够适合于项目的特点和项目组的需求。最后是保护伞活动,如软件质量保证、软件配置管理以及测量等,它们独立于任何一个框架活动并将贯穿于整个过程。软件过程模型的选择基于项目和应用的特点、采用的方法和工具、要求的控制和需交付的产品。
【9-2】A. ①, B. ④, C. ④, D. ①, E. ②, F. ⑤, G. ⑦, H. ③。
软件的度量包括直接度量和间接度量。软件产品的直接度量包括产生的代码行数、执行速度、存储量大小、在某种时间周期中所报告的差错数。软件产品的间接度量则包括功能性、复杂性、效率、可靠性、可维护性和许多其它的质量特性。只要事先建立特定的度量规程,很容易做到直接度量开发软件所产生的代码行数等。但是,软件的功能性、效率、可维护性等质量特性却很难用直接度量判明,只有通过间接度量才能推断。我们还可进一步将软件度量如图所示那样分类。软件生产率度量主要关注软件工程过程的结果;软件质量度量则指明了软件适应明确和不明确的用户要求(软件使用合理性)到什么程度;技术度量主要关注软件的一些特性(如逻辑复杂性、模块化程度)而不是软件开发的全过程。从图中还可以看到另一种分类方法:面向规模的度量用于收集与直接度量有关的软件工程输出的信息和质量信息。面向功能的度量提供直接度量的尺度。面向人的度量则收集有关人们开发软件所用方式的信息和人们理解有关工具和方法的效率的信息。
估算资源、成本和进度时需要经验、有用的历史信息、足够的定量数据和作定量度量的勇气。通常估算本身带有( A )。项目的复杂性越高,规模越大,开发工作量( C ),估算的( A )就( D )。项目的结构化程度的提高,进行精确估算的能力就能( E ),而风险将( F )。有用的历史信息( G ),总的风险会减少。
供选择的答案:
A. ①风范(范型) ② 风格 ③ 风险 ④ 度量
B G. ① 增加 ② 越大 ③ 降低 ④ 不变
⑤ 减少 ⑥ 越高
【9-3】A. ③, B. ②, C. ⑦, D. ①, E. ③, F. ②。
估算资源、成本和进度时需要经验、有用的历史信息、足够的定量数据和作定量度量的勇气。估算本身带有风险。增加风险的各种因素如图所示。

项目的复杂性对于增加软件估算的不确定性影响很大。复杂性越高,估算的风险就越高。但是,复杂性是相对度量,它与项目参加人员的经验有关。
项目的规模对于软件估算的精确性和功效影响也比较大。因为随着软件规模的扩大,软件元素之间的相互依赖、相互影响程度迅速增加,因而估算的一个重要方法──问题分解会变得更加困难。由此可知,项目的规模越大,开发工作量越大,估算的风险越高。
项目的结构化程度也影响项目估算的风险。所谓结构性是指功能分解的简便性和处理信息的层次性。结构化程度的提高,进行精确估算的能力就能提高,而风险将减少。
历史信息的有效性也影响估算的风险。回顾过去,就能够仿效做过的事,且改进出现问题的地方。在对过去的项目进行综合的软件度量之后,就可以借用来比较准确地进行估算,安排进度以避免重走过去的弯路,而总的风险也减少了。
风险靠对不确定性程度定量地进行估算来度量,此外,如果对软件项目的作用范围还不十分清楚,或者用户的要求经常变更,都会导致对软件项目所需资源、成本、进度的估算频频变动,增加估算的风险。
【9-4】A. ②, B. ③, C. ②, D. ④, E. ⑥, F. ⑤。其中,C、D的答案顺序可互换。
软件项目计划的第二个任务是对完成该软件项目所需的资源进行估算。若把软件开发所需的资源画成一个金字塔,在塔的底部必须有现成的用以支持软件开发的工具──硬件工具及软件工具,在塔的高层是最基本的资源──人。在考虑各种软件开发资源时,人是最重要的资源。在安排开发活动时必须考虑人员的技术水平、专业、人数、以及在开发过程各阶段中对各种人员的需要。
硬件是作为软件开发项目的一种工具而投入的。在软件项目计划期间,考虑三种硬件资源:宿主机(软件开发时使用的计算机及外围设备);目标机(运行已开发成功软件的计算机及外围设备);其它硬件设备(专用软件开发时需要的特殊硬件资源)。宿主机连同必要的软件工具构成一个软件开发系统。通常这样的开发系统能够支持多种用户的需要,且能保持大量的由软件开发小组成员共享的信息。但在许多情况下,除了那些很大的系统之外,不一定非要配备专门的开发系统。因此,所谓硬件资源,可以认为是对现存计算机系统的使用,而不是去购买一台新的计算机。宿主机与目标机可以是同一种机型。
软件在开发期间使用了许多软件工具来帮助软件的开发。这些软件工具叫做计算机辅助软件工程(CASE)。主要的软件工具分类为:业务系统计划工具集;项目管理工具集;支持工具;分析和设计工具;编程工具;组装和测试工具;原型化和模拟工具;维护工具;框架工具。这些框架工具能够提供一个建立集成项目支撑环境(IPSE)的框架。在多数情况,框架工具实际提供了数据库管理和配置管理的能力与一些实用工具,能够把各种工具集成到IPSE中。
为了促成软件的复用,以提高软件的生产率和软件产品的质量,可建立可复用的软件构件库。根据需要,对软件构件稍做加工,就可以构成一些大的软件包。这要求这些软件构件应加以编目,以利引用,并进行标准化和确认,以利于应用和集成。
【9-5】A. ④, B. ②, C. ③, D. ③, E. ①。
PERT技术叫做计划评审技术,是安排开发进度,制定软件开发计划的最常用的方法。它采用网络图来描述一个项目的任务网络。通常用两张表来定义网络图。一张表给出与一特定软件项目有关的所有任务(也称为任务分解结构),另一张表给出应当按照什么样的次序来完成这些任务(有时称为限制表)。
当某一开发项目的进度有可能拖延时,应该分析拖期原因加以补救,切忌中途加人,否则反而会降低软件生产率。对于一个典型的软件开发项目,各开发阶段需投入的工作量的百分比大致遵循40-20-40规则。即在整个软件开发过程中,编码的工作量占20%,编码前的工作量占40%,编码后的工作量占40%。
对于一些规模较小的项目(1个人年或者更少),只要向专家做些咨询,也许一个人就可以完成所有的软件工程步骤。而对一些规模较大的项目,在整个软件生存期中,各种人员的参与情况是不一样的。如图所示。在软件计划和需求分析阶段,对软件系统进行定义,主要工作是由管理人员和高级技术人员在做,初级技术人员参与较少。待到对软件进行具体设计、编码及测试时,管理人员逐渐减少对开发工作的参与,高级技术人员主要在设计方面把关,具体编码及调试参与较少,大量的工作将由初级技术人员去做。到了软件开发的后期,需要对软件进行检验、评价和验收,管理人员和高级技术人员又将投入很多的精力。

【9-6】 对于这样一个规模为10KDSI的商用微机远程通信的嵌入式软件,使用中间COCOMO模型进行软件成本估算。名义工作量为MM = 3.0*(10)1.12 = 146 (人月)。又查表知 f1 = 0.75,其它 fi = 1.00,则最终计算出的实际工作量为MM = 146 * 0.75 = 110 (人月)。
【9-7】A. ②, B. ③, C. ①, D. ②, E. ①, F. ③, G. ④, H. ⑥, I. ②, J. ④。其中,F、G、H的答案顺序可互换。
风险分析实际上是4个不同的活动: 风险识别,风险估计,风险评价和风险驾驭与监控。 在进行风险评价时,可建立一系列三元组:[ ri, li, xi ],其中,ri是风险,li是风险出现的可能性(概率),而xi是风险产生的影响。在做风险评价时,应进一步审查在风险估计时所得到的估计的准确性,尝试对已发现的风险进行优先排队,并着手考虑控制和∕或消除可能出现风险的方法。
在做风险评价时常采用的一个非常有效的方法就是定义风险参照水准。对于大多数软件项目来说,性能、支持、成本、进度就是典型的风险参照水准。就是说,对于成本超支、进度延期、性能降低、支持困难,或它们的某种组合,都有一个水准值,超出它就会导致项目被迫终止。在软件风险分析的上下文中,一个风险参照水准就有一个点,叫做参照点或崩溃点。在这个点上,要公平地给出可接受的判断,看是继续执行项目工作,还是终止它们(出的问题太大)。实际上,参照点能在图上被表示成一条平滑的曲线的情况很少。在多数情况中,它是一个区域,在此区域中存在许多不确定性的范围。
【9-8】A. ③, B. ②, C. ①, D. ②, E. ③, F. ②, G. ④。
对于一个小型的软件开发项目,一个人就可以完成需求分析、设计、编码和测试工作。但是,随着软件开发项目规模的增大,就会有更多的人共同参与同一软件项目的工作。例如10个人1年可以完成的项目,若让1个人干10年是不行的。因此,需要多人组成开发小组共同参加一个项目的开发。但是,当几个人共同承担软件开发项目中的某一任务时,人与人之间必须通过交流来解决各自承担任务之间的接口问题,即所谓通信问题。通信需花费时间和代价,会引起软件错误增加,降低软件生产率。
若两个人之间需要通信,则称在这两个人之间存在一条通信路径。如果一个软件开发小组有n个人,每两人之间都需要通信,则总的通信路径有n(n-1)/2条。
假设一个人单独开发软件,生产率是5000行/人年。若4个人组成一个小组共同开发这个软件,则需要6条通信路径(图(a))。若在每条通信路径上耗费的工作量是250行/人年。则小组中每个人的软件生产率降低为
5000-6×250/4 = 5000-375 = 4625 行/人年。
如果小组有6名成员,通信路径增加到15条(图(b))。每条通信路径消耗的工作量不变,则小组中每个成员的软件生产率降低为
5000-15×250/6 = 5000-625 = 4375 行/人年。

从上述简单分析可知,一个软件任务由一个人单独开发,生产率最高;而对于一个稍大型的软件项目,一个人单独开发,时间太长。因此软件开发小组是必要的。有人提出,软件开发小组的规模不能太大,人数不能太多,一般在2~8人左右为宜。
【9-9】A. ②, B. ①, C. ③, D. ⑥, E. ③, F. ⑤。
软件项目的进度计划和工作的实际进展情况,对于较大的项目来说, 难以用语言叙述清楚。特别是表现各项任务之间进度的相互依赖关系,需要采用图示的方法。常用的图示方法有甘特图、时标网状图、PERT等,IPO图是用于在结构化设计中描述程序结构中输入―处理―输出的,不是进度控制的图示工具。
甘特图以水平线段表示任务的工作阶段;线段的起点和终点分别对应着任务的开工时间和完成时间;线段的长度表示完成任务所需的时间。从甘特图上可以很清楚地看出各子任务在时间上的对比关系,并以文档编制与评审作为软件开发进度的里程碑。甘特图的优点是标明了各任务的计划进度和当前进度,能动态地反映软件开发进展情况。缺点是难以反映多个任务之间存在的复杂的逻辑关系。
时标网状图克服了甘特图的缺点,用具有时标的网状图来表示各个任务的分解情况,以及各个子任务之间在进度上的逻辑依赖关系(参看下图)。时标网状图中的箭头(直线、折线)表示各任务间的(先决)依赖关系;箭头上的名字表示任务代号;箭头的水平长度表示完成该任务的时间;而圆圈表示一个任务结束、另一个任务开始的事件。

PERT图也叫做计划评审技术,它采用网络图来描述一个项目的任务网络。不仅可以表达子任务的计划安排,还可以在任务计划执行过程中估计任务完成的情况,分析某些子任务完成情况对全局的影响,找出影响全局的区域和关键子任务,以便及时采取措施,确保整个项目的完成。在PERT图中,用箭头表示任务或子任务,箭头上附带的数字表示完成任务所需的时间;圆形结点表示事件,每一事件标明某些任务都已完成,下面另外一些任务将要开始。
【9-10】A. ②, B. ③, C. ⑥, D. ③, E. ④, F. ①。其中,A、B、C答案顺序可互换。
在建立项目组织时应注意到以下原则:
尽早落实责任:在软件项目工作的开始,要尽早指定专人负责。使他有权进行管理,并对任务的完成负全责。
减少接口:在开发过程中,人与人之间的联系是必不可少的,存在着通信路径。一个组织的生产率是和完成任务中存在的通信路径数目是相互抵触的。 因此,要有合理的人员分工、好的组织结构、有效的通信,减少不必要的生产率的损失。
责权均衡:软件经理人员所负的责任不应比委任给他的权力还大。
通常有三种组织结构的模式可供选择:
按课题划分的模式:把软件开发人员按课题组成小组,小组成员自始至终参加所承担课题的各项任务。他们应负责完成软件产品的定义、设计、实现、测试、复查、文档编制、甚至包括维护在内的全过程。
按职能划分的模式:把参加开发项目的软件人员按任务的工作阶段划分成若干个专业小组。要开发的软件产品在每个专业小组完成阶段加工(即工序)以后,沿工序流水线向下传递。例如,分别建立计划组、需求分析组、设计组、实现组、系统测试组、质量保证组、维护组等。各种文档资料按工序在各组之间传递。这种模式在小组之间的联系形成的接口较多,但便于软件人员熟悉小组的工作,进而变成这方面的专家。
矩阵形模式:这种模式实际上是以上两种模式的复合。一方面,按工作性质,成立一些专门组,如开发组、业务组、测试组等;另一方面,每一个项目又有它的经理人员负责管理。每个软件人员属于某一个专门组,又参加某一项目的工作。
【9-11】A. ②, B. ④, C. ③, D. ②, E. ①。
软件开发小组的主要目的是发挥集体的力量进行软件研制。因此,小组培养从“全局”的观点出发进行程序设计,消除软件的“个人”性质,并促进更充分的复审,小组提倡在共同工作中互相学习从而改善软件的质量。
小组内部人员的组织形式对生产率也有影响。现有的组织形式有三种。
① 主程序员制小组:突出了主程序员的领导,强调主程序员与其他技术人员的直接联系,简化了人际通信。这种集中领导的组织形式能否取得好的效果, 很大程度上取决于主程序员的技术水平和管理才能。美国的软件产业中大多是主程序员制的工作方式。
② 民主制小组:组内成员之间可以平等地交换意见,工作目标的制定及做出决定都由全体成员参加。这种组织形式强调发挥小组每个成员的积极性,要求每个成员充分发挥主动精神和协作精神。有人认为这种组织形式适合于研制时间长、开发难度大的项目。日本在发展计算机事业中,组织软件开发大多采用这种形式的开发小组,取得了很好的效果。
③ 层次式小组:这种结构比较适合项目本身就是层次结构状的课题。因为这样可以把项目按功能划分成若干个子项目,把子项目分配给基层小组,由基层小组完成。基层小组的领导与项目负责人直接联系。这种组织方式比较适合于大型软件项目的开发。
以上三种组织形式可以根据实际情况,组合起来灵活运用。例如,较大的软件项目也许是把主程序员小组组织成层次式结构;也许基层小组的领导又是一个民主制小组的成员。

  • 4
    点赞
  • 0
    评论
  • 6
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 1024 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值