第2章 软件生命周期模型           在第1章中,我们初步介绍了软件工程,并且讨论了探索式程序开发方法和软件工程方法之间的关键区别。我们发现,和探索式风格相反,现代软件开发方法强调要遵循一个生命周期模型。本章中将讨论有关生命周期模型的一些基本概念,并讨论几个著名的生命周期模型。   一个软件的生命周期是指软件产品会在其一生中所经历的一系列可识别的阶段。任何软件产品的生命周期的第一阶段通常是可行性研究阶段。一般来说,随后的阶段是:需求分析和说明、设计、编码、测试及维护。每一个阶段就是所谓的生命周期阶段。   软件生命周期模型是软件生命周期的一种描述性和概略性的表示,它代表了软件产品经历其生命周期阶段所需的所有活动,它还捕捉这些活动的开展顺序。换句话说,生命周期模型图描绘了对于一个软件产品,从其开始生产到被取代为止所执行的不同活动。不同的生命周期模型可能以不同的方式描绘基本的发展活动。因此,无论遵循哪个生命周期模型,所有的生命周期模型都会包含基本的活动,虽然这些活动在不同的生命周期模型中可能以不同的顺序进行。在任何的生命周期阶段中,可以有多个活动同时进行,例如,设计阶段就可能由结构分析活动和其后的结构设计活动共同组成。   大多数企业组织通过一些明确定义的步骤(所谓的业务流程)开展业务。与之类似,制造业也遵循一定的步骤来开发他们的产品(所谓的制造流程)。由于与软件开发相关的软件生命周期包含了相似的过程,所以软件生命周期模型通常称为软件过程模型。   我们需要分清楚方法和过程的区别。过程涵盖了所有活动,从产品生产开始,通过交付,直至退休。此外,它还处理更加广泛的事物:重用、文档、测试、团队成员进行的平行工作、与客户的合作等。相比之下,方法只适用于单一的活动,或充其量只是开发中的个别步骤。例如,测试方法和设计方法都只能在生命周期中各自的领域里处理某一单一的活动。因此在整个生命周期中进程会随着方法的增加而递增。          2.1 为什么使用生命周期   在软件开发中采用生命周期模型已为所有的现代软件开发组织所接受。但是,为什么一个开发团队得遵守一个合适的生命周期模型呢?其主要优势在于,它鼓励以系统化和规范的方式开发软件。当一个程序由一个程序员单独开发时,他可以自由决定他开发软件的具体步骤。但是,当程序由一个团队开发时,那么成员们就有必要准确地理解什么时候应该做什么,否则的话就可能导致混乱和项目失败。我们试着用一个例子来说明这个问题。假设一个软件的开发问题被分成若干部分,而这些部分又都分配给成员。从此,团队成员能够自由地以他们喜欢的方式开发分配给他们的部分,那么很有可能一个成员可以开始写他那一部分的代码,而另一个可能决定先准备测试文档,其他一些工程师可能首先开始设计阶段。不管您相信与否,这正是导致过去许多项目失败的原因!失败的原因不难猜测,严重的问题将出现在不同部分的连接和管理整体的开发上。 2.1.1 为什么要记录一个生命周期模型   生命周期模型形成了软件工程师中一个对活动的共同认识,并有助于以系统和规范的方式开发软件。软件开发组织通常会为其遵循的生命周期模型准备一个准确文档。文档化的软件生命周期模型除了在生命周期模型未充分记录时防止错误发生,也有助于查明开发过程中不一致、重复和遗漏的地方。记录生命周期模型的其他好处有,它提高了开发者对于过程的认识,并要求软件开发组织准确界定生命周期中的每一个活动。基本上,如果有些东西不能写下来,可能就不会有一个明确的概念。此外,当某些具体工程需要时,将备有记录的过程模型按照需要修改会更加容易。在这一章中稍后我们会看到,为了能够用于具体的工程,一个项目团队可能经常需要修改一个特定标准的过程模型,而文档化的过程模型有助于确定需要的修改应该在哪里发生。如今几乎没有一个软件开发组织不记录其遵循的生命周期模型。正如我们稍后会看到的,文档化的生命周期模型也是现代质量保证技术的一项强制性要求。这意味着,如果一个软件组织没有文档化的过程,它就不能被认为有能力开发高质量的软件产品。因此,对于软件开发组织,不仅遵循一个良好定义的过程很重要,记录遵循的过程也是很重要的。 2.1.2 阶段出入标准   除了明确一个软件产品生命周期的不同阶段之外,一个生命周期模型通常会为每一个阶段定义出入标准。只有满足了相应的阶段进入标准,阶段才可以开始;同样地,只有满足了相应的阶段退出标准,一个阶段才可视为完整的。例如,软件需求说明阶段的阶段退出标准可以是已经开发出的软件需求说明(SRS)文档,并经由内部审查和客户核准。只有满足了这些条件下一阶段才能展开。如果清楚定义了各个阶段的出入标准,那么监控工程的进度就会更加容易。   如果每个阶段的出入标准没有明确说明,并且没有遵循任何生命周期模型,那么制订工程进度就会变得十分困难。这通常会导致一个问题,即所谓的99%完成综合症。此综合症表现为,没有确切的方法来衡量项目的进度,乐观的团队成员会认为该项目是99%完成的,即使该工程远未完成——这样使得项目经理对于完成时间的所有预测都变得极不准确。   已经开发出了好几个生命周期模型,但是,在本书中我们仅讨论几个重要且常用的模型。首先讨论软件开发的经典瀑布模型,然后研究软件开发的迭代瀑布、进化、原型和螺旋模型。    2.2 经典瀑布模型   经典瀑布模型直观上是最明显的开发软件方式。尽管传统的瀑布模型优雅而直观,但我们将看到事实并非如此。这是一个切实可行的模型,但在某种意义上说它又不能用于实际的软件开发项目。因此我们可以把这种模型看作是一种理论上的软件开发方法。那么究竟为什么要研究这个模型呢?因为所有其他的生命周期模型基本上都来自于经典瀑布模型,所以,为了能够理解其他生命周期模型,我们必须首先学习经典瀑布模型。此外,稍后会看到在本书中这种模型还有其他用途的。   经典瀑布模型把生命周期划分为如图2-1所示的阶段。这个模型的名字恰如其分,表示出了瀑布的层叠关系。这种模型把生命周期打破细分为很直观的一系列阶段。不同的阶段分别是:可行性研究、需求分析和说明、设计、编码和单元测试、集成和系统测试以及维护。从可行性研究到集成和系统测试阶段的不同阶段被称为开发阶段。生命周期中可行性研究和产品测试和交付之间的部分被称为开发部分。截至生命周期的开发部分末尾,产品就可以随时交付给客户。维护阶段在开发阶段完成后即开始。在任何软件开发的各个阶段中都会有的一个活动就是项目管理。由于这一活动跨越整个项目期限,项目管理活动没有被单独列在图2-1中。即使是为了方便而在生命周期图中将其省略掉,但项目管理绝对是生命周期中一个很重要的活动,用于对产品开发和维护各个阶段工作的管理。 图2-1 经典瀑布模型   完成每一阶段通常需要开发团队投入不同的工作量。图2-2显示了完成一个典型产品的不同阶段的活动时所必须投入的工作量比较。可以观察到,在生命周期的各阶段中,维护阶段通常需要最多的工作量。不过,在开发阶段中,集成和系统测试阶段需要最多的工作量来开发一个典型产品。 图2-2 一个典型产品的不同阶段之间的相对投入分布   生命周期的每个阶段有早已明确的起始和终止标准,它们通常需要以文字说明的方式记录下来。因此,工程师可以确切知道何时停止一个阶段并开始下一阶段。除了为每个阶段定义出入标准之外,大多数组织往往就每一阶段末尾所产生的输出(又称deliverable)制定标准。很多时候它们也指明一些应遵循的方法以产生期望的输出。这当然需要确定具体的方法,然后由工程师执行,例如需求说明、设计、测试及项目管理。良好的软件开发组织通常会记录所有有关如下方面的信息:不同阶段末尾产生的输出、要采用的方法等,并使它们成为一个连贯的框架,称为组织软件开发模型。软件开发组织希望新进入组织的工程师首先能够掌握组织软件开发模型。现在我们简要讨论一下在经典瀑布模型的每个生命周期阶段中所执行的重要活动,以及相应的出入标准。 2.2.1 可行性研究   可行性研究的主要目的是确定开发产品在经济上和技术上是否可行。可行性研究阶段分析问题,并收集各种相关产品的资料,如待输入到系统中的不同数据项、在这些数据上必须执行的处理、需要系统生成的输出数据,以及针对系统的各种约束行为等。对收集到的数据进行分析,我们可以得出如下结论: ● 抽象的问题定义。抽象的问题定义是问题的简要描述,仅考虑重要的需求并忽略其他部分。 ● 不同解决方案的表述。 ● 分析可选的解决方案以比较其优缺点。这种分析通常需要对所需资源、开发成本以及各个选项的开发时间做出大致估算,这些估算是比较不同的解决办案的基础。一旦找到了最好的解决方案,后面所有的开发阶段都将据此进行。因此,在可行性研究中就有大部分的高层次体系结构设计被确定下来,而可行性研究也被认为是一个非常重要的阶段。在这项研究中,由于高成本、资源限制或者一些技术上的原因,可能会出现没有一种办法可行的情况,这当然会要求必须放弃项目。   下面是一个组织进行的可行性研究的例子,可以让您切身体验一下一个典型软件项目的可行性研究阶段中所包含的活动和问题。   案例分析   有一家名为银河矿业有限公司(GMC)的采矿公司,在印度很多地方都有矿山。它在8个邦拥有约50处不同的矿点,并在每个矿场雇佣了大批矿工。采矿业是一个高风险行业,因此该公司打算开办一个特别公积金,这样除了标准公积金之外矿工还可以享受这一额外的公积金。这个特殊公积金(SPF)的主要目标是,在支付标准公积金之前能够快速分发一些赔偿。   根据这项计划,每个矿场将从每个矿工的月收入中扣除SPF基金,然后存入中央特别公积金委员会(CSPFC)。该CSPFC将保管有关从矿工收取的SPF基金的所有细节。   GMC聘请了知名软件厂商Adventure Software Inc.开发一套软件,该软件将用来自动维护所有雇员的SPF记录。GMC意识到,除了可以在簿记工作上节省人力外,该软件还将有助于迅速理赔。GMC表示,它愿意为开发和安装这个软件支付1万卢比。   Adventure Software Inc.选派了自己的项目经理进行可行性研究。项目经理同GMC高层管理者进行讨论,对项目进行大致概览。他还讨论了与一些矿场的几个现场PF官员有关的问题,以确定计划的确切详情。项目经理确定两大途径来解决问题:一个是使用一个中央数据库,可以通过到各矿场的卫星连接进行查阅和更新;另一种办法是,在每个矿场设置本地数据库,然后通过一个拨号连接定期更新中央数据库,这些定期更新可以按日或按时执行,这取决于GMC在调用软件的不同功能时可接受的延迟。项目经理认为第二种做法非常便宜,容错性也更佳,因为即便是到中央数据库的通信连接暂时失败,当地的矿场可能仍在经营。项目经理迅速分析了数据库的功能要求、用户界面问题以及该软件可以处理的与矿场的通信,根据分析他算出了开发的成本。他发现,涉及在矿场维护本地数据库并定期更新中央数据库的解决方案在经济和技术上都是可行的。GMC管理层和项目经理讨论了他的解决方案,并接受了该解决方案。 2.2.2 需求分析和说明   需求分析和说明阶段的目的是为了确切地了解客户的需求,并妥善地记录。这一阶段分为两个不同的活动,即需求收集和分析,以及需求说明。   需求收集和分析   需求收集活动的目标是向客户收集客户对将要开发的产品的所有相关信息,从而清楚地了解顾客的需求,并消除这些需求中的不完备和不一致的地方。请注意,一个不一致的需求指的是该需求中的某一部分同其他一些部分相矛盾;另一方面,一个不完整的需求则指的是需求中的某一部分有可能被忽略掉了,哪怕是无意造成的。   需求分析活动最开始是收集资料,通过对产品用户和顾客进行采访和讨论收集他们对将要开发的产品的观点1。例如,要执行一个组织所需的企业会计软件的需求分析,分析员2可能和该组织的所有会计师进行访谈,从而确定他们的需求。这样一组收集来的用户数据通常包含一些有矛盾和歧义的地方,因为每个用户常常对该系统会有局部和片面的看法。因此,我们有必要找出需求中所有有矛盾和歧义的地方,然后和顾客进一步商讨以解决它们。在解决了所有的歧义、不一致和不完备的问题,以及所有的需求都被正确理解之后,我们就可以展开需求说明活动了。在这项活动中,用户需求被系统地组织成一个软件需求说明(SRS)文档。   需求说明   在需求收集和分析活动中发现的客户需求被组织为一个SRS文档,该文档的重要组成部分有功能需求、非功能需求以及目标的实现。记录功能需求涉及鉴定系统支持的功能,每个功能的特点都由输入数据、输入数据上所需的处理以及将要产生的输出数据来决定。非功能需求则确定性能要求、需要遵循的标准等。SRS文档是使用终端用户术语写成的,以便客户能够理解SRS文档。毕竟,SRS文档需要顾客进行审核并批准,这是很重要的。SRS文档通常充当开发团队和客户之间签订的合同。今后客户与开发商之间的任何纠纷都可以通过SRS文档解决。因此这是一个重要的文件,开发团队必须对其彻底了解,并要和客户共同审查。SRS文档不仅提供了执行所有开发活动需要的基础,也为其他几个文档提供了基础,例如设计文档、用户手册、系统测试计划等。   本阶段结束时产生的SRS文档也被叫做该问题的“黑盒 specification”,因为在这一文档中系统被看作是一个黑盒,只有外部(即输入/输出)行为有说明(见图2-3),而内部的详情无从得知。换句话说,需求分析和说明阶段主要关注的是需要做什么,并小心规避了解决方案(如何做)的问题。在第4章中,我们会研究需求分析活动,以及开发一个良好的SRS文档涉及到的各项问题,并都进行详细的说明。 图2-3 一个系统的黑盒视图 2.2.3 设计   设计阶段的目标是,把SRS文档中说明的需求转换到在某些编程语言中适于执行的一个结构中。从技术上讲,在设计阶段中软件架构来自于SRS文档。我们有两种截然不同的设计方法可用:传统的设计方法和面向对象的设计方法。下面我们简要地介绍这两种方法的实质,第6章和第7章会详细讨论这两种方法。   传统设计方法   当前很多软件开发商都在使用传统设计方法。传统设计分为两个不同的活动。首先执行需求说明的结构分析,其中会对该问题的详细结构进行研究。其次是结构设计活动。在结构设计中,结构分析的结果会被转化为软件设计。   结构分析包括对系统支持的不同功能进行详细的分析,以及鉴定不同功能中的数据流。用户需要的每项功能都会认真研究,然后被递归分解成各种子功能。在本书中,我们将交替使用功能和过程这两个术语。除了确定系统中的各道工序之外,过程中的数据流也得以确定。我们使用数据流程图(DFD)来进行结构分析并记录结果。DFD技术会在第6章中进行讨论。结构分析往往将自己局限在问题的“需要做什么”这个方面,并小心避免讨论“如何去做”这个方面。在结构分析中,SRS文档中说明的功能要求会被分解成子功能,而这些子功能中的数据流会被分析,并以DFD的形式以图表表示出来。   结构分析活动结束时,结构设计就开始进行了。结构设计主要有两个活动:体系结构设计(也称为高层次设计)和详细设计(也称为低层次设计)。高层次设计涉及把系统分解成模块,并表示模块之间的接口和调用关系。一个高层次的软件设计有时被称为软件体系结构。在详细设计中,个别模块的内部设计更为详细,例如数据结构与模块算法被设计并记载。几个著名的方法可以用来进行体系结构设计和低层次设计。   面向对象的设计方法   面向对象的设计(OOD)方法是一种相对较新的方法。在这种方法中,发生在问题域与解决方案域中的不同对象会被首先发现,然后这些对象间存在的不同关系被发现。对象结构会被进一步完善以取得详细的设计。OOD方法有几个好处,例如更少的产品开发时间和工作量以及更好的产品可维护性。面向对象的程序设计技术会在第7章和第8章中讨论。 2.2.4 编码和单元测试   软件开发的编码和单元测试阶段(有时被称为执行阶段)的目的是把软件设计转换成源代码。设计的每个组件都会作为一个程序模块执行。这个阶段的最终产品是一系列已单独测试的程序模块。要使工程师能够写出优质的程序,每一个软件开发组织通常都会制定适合自己的编码标准。编码的标准能够解决不少问题,如确定程序代码的标准方法、制定功能和模块头部的模板、注释写法、变量和函数命名约定以及每一个模块中允许的最多代码行等。   在这个阶段中,每一个模块都会进行单元测试,这样可以确定所有单独的模块是否能够正确工作。它包括独立测试每个模块,因为这是在这一阶段调试发现错误的最有效的方式。独立测试模块的另一个原因是必须与本模块接口的其他模块可能还没有准备好。单元测试还涉及到测试用例的确切定义、测试标准和管理测试用例。我们将在第10章讨论各种编码和单元测试的技术。 2.2.5 集成和系统测试   一旦编码和单元测试完成之后,不同的模块就会被集成。在集成和系统测试阶段中,这些模块集成会有计划地进行。组成软件产品的不同模块几乎从来没有一次就能集成成功的(猜猜出现这种状况的原因)。集成通常是分多个步骤进行。在每一个集成步骤中,部分集成的系统会被测试,然后一系列原先计划好的模块也会被添加到其中。最后,当所有模块已被成功地集成与测试之后,系统测试就会进行。系统测试的目标是确保已开发的系统符合其SRS文档中列出的规定。系统测试通常由三种不同类型的测试活动构成: ● α测试(α-testing):由开发团队执行的系统测试; ● β测试(β-testing):由友好的客户群体进行的系统测试; ● 接受测试(acceptance testing):在产品交付之后由客户自身进行的系统测试,决定是否接受该产品。   系统测试通常是按照系统测试计划文档(system test plan)有计划地进行。系统测试计划确定了所有必须执行的和测试相关的活动,明确测试的时间表,并分配资源。它还列出了所有的测试用例和每个用例的预计输出。在需求说明阶段之后,系统测试计划会马上被准备好,并记录系统测试的计划。在需求说明阶段之后,我们可以仅基于SRS文档就立刻准备系统测试计划。集成和系统测试的结果均以一个测试报告(test report)的形式记载。测试报告汇总该阶段中展开的所有测试活动的结果。我们将在第10章中详细讨论各种集成和系统测试的技术。 2.2.6 维护   和开发产品自身投入的工作量相比,维护一个典型的软件产品需要更多的工作量。过去进行过的许多研究证实了这个说法,并表明开发一个典型的软件产品所需的工作量和维护所需的工作量的比例大约是40︰60。维护包括执行下列三种活动中的任何一个或多个: ● 改正产品开发阶段中未发现的错误。这叫做纠正性维护。 ● 根据客户的需求改善系统执行和加强系统的功能性。这叫做完善性维护。 ● 让软件在一个新环境中工作。例如,要在一个新的平台或新的操作系统上运行,软件就可能需要维护。这叫做适应性维护。   多种维护活动的细节会在第13章中讨论。 2.3 迭代瀑布模型   经典瀑布模型是一个理想化的模型,因为它假定在生命周期的任何阶段中,工程师不会犯任何一个开发错误。然而在实际的开发环境中,工程师确确实实地会犯很多错误,几乎在生命周期的每一个阶段都如此。这些问题的来源很多,包括忽视、假设错误、使用不适当的技术、项目工程师之间的沟通问题等。这些缺陷通常在这个生命周期中比较后面的阶段才被发现。比如,一个设计缺陷可能不为人知,直到我们在编码或测试阶段中才会发现。一旦发现了一个缺陷,工程师需要回到缺陷发生的阶段,然后重做该阶段及后面阶段中的一些工作,来纠正缺陷及消除对后期阶段的影响。所以,在所有实际的软件开发工作中,我们不可能严格遵循经典瀑布模型。在经典的瀑布模型中,我们需要从每一个阶段到其前一个阶段的反馈路径如图2-4所示,这样就可以改正在稍后阶段中发现的之前犯下的错误。   虽然几乎在开发的每个阶段中错误都是不可避免的,但我们仍然想要在错误发生时就能够探测到它们。然而,这种情况并不是总能发生。不过,这些错误应当可以尽早发现。例如,如果在设计阶段发现了一个设计问题,这个问题处理起来就容易得多;但如果是在集成和系统测试阶段才发现的话,处理起来就要困难许多。对于后面的情况,我们不仅需要重新进行设计,还要适当地重新写代码和系统测试,因此成本会更高。离它们的发生点最近的地方发现错误的原则被称为错误的阶段性遏制。这是一个重要的软件工程原理。 图2-4 迭代瀑布模型   如何实现错误的阶段性遏制呢?一项重要技术就是在每个重要的阶段之后进行审查。尽管我们会尽最大努力把错误在发生时即检测出来,但有些错误却可以逃避侦查,并且可能只有在稍后的阶段中才会被注意到,这就必须要重做已经完成的阶段。因此,有时会回过头来重做一个阶段的工作,因为该阶段中的错误在稍后阶段中被发现。工作量的分配如图2-5所示。在任何实际的开发环境中,通常需要通过瀑布阶段的几个迭代来开发最终产品。尽管如此,如果该产品是使用纯粹的经典瀑布模型开发而成,如Parnas[1972]所示,那么该产品的最终文档就需要写出来。   不管产品开发实际遵循的生命周期如何,如果最终文档反映了开发的经典瀑布模型,那么理解系统文档就变得很简单了。这个论述的基本原理由CAR Hoare[1994]使用数学定理证明的比喻进行了解释。一位数学家提出了一个单链的推论的证明,此证明来自于一个部分尝试、blind alleys和backtracks的难解的集合。 图2-5 迭代瀑布模型中不同阶段的工作量分布   虽然对瀑布模型的基本了解是了解其他开发过程所必需的,但瀑布模型仍然有很多不足之处。一些比较明显的缺点如下: ● 瀑布模型不能满意地处理一个实际的软件工程可能遭受到的不同类型的风险。例如,瀑布模型假设在下面的开发活动可以开始之前,需求都已经被完全说明了。因此,它无法适应大多数工程开始时存在的不确定性。如果工程开始时只有一个粗略大概的需求,那么它就无法在工程中被满意地使用。 ● 要想取得更好的效率和更高的生产力,大多数现实的工程都无法遵循瀑布模型所规定的严格的阶段顺序。严格遵循瀑布模型会造成系统里的阻塞状态(blocking state)。这就是说,在开始下一个活动之前,一些团队成员不得不等待一个阶段完成。很明显这是一种资源浪费,而在现实的工程中此种浪费很少能被容忍。例如,如果一个工程师工作很快,并很早就完成了分派给他的设计部分,那他不应该闲着没事儿干,他应当前进到下一个阶段去。 2.4 原型模型   原型模型指的是在执行实际软件的开发之前,我们应当建立系统的一个工作原型。一个原型是系统的一个模拟执行,和实际的软件相比,通常功能有限、可靠性较低及性能不充分。我们通常使用几个捷径来建设原型。这些捷径可能包括使用低效率的、不精确的和虚拟的函数。例如,快捷执行一个函数可能产生使用一个表查询所想要的结果,而不是执行真正的计算。一个原型通常是实际系统的一个比较粗糙的版本。   原型有好几种用处。一个重要的目的在于说明输入数据格式、消息、报告以及和客户的交互对话。这对于更好地了解客户需求而言是一个很有价值的系统。就这方面来说,在开发系统的图形用户界面(GUI)时,原型模型就很有用。这样用户就可以更加容易地使用一个工作模型进行试验从而得出自己的观点,而不是试图想象一个假设的体系进行工作。   在开发团队不清楚技术解决方案时,他们就可以使用原型模型。一个已开发的原型可以帮助工程师仔细地检查和产品开发相关的技术问题。通常来说,重要的设计决定取决于诸如一个硬件控制器的响应时间和一个归类算法的效率等问题。在这些情况下,原型可能就是最好的,或者是唯一的解决技术问题的方法。开发原型的第三个原因是因为第一次就要“完全弄好”是不可能的,因此我们就必须计划放弃第一个产品,这样后面就能开发出一个好产品。这也正是Brooks[1975]所提倡的。   软件开发的原型模型如图2-6所示。在这个模型中,产品开发的首个阶段是初次需求收集阶段。其中会执行一个快速设计,然后建立原型。已开发的原型会被发送给客户以供其评价。基于客户的反馈,需求会得到改善,然后原型就能够得到适当修改。这种收取客户反馈然后修改原型的循环过程会一直进行下去,直到客户认可了原型。实际的系统是使用迭代瀑布的方法开发的。然而,在原型模型的开发中,需求分析和说明阶段就变得多余了,因为客户认可的工作原型就是一个活生生的需求说明。 图2-6 软件开发的原型模型      原型的代码通常都是弃之不用的。然而,从原型开发中获得的经验大大帮助了开发实际的系统。因此,即使构建一个工作原型可能需要额外的开销,但对于没有明确的客户需求和有未解决的技术问题的系统来说,和使用迭代瀑布模型开发同等的系统相比,原型模型的整体开发成本可能更低。通过构建原型并提交以供用户评价,进行了原型的试验之后,很多客户的需求都能够得到准确地定义,并且技术问题也能得到解决,这样就最小化了客户的更换要求,也把相关的重新设计所需开销降到了最低。 2.5 进化模型   生命周期模型也被称为连续版本模型(successive version),有时也被称为增量模型(incremental model)。在这种生命周期模型中,软件首先被分解成几个模块(或功能单元),它们可以被逐步构建和交付(如图2-7所示)。开发团队首先会开发系统的核心模块,通过添加新的功能到连续版本中,这个起始的产品骨架会被不断地改进。我们可以使用迭代瀑布开发模型来开发所有的进化版本。进化模型如图2-8所示。 图 2-7 一个软件产品的进化开发   该产品的每一个连续版本都是一个功能完全的软件,能够比前一版本做更多有用的事情。这种开发模型有几个好处。在这个模型中,在软件的完全版本发布之前,用户有机会在部分开发的软件上进行试验。因此,在软件的不同版本的交付过程中,进化模型有助于准确定义用户需求,从而可以把整个软件交付完成之后的需求修改降到最低。另外,核心模块会得到完全地测试,从而减少最终产品中核心模块出问题的机会。此外,连续版本模型的主要缺点在于,对大多数实用问题而言,我们很难把问题分为几个可以增量执行和交付的功能单元。因此,进化模型通常仅对非常大型的产品有用,因为在其中找到增量执行的模块比较容易。通常,如果客户想要获得增量的产品,在开发过程中就可以开始使用不同的功能,而不是等待整个产品开发完成并交付后再使用,那么我们就可以使用进化模型。另外,对于面向对象的软件开发项目而言,进化模型也是很流行的,因为就对象而言系统能够很轻松地被分为几个独立的单元。 图2-8 软件开发的进化模型 2.6 螺旋模型   软件开发的螺旋模型如图2-9所示。此模型用图表示出来就是一个有许多环回的螺旋。螺旋中环回的确切数量不是固定的,每个环回都各代表软件过程的一个阶段。例如,最里面的环回可能和可行性研究相关。下一个环回则是和需求说明相关,再下一个和设计相关,依次类推。和其他模型相比,这个模型要灵活得多,因为在该模型中开发产品所需的阶段的具体数目不是固定的。我们提到的阶段只是一些例子,并且各个项目之间还有可能不一样。例如,某些项目完成设计可能需要3个或4个连续的环回,而在其他项目中可能只需要一个环回。   此模型中的每个阶段都被分为四个部分(或quadrant),如图2-9所示。第一个quadrant识别阶段的目标以及考虑对于阶段可行的备选解决方案。在第二个quadrant中,备选解决方案会被评估从而选出有可能是最好的方案。对于选中的解决方案而言,潜在的风险会被识别出来,并通过开发一个合适的原型进行处理。风险实际上就是不利的情况,可能妨碍软件项目的成功完成。例如,访问一个远程数据库的数据可能的风险就是数据访问速度太慢。我们可以构建数据访问子系统的一个原型来解决这个问题。因此,螺旋模型对处理项目风险提供直接的支持。第三个quadrant中的活动包括开发和检验产品的下一级别。第四个quadrant中的活动涉及评估客户经历过的阶段的结果,以及计划下一次螺旋旁的迭代。随着螺旋周围的每一次迭代(从中心向外运动),软件的一个更完善的版本就能够建立起来。通常,螺旋旁进行几次迭代之后,所有的风险都会被解决,而软件也可以进行开发了。在这个时候,我们就可以采用软件开发的瀑布模型。任何点上的螺旋射线都代表了项目中截至那时的开销,而角度则代表了当前阶段中取得的进步。 图2-9 软件开发的螺旋模型   在螺旋模型开发中,项目组必须决定以怎样的精确度把项目分成阶段。通常,项目首先用的是一些普通模型,接着在识别出一些特殊的风险之后就添加额外的阶段。该模型最显著的功能很可能是处理风险的能力。和其他模型不一样,此模型明确包括了开发的每一阶段中的风险处理。在每一次迭代中,风险分析通过原型构建进行,允许就处理风险可用的不同选项进行权衡。   和之前讨论的模型相反,螺旋模型可以被看作是一个元模型,因为它包含了所有讨论过的模型。例如,一个单环回的螺旋实际上代表了瀑布模型。螺旋模型使用了一种原型方法:在正式开始开发产品之前先建立一个原型。另外,螺旋模型还可以被看作是支持进化模型:沿着螺旋的迭代可以被看作是进化级别,一个完整的系统正是通过这些级别建立的,这就使得开发人员能够在每一个进化级别理解并解决风险(即螺旋式迭代)。螺旋模型把原型用作一种风险减少机制,并保留了瀑布模型系统的step-wise方法。 2.7 不同生命周期模型的比较   经典瀑布模型可以被看作是基本的模型,而所有其他的生命周期模型则是对该模型的修改。然而,经典瀑布模型不能被用在实际的开发项目中,因为该模型不支持处理在任何阶段发生的错误的机制,而迭代瀑布模型就避免了这个问题。迭代瀑布模型可能是这么多年来运用最广泛的软件开发模型。该模型易于理解和使用。然而,此模型仅对完全理解的问题适用,它并不适用于很大型的项目和风险很多的项目。   原型模型适用于用户需求和底层技术方面都不是很清楚的项目。此模型多用于开发项目的用户界面部分。   进化方法适用于可被分为一系列模块以供增量开发和交付的大型问题。该模型也被广泛用在面向对象的开发项目中。当然,此模型只在客户接受系统的增量交付时使用。   螺旋模型被称为元模型,因为它包括了所有其他的生命周期模型。风险处理是此模型所固有的。螺旋模型适用于开发技术挑战很大,而且易发生多种风险的软件产品。然而,这个模型比其他模型要复杂得多——这可能是它在一般项目中用得不多的一个原因。   让我们从客户的角度来比较一下不同的生命周期模型。首先,客户对于开发小组的信心和其采用的开发模型是高度无关的。在漫长的开发过程中,客户信心通常会下跌,因为他们没有看到任何实实在在工作的产品。开发人员使用技术名词回答客户的问题,并会宣告延迟,这就使得客户会愈加不满。另一方面,进化方法可以让客户提前体验一个工作的产品,而不用等待产品整体完工。增量模型的另一个重要优势在于它减少了用户适应一个全新的系统所要花费的时间和精力,通过增量阶段逐渐介绍产品可以让客户有时间适应新产品。此外,从客户的经济角度来讲,增量开发并不需要大量的资金,客户在他们可以负担时订购增加的版本。 2.8 小结 ● 在开发所有类型的软件产品时,遵循一个合适的过程模型已经成为一个通用的标准。尤其是对于开发大型软件产品而言,要想成功完成该项目,采用一个合适的生命周期模型是绝对必须的。 ● 我们仅讨论了一些重要的过程模型中的根本思想。良好的软件开发组织会仔细记录他们遵循的精确过程模型,通常会在文档中包括以下内容: ? 识别不同的阶段 ? 识别每个阶段中的不同活动及它们的执行顺序 ? 不同阶段的阶段出入标准 ? 执行不同活动所遵循的方法 ● 遵循一个软件生命周期模型鼓励小组成员以系统和有规律的方式执行不同的开发活动,它还使得管理软件开发项目更加简单。 ● 尽可能地及时发现错误的原则就是错误的阶段性遏制。它最小化了纠正错误的成本。 ● 在这一章中我们讨论了5种重要的生命周期模型。迭代瀑布模型是迄今为止运用最广泛的生命周期模型。 ● 经典瀑布模型可以被看作是基本的模型,而所有其他的生命周期模型则是对该模型的修改。 ● 不同的生命周期模型各有优缺点,因此,我们应为当前的问题选择一个合适的生命周期模型。实际上,软件开发组织通常会根据他们的需要修改标准的生命周期模型。 ● 即使一个组织可能遵循适合一个项目的生命周期模型,就像遵循了经典瀑布模型一样,那么也应当有最终的文档,这也使得维护人员更加容易理解产品文档。 2.9 练习   1. 如何理解“软件开发的生命周期模型”?为什么在开发一个大型软件产品时遵循一个生命周期模型很重要?   2. 如果软件开发组织没有很好地记录其软件过程,它会遇到什么问题?   3. 软件过程指的是什么?方法和过程之间的区别是什么?如果软件开发组织在开发软件时没有遵循任何系统的过程,会遇到什么问题?   4. 软件开发的瀑布模型的主要阶段是什么?在开发一个典型的软件产品时,哪个阶段消耗了最多的时间和精力?   5. 画出一个示意图来表示软件开发的迭代瀑布模型。在图上,把每个阶段末尾生成的deliverables表示出来。   6. 在可行性研究阶段执行的重要活动是什么?   7. 举例说明不适用迭代瀑布模型的软件产品开发。   8. 讨论花费在迭代瀑布模型的不同阶段上的工作量是如何随着时间的变化进行分布的。   9. 为下面这些应用开发软件,应采用哪种生命周期模型?要求说明原因。 (a) 充分了解的数据处理应用程序 (b) 通过卫星通信连接电脑的新软件产品,假设之前没有开发卫星通信软件的经验 (c) 作为电话交换系统控制器的软件产品 (d) 新的图书馆自动化软件,连接城市中的不同图书馆 (e) 超大型的软件,可以使用一套旋转的卫星,在订阅者中提供、监视和控制移动电话通信 (f) 一个新的文本编辑器 (g) 一种新语言的编译器 (h) 面向对象的软件开发 (i) 一个大型软件产品的图形用户界面   10. 原型是什么?在什么情况下建立一个原型有好处?建立一个原型总会增加软件开发的总体成本吗?   11. 在开发实际产品前先建立一个工作原型的主要好处是什么?   12. 解释在螺旋模型中开发软件如何起始,如何终结。   13. 解释为什么螺旋生命周期模型被看作是一个元模型。   14. 使用恰当的例子解释使用螺旋模型的软件开发。不同的开发计划的螺旋的环回数是固定的吗?如果不是,请解释螺旋中的环回数是怎样确定的。   15. 在软件开发的螺旋模型中怎样处理和一个项目关联的风险?   16. 比较使用迭代瀑布模型和螺旋模型进行软件开发的相对优势。使用几个合适的例子,解释您会为哪些问题采用软件开发的瀑布模型,以及为何种问题您会采用螺旋模型。   17. 用适当的例子解释进化生命周期模型更适用的产品开发类型,以及螺旋模型更为适合的问题类型。   18. 判断下列说法正确还是错误,并说明原因。 (a) 错误的阶段性遏制的主要目标是开发一个没有错误的产品。 (b) 使用原型生命周期模型开发软件产品总是比使用迭代瀑布模型开发相同产品成本更高,因为构建一个被抛弃的原型需要额外的成本。 (c) 软件开发组织开发新产品时遵循迭代瀑布模型以提供最大的客户满意度。 (d) 在所有的软件开发阶段中,如果一个错误在设计阶段产生,但直到系统验收测试时才被发现,那它的成本就会最高。   19. 解释为什么使用迭代瀑布模型开发非常大型的软件产品是不合适的。   20. 为什么不在软件产品开发结束时一次测试,而是必须要有三种不同级别的测试——单元测试、集成测试和系统测试?这些不同级别的测试的主要目的分别是什么?   21. 错误的阶段性遏制指的是什么?为什么它如此重要?如何做到错误的阶段性遏制?   22. 不论在开发软件时遵循的是哪一个生命周期模型,如果是经典的瀑布模型,为什么最终文档必须要描述产品?   23. 假设计划研发一种产品,技术含量很高,与客户有关的风险也很多,您会采用哪种生命周期模型?请说明理由。   24. 为公司的软件项目推荐一个合适的生命周期模型,该项目的客户很可能会频繁改变他的需求。请说明理由。       1 注意客户和产品的用户可能是不一样的。例如,客户可能是一个组织,而用户则可能是该组织中一些选定的雇员。 2 在软件行业中,执行需求分析和说明的工程师通常叫做分析师。 ??      ??      ??      ??    30 软件工程导论(第2版)    31 第2章 软件生命周期模型