软件工程(第4版•修订版)

978-7-115-31347-8
作者: 【美】Shari Lawrence Pfleeger 【加】Joanne M.Atlee
译者: 杨卫东
编辑: 杨海玲

图书目录:

详情

本书是软件工程领域的经典著作,国际上众多名校均采用本书作为教材。本书分为3个部分。第一部分解释为什么软件工程知识对实践者和研究者同样重要,还讨论了理解过程模型问题的必要性以及敏捷方法和精细地进行项目计划的必要性;第二部分论述开发和维护的主要步骤;第三部分主要讲述软件评估和改进。

图书摘要

软件开发方法学精选系列

Software Engineering:Theory and Practice,Fourth Edition

软件工程(第4版·修订版)

[美]Shar.Lawrenc.Pfleeger [加]Joann.M.Atlee 著

杨卫东 译

人民邮电出版社

北京

图书在版编目(CIP)数据

软件工程:第4版修订版/(美)弗里格(Pfleeger,A.L.),(加)阿特利(Atlee,J.M.)著;杨卫东译.--北京:人民邮电出版社,2014.3

(软件开发方法学精选系列)

书名原文:Software engineering:theory and practice,Fourth Edition

ISBN 978-7-115-31347-8

Ⅰ.①软… Ⅱ.①弗…②阿…③杨… Ⅲ.①软件工程 Ⅳ.①TP311.5

中国版本图书馆CIP数据核字(2013)第277036号

内容提要

本书是软件工程领域的经典著作,国际上众多名校均采用本书作为教材。全书共分为14章,分3个部分介绍主要内容。第一部分解释为什么软件工程知识对实践者和研究者同样重要,还讨论了理解过程模型问题的必要性以及敏捷方法和精细地进行项目计划的必要性;第二部分论述开发和维护的主要步骤;第三部分主要讲述软件评估和改进。

本书适合作为计算机相关专业软件工程课程的本科教材,也适用于介绍软件工程的概念与实践的研究生课程,期望进一步学习该领域相关知识的专业人员也可以阅读本书。

◆著 [美]Shari Lawrence Pfleeger [加]Joanne M.Atlee

译 杨卫东

责任编辑 杨海玲

责任印制 程彦红 焦志炜

◆人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

三河市海波印务有限公司印刷

◆开本:787×1092 1/16

印张:35.5

字数:1052千字  2014年3月第2版

印数:6001-9000册  2014年3月河北第1次印刷

著作权合同登记号 图字:01-2009-5712号

定价:79.00元

读者服务热线:(010)81055410 印装质量热线:(010)81055316

反盗版热线:(010)81055315

广告经营许可证:京崇工商广字第0021号

版权声明

Authorized translation from the English language edition,entitled Software Engineering:Theory and Practice,Fourth Edition,9780136061694 by Shari Lawrence Pfleeger and Joanne M.Atlee,published by Pearson Education,Inc.,publishing as Pearson Higher Education,Copyright © 2010,2006,2001,1998 by Pearson Education,Inc.

All rights reserved.No part of this book may be reproduced or transmitted in any form or by any means,electronic or mechanical,including photocopying,recording or by any information storage retrieval system,without permission from Pearson Education,Inc.

CHINESE SIMPLIFIED language edition published by PEARSON EDUCATION ASIA LTD.and POSTS & TELECOM PRESS Copyright © 2014.

本书中文简体字版由Pearson Education Asia Ltd.授权人民邮电出版社独家出版。未经出版者书面许可,不得以任何方式复制或抄袭本书内容。

本书封面贴有Pearson Education(培生教育出版集团)激光防伪标签,无标签者不得销售。

版权所有,侵权必究。

前言

跨越研究与实践之间的鸿沟

在1968年的北约会议上,首次使用了“软件工程”这一术语。时至今日,软件工程已经走过了很长的一段路,软件本身也已经以各种形式融入了我们的生活。就算在10年前,估计也没人会预料到软件会有这么大的影响力。因此,要懂得如何开发好的软件以及如何评估软件在日常生活中面临的风险和机遇,坚实的软件工程理论和实践基础是不可或缺的。本书体现了当前软件工程领域实践者阵营和研究者阵营之间相互融合的现状:实践者主要关注构造高质量产品完成某些功能,而研究者则努力寻找各种方法改进产品质量以及提高开发人员生产效率。Edsgar Dykstra不断提醒我们:研究与实践之间的紧张关系将检验我们对软件工程的理解,并帮助我们改进思维方式、方法,进而最终改进我们的产品。

正是本着这种精神,我们对本书进行了增订,为这种不断的探究和改进构造一个基础框架。尤其是,第4版纳入了更广泛的素材,来说明如何抽象出一个问题并对它建立模型,以及如何使用各种模型、设计原则、设计模式和设计策略,来产生适当的解决方案。软件工程师绝不止像程序员那样按照说明书来编写程序,就像厨师不止是遵循菜谱来烹饪。构造优秀的软件是一门艺术,这体现在如何抽象出问题的要素并建模,再使用这些抽象设计出解决方案。经常能听到优秀开发人员谈论“优雅的”解决方案,说明这样的解决方案抓住了问题的核心,得到的软件不仅能够解决当前的问题,而且当问题随着时间演化时,软件也能够很容易地进行修改。这样,学生就能够学会融合研究与实践、艺术与科学,构造出坚实的软件。

科学总是以事实为基础的。本书是为本科生软件工程课程而设计的,注重软件工程研究与实践的实际效果层面,使学生能够直接将所学知识应用于要解决的现实问题。书中所举的例子针对的是经验有限的学生,但是,这些例子清楚地阐明了大型软件开发项目是如何从需求到计划,再进而成为现实的过程。例子所描述的许多情形,读者未来都很可能经历:大型项目与小型项目、“敏捷”方法与高度结构化方法、面向对象与面向过程的方法、实时处理与事务处理、开发情形与维护情形。

本书也适用于介绍软件工程的概念与实践的研究生课程,还适合于那些期望进一步学习该领域相关知识的专业人员。尤其是最后3章给出了一些引人思考的资料,旨在引起研究生对当前研究主题的兴趣。

主要特色

下面是本书区别于其他书的主要特色。

其他软件工程图书将度量和建模作为单独的问题分开考虑,本书则将二者与更全面的软件工程论述结合起来。也就是说,把度量和建模看做是软件工程策略不可分割的部分,而不作为一个单独的分支。这样,学生能够学会如何抽象与建模,以及如何在日常开发活动中进行定量评估和改进。他们可以利用他们的模型理解所要解决的问题的要素,了解可供选择的其他解决方案;他们可以利用度量对个人、小组和项目的进度进行评估。

类似地,诸如复用、风险管理和质量工程之类的概念都揉合到在它们影响之下的软件工程活动中,而不是作为单独的问题来讲述。

这一版介绍了敏捷方法的使用,包括极限编程。它论述了给予开发人员更多自主权所带来的利益和遭遇的风险,并将敏捷方法与传统的软件开发方法进行了比较。

每一章都将相应的概念应用于两个贯穿全书的例子:一个例子是典型的信息系统,另一个是实时系统。两个例子都基于实际的项目。信息系统的例子描述了一个大型英国电视公司确定广告时间价格的软件。实时系统的例子是阿丽亚娜5型火箭的控制软件,我们将考查该软件见诸报端的一些问题,并探讨软件工程技术如何能够帮助找出并避免其中的问题。学生能够随着两个典型项目的进展,领会本书所描述的各种实践方法如何融入到构造系统的技术之中。

每章结尾的结论都用三种形式表述:这一章的内容对开发团队的意义,对单个开发人员的意义,以及对研究者的意义。学生可以很容易地复习每章的要点,并了解每一章与研究以及实践的相关性。

本书有配套网站,网址是http://www.prenhall.com/pfleeger。该网站包含了各种文献中的最新例子,以及真实项目中的实际工件(artifact)的例子。该网页还提供了相关工具和方法厂商的网页的链接。学生可以在这里找到实际的需求文档、设计方案、代码、测试计划等内容。那些想寻找更多、更深入信息的学生,可以通过该网页找到其他值得信赖的、容易理解的出版物及网站。这些网页会定期更新,以及时补充书中的内容。它还包括一个工具,读者可用来向作者和出版社提供反馈。

学生学习指南可从Pearson的销售代表处获得。

PPT及完整的习题答案手册可以从本书配套网站的教师资源中心获得。要获得访问权限,请与Pearson的销售代表联系。

本书从各种文献中引用了大量案例研究和例子。书中以补充材料形式给出的很多只有一页左右篇幅的案例研究在网页上都有详细描述。据此,学生能够了解本书中的理论概念是如何应用于现实情形的。

每章最后都列出了引人思考的、与软件工程有关的法律和道德问题。学生可以根据软件工程所处的社会和政治背景来理解软件工程。与其他的学科一样,软件工程的结果会影响到人,必须据此来考虑软件工程决策。

每章都强调过程化和面向对象的开发。另外,第6章专门解释了面向对象开发过程的步骤。我们会讨论若干设计原则,并用面向对象的例子展示如何结合这些原则来改善设计。

本书还有一个带有注解的参考文献列表,其中不少是软件工程中开创性的论文。另外,本书的网页有进一步的链接指向注释的参考文献和特定领域讨论组(诸如软件可靠性、容错性、计算机安全等)。

每章都会提到同一个学期项目,是开发一个住房抵押处理系统软件,教师可以把这个学期项目稍加改动,作为课程作业。

每章的结尾都列出了本章概念的主要参考文献,学生能够借此进一步深入研究本章中讨论的特定工具和方法。

这一版还包含了强调计算机安全性的例子。尤其是,我们强调了设计时的安全性,而不是在编码或测试的时候加入安全性。

本书的内容和组织结构

本书分为14章,分3部分介绍各章主要内容。第1部分力图激发读者学习软件工程的兴趣,解释为什么软件工程知识对实践者和研究者同样重要。第1部分还讨论理解过程问题的必要性,确定开发人员具有多大程度“敏捷性”的必要性和精细地进行项目计划的必要性。第2部分论述开发和维护的主要步骤,这些步骤与构造软件所使用的过程模型关系不大,都是先引出需求、对需求建模、检查需求,然后设计问题的解决方案,编写和测试代码,最后将软件交付给客户。第3部分主要讲述软件评估和改进,这一部分着眼于如何评价过程和产品的质量,以及如何改进质量。

第1章:软件工程概述

这一章介绍软件工程的发展历程,以激发读者的学习兴趣,并简要介绍在后面的章节中要研究的某些关键问题。尤其是,讨论Wasserman用来帮助定义软件工程的关键因素:抽象、分析与设计方法、表示法、模块化与体系结构、软件生命周期与过程、复用、度量、工具与集成环境,以及用户界面与原型化。我们将讨论计算机科学和软件工程的差异,解释可能遇到的一些主要问题,并为本书的其余部分奠定基础。还探讨采用系统的方法构造软件的必要性,并介绍每一章都会用到的两个公共的例子。学期项目的背景也是在这里介绍的。

第2章:过程和生命周期的建模

这一章概要介绍不同类型的过程和生命周期模型,包括瀑布模型、V模型、螺旋模型和各种原型化模型。论述敏捷方法的必要性,与传统的软件开发过程相比,在敏捷方法中开发人员拥有许多自主权。还描述几种建模技术和工具,包括系统动态建模以及其他常用方法。两个公共的例子都用到了这里介绍的一些建模技术。

第3章:计划和管理项目

在这一章,我们着眼于项目计划和进度安排。介绍诸如活动、里程碑、工作分解结构、活动图、风险管理、成本以及成本估算等概念。用估算模型估算两个公共例子的成本和进度。我们重点关注实际案例研究,包括F-16飞机和DEC的alpha AXP程序的软件开发管理。

第4章:获取需求

这一章强调在优秀的软件工程中抽象和建模的关键作用。尤其是,我们使用模型澄清需求中容易误解和遗漏的细节,并使用模型与其他人员进行沟通。本章中探讨了多种不同的建模范型;针对每一种范型,研究表示法实例;讨论什么时候使用哪种范型,并对如何做出特定建模和抽象决策给出建议。讨论了需求的不同来源和类型(功能性需求、质量需求与设计约束)。解释如何编写可测试的需求,并描述如何解决其中的冲突。其他讨论的主题还有需求引发、需求文档、需求评审、需求质量、如何测量需求,并介绍了一个如何选择规格说明方法的例子。本章最后将其中几个方法应用于两个公共的例子。

第5章:设计体系结构

在本书的第4版中,完全改写了软件体系结构这一章。在这一章的开始,论述了体系结构在软件设计过程中以及在较大型的开发过程中的作用。我们详细讨论了产生软件体系结构的相关步骤,从建模、分析、文档化和评审,到最后产生软件体系结构文档;程序设计人员用软件体系结构文档来描述模块和接口。我们讨论了如何对问题进行分解,以及如何使用不同的视图来检查问题的若干方面,以便找到合适的解决方案。接着,我们集中讨论了如何使用一种或多种体系结构风格来建模解决方案,包括管道—过滤器、对等网络、客户/服务器、发布—订阅、信息库和分层体系结构。我们还讨论了如何组合体系结构风格并使用它们来达到质量目标,如可修改性、性能、安全性、可靠性、健壮性、易使用性。

一旦完成了初始的体系结构,就可以对其进行评估和改进。在这一章,我们说明了如何测量设计质量,以及如何使用评估技术在故障分析、安全性分析、权衡分析以及成本效益分析中选择满足客户需求的体系结构。我们强调了把设计理念记入文档、确认和验证设计是否匹配需求、创建满足客户产品需求的体系结构这三方面的重要性。在本章快结束的时候,我们详细论述了如何构建一个产品线体系结构,以允许软件提供者在一族相似的产品中复用设计。最后,讨论了信息系统和实时系统这两个例子的体系结构分析。

第6章:设计模块

这一版中,第6章做了全面的修订,讨论了如何从系统体系结构的描述转向单个模块设计的描述。本章从讨论设计过程开始,然后介绍6个关键的设计原则来指导我们由体系结构到模块的细化设计:模块化、接口、信息隐藏、增量式开发、抽象和通用性。接着,进一步探讨面向对象的设计以及面向对象设计是如何支持这6个设计原则的。这一章使用统一建模语言的各种表示法来说明如何表示模块功能性和交互的多个侧面,这样,我们就能够构造健壮的、可维护的设计。本章还论述了一组设计模式,其中每一个设计模式都具有明确的目的,并说明了如何使用它们来强化设计原理。接下来,本章讨论了一些全局的问题,如数据管理、异常处理、用户界面和框架,我们从中可以了解到一致而清晰地使用方法可以获得更有效的设计。

本章详细探讨面向对象度量,并将一些常用的面向对象度量方法应用于一个服务站的例子。我们可以从中看到,设计中发生变化后,度量中的值是如何变化的,这有助于我们决定如何分配资源和查找故障。最后,我们把面向对象的概念应用到信息系统和实时系统的例子中。

第7章:编写程序

本章论述代码层的设计决策,以及实现一个设计以产生高质量代码所涉及的问题。我们讨论各种标准和过程,并给出一些简单的编程准则。提供了用多种语言编写的例子,包括面向对象语言和过程语言。讨论了程序文档和错误处理策略的必要性。最后,将一些概念应用于两个公共的例子。

第8章:测试程序

这一章探讨一些测试程序方面的问题。我们把传统测试方法和净室方法区分开,并着眼于如何测试各种系统。给出软件问题的定义和分类,讨论怎样利用正交缺陷分类使数据汇集和分析更加有效。随后,解释单元测试与集成测试之间的区别。在介绍几种自动化测试工具和技术之后,解释测试生命周期的必要性,以及如何将这些工具集成到生命周期中。最后,把这些概念应用于两个公共的例子。

第9章:测试系统

这一章首先介绍系统测试的原理,包括测试包和数据的复用,并讨论精细地进行配置管理的必要性。介绍的概念包括功能测试、性能测试、验收测试和安装测试。讨论测试面向对象系统的特殊需要。描述几种测试工具,并讨论测试小组成员的角色。然后,向读者介绍软件可靠性建模,并讨论可靠性、可维护性和可用性问题。读者将学会如何使用测试结果来评估交付的产品可能具有的特征。本章还介绍几种测试文档,最后描述两个公共例子的测试策略。

第10章:交付系统

这一章讨论培训和文档的必要性,并给出信息系统和实时系统例子中可能使用的几个培训和文档的例子。

第11章:维护系统

这一章强调系统变化的结果。解释在系统生命周期的过程中变化是如何发生的,以及系统设计、编码、测试过程、文档如何必须与这些变化保持一致。讨论典型的维护问题,以及精细地进行配置管理的必要性。全面讨论使用度量预测可能的变化,并评估变化所产生的影响。还讨论在使遗留系统再生的大背景下的再工程和重组技术。最后,根据变化的可能性对两个公共的例子进行评估。

第12章:评估产品、过程和资源

由于许多软件工程的决策涉及合并和集成现有组件,这一章讨论评估过程和产品的方法。讨论经验性评估的必要性,并给出若干例子以说明如何使用度量建立质量和生产率的基线。我们着眼于几个质量模型,如何评估系统的复用性,如何执行事后分析,以及如何理解信息技术的投资回报。把这些概念应用于两个公共的例子。

第13章:改进预测、产品、过程和资源

这一章建立在第11章的基础之上,说明如何完成预测、产品、过程和资源改进。包含几个深入的案例研究,以说明如何通过多种调查技术来理解和改进预测模型、审查技术及软件工程的其他方面。本章最后给出一组指导原则,用于评估当前情形并识别改进的机会。

第14章:软件工程的未来

在最后这一章,讨论软件工程中的若干悬而未决的难题。我们重新回顾Wasserman的概念,来审视软件工程这一学科的发展状况。研究技术转移和决策制定中的若干问题,以确定在把重要的思想从研究应用于实践方面,我们做得是否出色。最后,我们研究一些有争议的问题,比如给软件工程师发放职业证书的问题,以及向更特定领域解决方案和方法发展的趋势。

致谢

由于朋友及家人所给予的技术上的支持和情感上的鼓励,本书才得以完成。我们不可能列举出所有在本书的编写和修订期间帮助过我们的人,若有遗漏,在此提前表示歉意。我们很感谢本书早期版本的读者,他们详细审读了本书,提出了不少良好建议。我们已经尽力将所有这些建议融入这一版本。我们一如既往地感谢来自读者的反馈,不管是正面的还是负面的。

Carolyn Seaman(马里兰大学巴尔的摩校区)是本书第1版非常杰出的审稿人。她提出许多方法使内容更简洁明了,使得本书更紧凑、更易于理解。她还整理好大部分的练习解答,并帮助我们建立本书早期版本的网站。我要感谢她的友谊和帮助。Yiqing Liang和Carla Valle更新了该网站,并为第2版增加了重要的新资料;Patsy Ann Zimmer(滑铁卢大学)修订了本书第3版的网站,特别是关于建模表示法和敏捷方法。

我们万分感谢Forrest Shull(马里兰大学法朗霍夫中心)以及Roseanne Tesoriero(华盛顿学院),他们编写了本书最初的学习指导;感谢Maria Vieira Nelson(巴西米纳斯吉拉斯州天主教大学),他对第3版的解答手册及学习指导进行了修订;感谢Eduardo S.Barrenechea(滑铁卢大学)对第4版中素材的更新。还要感谢Hossein Saiedian(堪萨斯大学)制作了第3版和第4版的PowerPoint演示。我们要特别感谢Guilherme Travassos(里约热内卢联邦大学),本书使用了他与Pfleeger在马里兰大学期间共同编写的材料,而且他在以后的课程教学中,又对这些材料进行了大量扩充。

对我们有所帮助且颇有创见的本书所有4个版本的审稿人有:Barbara Kitchenham(英国基尔大学)、Bernard Woolfolk(朗讯公司)、Ana Regina Cavalcanti da Rocha(里约热内卢联邦大学)、Frances Uku(加利福尼亚大学伯克利分校)、Lee Scott Ehrhart(MITRE公司)、Laurie Werth(得克萨斯大学)、Vickie Almstrum(得克萨斯大学)、Lionel Briand(挪威Simula研究室)、Steve Thibaut(佛罗里达大学)、Lee Wittenberg(新泽西基恩学院)、Philip Johnson(夏威夷大学)、Daniel Berry(加拿大滑铁卢大学)、Nancy Day(滑铁卢大学)、Jianwei Niu(滑铁卢大学)、Chris Gorringe(英国东英吉利大学)、Ivan Aaen(奥尔堡大学)、Damla Turget(中佛罗里达大学)、Laurie Williams(北卡罗来纳州立大学)、Ernest Sibert(锡拉丘兹大学)、Allen Holliday(加州大学,福乐顿市)、David Rine(乔治梅森大学)、Anthony Sullivan(得克萨斯大学,达拉斯)、David Chesney(密西根大学,安娜堡)、Ye Duan(密苏里大学)、Rammohan K.Ragade(肯塔基大学)以及Prentice Hall提供的一些不具名的评阅人。与下列人员的讨论使本书得到了许多改进和加强:Greg Hislop(德雷克赛尔大学)、John Favaro(意大利Intec.Sistemi公司)、Filippo Lanubile(意大利巴里大学)、John d’Ambra(澳大利亚新南威尔士大学)、Chuck Howell(MITRE公司)、Tim Vieregge(美国军方计算机应急反应组)以及James Robertson和Suzanne Robertson(英国大西洋系统行业协会)。

感谢Toni Holm以及Alan Apt,他们使本书第3版的创作显得有趣并相对轻松;也感谢James Robertson和Suzanne Robertson让我们使用皮卡地里例子,还要感谢Norman Fenton同意使用我们软件度量一书中的资料;我们非常感谢Tracy Dunkelberger,她在我们编纂本书第4版的时候给予我们鼓励,我们也感激她的耐心和专业精神;还要感谢Jane Bonnell和Pavithra Jayapaul的完美工作。

这里,我们非常感谢一些出版商允许我们引用一些图和例子。来自于Complete Systems Analysis (Robertson and Robertson 1994)和Mastering the Requirements Process(Robertson and Robertson 1999)的资料是从网站www.dorsethouse.com上抽取的,得到了Dorse.House出版社的使用许可。练习1-1中的文章是在美联社的许可下从《华盛顿邮报》中引用的。图2-15及图2-16是在John Wiley & Sons公司的许可下,从Barghouti等人的著作(Barghouti et al.1995)中引用的。图12-14和图12-15是在John Wiley & Sons公司的许可下,从参考文献(Rout 1995)中引用的。

在电气和电子工程师学会(IEEE)的许可下,我们引用了第2、3、4、5、9、11、12和14章中标注有IEEE版权的图和表。类似地,第14章中标注有ACM版权的三个表,是在计算机协会(ACM)的许可下引用的。表2-1和图2-11是在软件生产力联合技术发展中心的许可下,引用于参考文献(Lai 1991)。来自参考文献(Graham 1996a)的图8-16、图8-17是在Dorothy R.Graham的许可下引用的。在公众利益科学中心(华盛顿特区西北部康涅狄格大街1875号)的许可下,图12-11和表12-2改编自参考文献(Liebman 1994)。表8-2、表8-3、表8-5和表8-6是在McGraw-Hill公司的许可下引用的。来自于参考文献(Shaw and Garlan 1996)、(Card and Glass 1990)、(Grady 1997)以及(Lee and Tepfenhart 1997)的图和例子是在Prentice Hall的许可下引用的。

经过Norman Fenton的许可,表9-3、表9-4、表9-6、表9-7、表13-1、表13-2、表13-3、表13-4,以及图1-15、图9-7、图9-8、图9-9、图9-14、图13-1、图13-2、图13-3、图13-4、图13-5、图13-6和图13-7(部分或全部)引用或改编于Fenton和Pfleeger的著作(Fenton and Pfleeger 1997)。在Norman Fenton的善意许可下,图3-16、图5-19和图5-20引用或改编于Norman Fenton的课程注解。

我们特别感谢我们的雇主,兰德公司以及滑铁卢大学,他们分别给我们以鼓励。[1]我们感谢朋友及家人的关心、支持和耐心,写作本书占用了我们本应共度的时光。尤其是,Shari Lawrence Pfleeger要感谢皇家服务站的经理Manny Lawrence,也感谢他的簿记员Bea Lawrence,不仅因为他们与她及她的学生在皇家系统规格说明上的合作,而且因为他们作为父母所给予Shari的关爱和指导。Jo Atlee特别感谢她的父母,Nancy和Gary Atlee,他们对她所做的或尝试要做的任何事情都给予支持和鼓励;也感谢她的同事及学生,他们在本书的主要创作期间,愉快地承担了许多的额外工作。我们最要特别感谢的是Charles Pfleeger和Ken Salem,他们是我们获得赏识和持续不断的支持、鼓励和好心情的源泉。

Shari Lawrence Pfleeger

Joanne M.Atlee

[1].请注意,本书并不是兰德公司的产品,也未经兰德的质量保证过程。本书的相关工作仅代表作者个人,并不代表我们所在的机构。

第1章 软件工程概述

本章讨论以下内容:

软件工程的含义;

软件工程的发展历程;

“好的软件”的含义;

为什么系统的方法是重要的;

自20世纪70年代以来,软件工程是如何变革的。

软件在生活中随处可见,我们时常认为,软件理所当然地应该使我们的生活更加舒适、高效和有效。例如,准备早餐面包这样一个简单的任务中,烤箱中的代码控制面包颜色的深浅,以及何时将烤好的面包弹出;住宅的电力供应由程序控制和调节;能源使用的账单均由软件记录输出。实际上,我们可以使用自动化程序支付电费账单、订购更多食品,甚至购买一台新烤箱!现在,几乎在我们生活的各个方面,包括影响我们健康和福利的关键系统,软件都发挥着作用。正因为如此,软件工程比以往任何时候都更加重要。好的软件工程实践必须确保软件在我们的生活中发挥积极的作用。

本书强调软件工程中的关键问题,描述我们所了解的技术和工具,以及它们如何影响我们构建和使用的最终软件产品。我们将从理论和实践两个方面说明我们所了解的软件工程以及如何将其应用于通常的软件开发或维护项目中。我们也将研究那些我们还不了解但有助于使产品更加可靠、安全、有用、易理解的理论和实践。

首先,我们探讨如何分析问题以及寻求解决方案;然后,研究计算机科学问题与工程问题之间的区别。我们的最终目标是,生产出高质量软件,进而找到解决方案,并考虑那些对质量有影响的特性。

我们还将探讨软件系统的开发人员已经取得了多大的成功。通过对几个软件失效的例子进行研究,可以了解在掌握高质量软件开发的艺术方面,软件系统的开发人员已经取得的进展以及还需要在哪些方面做出努力。

接着,探讨软件开发涉及的人员。在描述客户、用户和开发人员的角色和责任之后,会转而研究系统本身。我们看到,可以把一个系统看作是与活动相关的一组对象,并且处于某个边界之内。或者,我们从工程师的角度考虑系统:可以像盖房子一样开发一个系统。在定义了构造系统的步骤以后,将讨论每个步骤中开发团队的角色。

最后,讨论影响我们实践软件工程方式的一些变化,给出Wasserman的将实践融为一体的8个概念。

1.1 什么是软件工程

软件工程师要利用与计算机和计算相关的知识来解决问题。通常情况下,我们要处理的问题是与计算机或现有的计算机系统相关的。但是,也有时候,解决问题的潜在困难与计算机无关。因此,首先要理解问题的本质,这是至关重要的。尤其是,我们必须十分谨慎,不要把计算机器或技术按我们的意愿强加给遇到的每个问题。我们必须首先解决这个问题;然后,如果需要的话,再把技术作为工具来实现我们的解决方案。本书假定,我们的分析已经表明,某种计算机系统对于解决手头特定的问题是必需的或是合适的。

1.1.1 问题求解

多数问题是大型问题,往往难以处理,尤其是,当它含有一些以前从未解决过的新问题的时候。因此,首先要通过分析(analyzing)对问题进行研究,也就是说,将问题分解成可以理解并能够处理的若干小的部分。这样,就可以将较大的问题变为一组较小的问题以及它们之间的关系。图1-1说明了如何进行分析。子问题之间的关系(用图中的箭头以及子问题的相对位置来表示)与子问题本身同样是非常重要的。有时,正是这种子问题之间的关系而不是子问题本身提供了解决大型问题的线索。

一旦分析了这个问题之后,就需要根据针对问题不同方面的构件来构造解决方案。图1-2说明了这样的逆向过程:合成(synthesis)是把小的构造块组合成一个大的结构。如同分析一样,将每个解决方案组合在一起可能与寻找解决方案本身同样具有挑战性。要了解其中的原因,可以考虑写一本小说的过程:字典中包含了写作过程中可能用到的所有单词,但是,写作最困难的部分是决定如何将单词组织好写成句子,以及如何将句子组织为段落,乃至于如何把章节组织成一本完整的书。因此,任何问题求解技术都必须包括两部分:通过分析问题来确定问题的本质含义,然后,再基于分析来合成解决方案。

为了帮助我们解决问题,我们会采用各种方法、工具、过程和范型。方法(method)或技术(technique)是产生某些结果的形式化过程。例如,厨师可能使用严格定时的、有序的方式,把一系列调料成分组合成调味汁,使得调味汁变稠,但不会凝结或散开。调制调味汁的过程包括时间选择和调料成分,但并不依赖于使用了哪种烹饪设备。

工具(tool)是用更好的方式完成某件事情的设备或自动化系统。这个“更好的方式”可能是工具使我们更精确、更高效或生产率更高,也可以是它能够提高最终产品质量。例如,我们使用打字机或者键盘加打印机来写信,因为这样写出的信件比手写的更易于阅读。再如,我们使用剪刀作为工具是因为用它裁纸要比直接用手撕纸更快、更整齐。然而,要做好一件事情,工具并不总是必需的。例如,使调味汁更美味的是烹调技术,而不是厨师用的锅和羹匙。

过程(procedure)如同食谱:把工具和技术结合起来,共同生产特定产品。例如,就像后面的章节所论述的那样,测试计划描述测试过程:它告诉我们,在什么情况下将使用哪种工具处理哪个数据集,以便我们能够确定软件是否满足了需求。

最后,范型(paradigm)就像烹饪风格。它表示构造软件的特定方法或哲学。就像我们能够区分法国菜烹饪法与中国菜烹饪法一样,我们也能够区分面向对象开发和过程开发这样的范型。并不是某一种范型就一定比另一种范型好,每一种范型都有其各自的优缺点,只是在某些情况下,一种范型可能要比另外一种范型更合适。

软件工程师使用工具、技术、过程和范型来提高软件产品的质量。他们的目标就是使用高效的、高生产率的方法形成相关问题的有效解决方案。下面的章节将重点介绍支持我们所说的开发和维护活动的特定方法。读者可以在本书的万维网主页中找到相关工具和技术的最新链接。

1.1.2 软件工程师的角色是什么

要理解软件工程师在计算机科学领域中所扮演的角色,我们先看看另外一个学科的例子。考虑一下化学研究以及用它来解决问题这两者之间的区别。化学家研究化学物质:它们的结构、它们的相互作用以及现象背后的理论。而化学工程师则用化学家的研究结果解决各种问题。化学家将化学作为研究对象;但对化工人员而言,化学是用来解决一般问题(这个问题本质上甚至可能不是“化学”领域的问题)的工具。

我们可以从类似的角度看待计算技术。人们可以集中精力研究计算机和程序设计语言,也可以把它们看作是用于设计和实现问题解决方案的工具。软件工程显然属于后一种情形,如图1-3所示。软件工程师的精力集中于把计算机作为问题求解的工具,而不是研究硬件设计或者算法的理论证明。在本章的后面,我们会了解到,软件工程师使用计算机的功能进行工作,并将其作为一般解决方案的一部分,而不是研究计算机本身的理论和结构。

1.2 软件工程取得了哪些进展

编写软件既是一门艺术也是一门科学。作为一名计算机专业的学生,深入理解这句话是非常重要的。计算机科学家和软件工程研究人员研究的是计算机的机制,并建立起使它们具有更高生产率、更有效的理论。但同时,他们也设计计算机系统并编写程序,执行相关任务,这是一项融合艺术、天赋和技巧的实践性工作。在具体的系统上执行特定任务,可能会存在很多方法,但是,某一种方法可能更有效、更精确、更易于修改、更容易使用或更便于理解。任何黑客都能够编写代码完成工作,但是,要写出健壮的、易于理解和维护的并且能以最高效的方式完成工作的代码,必须具备专业软件工程师的技巧和洞察力。因此,软件工程的目标就是设计和开发高质量软件。

在学习生产高质量软件系统所需要的知识之前,先回头了解一下我们取得了哪些成就。考虑这样一个问题:用户是否对现有软件系统感到满意?答案可能是“满意”,也可能是“不满意”。一方面,软件使我们比以往任何时候都能够更快、更有效地完成任务。例如,想一下,在字处理、表格处理、电子邮件或网络电话等现代化技术出现之前,人们的生活是怎样的呢?软件支撑着医学在生命维持治疗或生命救治方面的进展,以及农业、交通和其他诸多产业取得进展;另外,软件已经使我们能够做到以前从来不敢想象的事情,如显微外科手术、多媒体教育、机器人等。

但是,软件也不是完美无缺的,系统的功能通常并不完全符合用户的期望。我们都听说过系统几乎不能运行这样的事情。我们所有人都编写过有故障的程序:代码包含错误,但也刚好可用或足以证明一个方法的可行性。显然,如果开发出这样的系统交付给客户,客户是不能接受的。

课程项目中的错误和大型软件系统中的错误不可同日而语。事实上,软件故障和生产无故障软件的困难性是文献和闲谈中经常讨论的问题。有些故障仅仅是令人讨厌,而有一些故障则会耗费大量的时间和金钱,甚至还有一些可能会危及生命。补充材料1-1解释了故障、错误和失效之间的关系。我们在此讨论几个关于故障的例子,看一看问题出在哪里以及其中的原因是什么。

补充材料1-1 描述“bug”的术语

我们常常会谈到软件中的“bug”,根据不同的上下文,它有多种含义。“bug”既可以指解释需求时犯的错误,也可以指一段代码中的语法错误,还可以指由于未知因素引起系统崩溃的原因。IEEE有描述软件产品(IEEE 1983)中“bug”的标准术语(见IEEE标准729)。

当人们在进行软件开发活动的过程中出错时(称为错误(error)),就会出现故障(fault)。例如,设计人员可能误解了某个需求,创建出与需求分析人员和用户的实际意图不相符的设计。这个设计故障是一种错误的编码,可能导致其他故障,如不正确的代码或用户手册中不正确的描述等。因此,单个错误可能产生多个故障,并且故障可能驻留在任何开发或维护的产品中。

失效(failure)是指系统违背了它应有的行为。它可能会在系统交付前或交付后被发现,也可能在测试过程中或者在运行和维护的过程中被发现。我们在第4章会看到,需求文档可能会包含故障,所以即使系统按照需求规格说明来运行,如果它未进行应有的行为,也称为失效。

因此,故障是系统的内部视图,这是从开发人员的角度看待系统;而失效是系统的外部视图,它是用户所看到的问题。并非每一个故障都对应于一个失效,例如,如果不执行故障代码,或者不进入某个特定状态,那么故障就不会使代码失效。图1-4所示说明了失效的起源。

20世纪80年代早期,美国国家税务局(Internal Revenue Service,IRS)雇佣Sperry公司构建一个联邦收入所得税表格自动处理系统。根据《华盛顿邮报》的报道,“系统……被证明难以承载当前的工作负荷,成本几乎是预算的两倍,必须尽快更换”(Sawyer 1985)。在1985年,还需花费额外的9千万美元来升级Sperry公司最初价值1.03亿美元的设备。另外,因为出现的问题妨碍了IRS在最后期限前及时向纳税人退税,IRS被迫向纳税人支付4020万美元的利息,并且要向自己的员工支付2230万美元的加班工资。到1996年的时候,情况仍未改善。《洛杉矶时报》在3月29日报道:除了6000页的技术文档外,目前系统的升级仍未有整体规划。国会议员Jim Lightfoot把这个项目称为“因为规划失当而正在挣扎的40亿美元的惨败”(Vartabedian 1996)。

诸如此类的情况仍然时有发生。在美国,联邦调查局(FBI)的“三部曲”(Trilogy)项目尝试着更新FBI的计算机系统。但其结果却是毁灭性的:“经历了四年多的艰苦工作,花费了5亿美元,但是,据报道:‘三部曲’项目几乎没有改善陈旧的案例管理系统,迄今为止,该系统仍然混乱不堪,深陷于带有绿色屏幕的大型机和大量的纸介档案之中。”(Knorr 2005)。类似地,在英国,对“国家保健服务”信息系统的大修,耗费了两倍的预算(Ballard 2006)。第2章将讨论为什么项目计划对于生产高质量的软件产品是至关重要的。

多年来,公众在日常生活中不断被灌输:软件不存在问题。但里根总统提出的主动战略防御(Strategic Defense Initiative,SDI)增强了公众对开发无故障软件系统的困难性的认识。报纸和杂志上一些有影响的报道(比如Jacky 1985,Parnas 1985,Rensburger 1985)描述了计算机科学界中的怀疑论的观点。20年后,当美国国会被要求拨款来建立一个类似系统的时侯,许多计算机科学家和软件工程师仍然认为,在编写和测试软件时,没有办法确保它具备充分的可靠性。

例如,许多软件工程师认为反弹道导弹系统至少需要1千万行代码,有些人甚至估计其代码量会高达1亿行。相比较而言,支持美国航天飞机的软件只包含300万行代码,包括控制发射和飞行的地面控制计算机所用的程序。1985年,航天飞机上只有10万行代码(Rensburger 1985)。因此,反导弹软件系统将需要海量的代码测试。再者,可靠性约束是无法测试的。要了解其中的原因,考虑安全攸关(safety-critical)软件的概念。通常我们说某些事情是安全攸关的(即这些事情的失败会对生命或健康构成威胁),则其可靠性至少应当是10−9。正如我们将在第9章中看到的那样,这意味着系统在109小时的运行期间其失效不能超过一次。要观察可靠性的程度,这个系统应运行至少109小时来验证它不会失效,但是109小时是114 000多年——作为一个测试时间段来说,它实在是太长了!

我们在第9章中还将看到,当软件设计有误或编程有误的时候,原本有用的技术可能会变成致命的。例如,当Therac-25(一种射线疗法和X光设备)发生故障并致使几个病人死亡的时候,医学界变得惊恐万状。软件设计人员没有预料到会有不按操作规范使用几个方向键的情况。其结果是,当需要低剂量的射线束时,软件却保持高剂量的设置并发出了极为集中的射线束(Leveson and Turner 1993)。

很容易找到类似的意想不到的使用方式的例子。例如,最近使用一些商业现货构件(作为节省开支的手段,而不是对软件的定制加工)导致以一种原先设计者未曾设想的方式使用这些构件的设计。许多许可协议明确地指出这种意想不到的使用方式所带来的风险:“因为每一个终端用户系统都是定制的,并且与该软件所使用的测试平台不同;还因为用户或应用设计人员将其他产品结合在一起,以厂商或供应商未曾评估或未曾考虑过的方式使用该软件,因此,用户或应用设计人员最终对该软件的验证和确认负责。”(Lookout Direct,未注明出版日期。)

在整个软件设计活动的过程中,必须考虑对系统意想不到的使用方式。至少可以用两种方式来处理这些非正常的使用:一是通过你的想象来考虑系统可能如何被滥用(以及正确使用),二是假定系统将被滥用并设计软件来处理这种滥用。我们将在第8章讨论这些方法。

尽管许多厂商努力设计零缺陷的软件,但事实上,大多数软件产品都不是无故障的。市场压力促使软件开发人员快速交付产品,几乎没有时间进行完全的测试。通常,测试小组只能测试那些最有可能用到的功能,或那些最有可能危及用户或激怒用户的功能。基于这样的原因,许多用户在安装系统的第一个版本时都很谨慎。他们知道,直到第二个版本,这些“bug”才会得到解决。此外,对已知故障进行修复有时是非常困难的,甚至重写整个系统都要比改变现有代码更加容易。我们将在第11章中探讨软件维护过程中所涉及的问题。

尽管在现实生活中,有些软件取得了巨大成功并被全面接受,但是,我们生产的软件的质量仍然有很大的改进余地。例如,缺乏质量的代价是高昂的,一个故障未被检测到的时间越长,改正它的花费就越大。尤其是,在项目最初的分析阶段改正一个错误,比起把系统移交给客户后再去改正,所需成本只有后者的1/10。不幸的是,我们没有在早期捕捉到大多数的错误。改正在测试和维护过程中发现的故障的一半成本,是来自于系统生命周期的更早阶段所犯的错误。在第12章和第13章中,我们将讨论评估开发活动有效性的方法,以及对过程加以改进以尽可能早地捕捉到错误的方法。

我们提出的一种简单而有效的技术是使用评审和审查。许多学生习惯于自己开发和测试软件,但是他们的测试并没有他们想象的那么有效。例如,Fagan研究了故障的检测方法。他发现,采用以测试数据运行程序的方法只能找出开发阶段故障的1/5。然而,同行评审(由同事检查和评论彼此的设计和代码的过程)能够揭示出其余4/5的故障(Fagan 1986)。因此,请你的同事来评审你的工作,软件质量会有大幅度的提高。在后面的章节中,我们将学习如何在每个主要开发步骤之后,利用评审和审查的过程尽可能早地发现并修复故障。并且,我们将在第13章了解到如何改进审查过程。

1.3 什么是好的软件

正如制造商寻找各种方法以确保他们生产的产品的质量一样,软件工程师也必须寻找各种途径来确保他们的产品具有可接受的质量和功效。因此,好的软件工程必须总是使用开发高质量软件的策略。但是在我们设计一个策略之前,必须理解高质量软件的含义是什么。补充材料1-2说明了不同的视角是如何影响“质量”的含义的。在本节中,我们研究好的软件和差的软件之间的区别是什么。

补充材料1-2 关于质量的各种视角

Garvin描述了不同的人是如何认识质量的(Garvin 1984)。他从5种不同的视角对质量加以描述。

先验论的观点:质量是可认知但不可定义的。

用户的观点:质量是恰好达到目的。

制造业的观点:质量是与规格说明的一致。

产品的观点:质量是与产品的内在特征相联系的。

基于价值的观点:质量取决于客户愿意支付的金额。

先验论的观点很像柏拉图对理想的描述或亚里士多德关于形式的概念。换言之,就像每个真实的桌子都是接近理想的桌子一样,我们可以把软件质量认为是我们努力的理想。然而,我们可能永远也不能完全实现它。

先验论的观点是无形的。它与用户更为具体的观点形成对照。当我们测量产品特性(如缺陷密度或可靠性等)的时候,为了理解整个产品质量,我们采用用户的观点。

制造业的观点是在开发过程中以及交付后考虑产品质量。尤其是,它检查产品是否第一次就构建正确,以避免花费大量重复劳动去修复产品交付后的故障。因此,制造业的观点是过程的观点,它提倡遵守良好的过程。然而,几乎没有什么证据能够说明遵守过程就会真正生产出含有较少故障或失效的产品。过程可能确实会导致高质量的产品,但是也可能使生产低质量的产品成为制度化。我们将在第12章分析其中一些问题。

用户和制造业的观点从外部来考虑产品,而产品的观点是从产品内部考察产品并评估产品的内在特性。这通常是软件度量专家所提倡的观点之一,他们假设良好的内部质量指标将会导致良好的外部质量,例如可靠性和可维护性等。然而,仍需要做更多的研究工作来对这些假设加以验证,确定哪些方面的质量会影响产品的实际使用。我们可能必须要开发将产品观点和用户观点联系起来的模型。

客户或市场人员通常采取用户的观点来看待质量。研究人员有时采取产品的观点,而开发团队则会持有制造业的观点。如果未对这些观点之间的差异加以明确,则由此造成的混淆和误解可能会导致错误的决策和劣质的产品。基于价值的观点,可以将这些完全不同的质量描述联系起来。通过将质量与客户愿意支付的价钱等同起来,我们可以在产品价格和质量之间进行权衡,当冲突发生时能够加以控制。类似地,购买方将产品的价格与潜在的收益进行比较,他们按照金钱的价值来考虑质量。

Kitchenham和Pfleeger在一期IEEE Software质量专刊的导言中,研究了这个问题的答案(Kitchenham and Pfleeger 1996)。他们指出,背景有助于答案的确定。字处理软件的容错程度在安全攸关或关键任务的系统中,可能是无法接受的。因此,我们必须至少以3种方式考虑质量:产品的质量、生产该产品的过程的质量以及在将使用产品的商业环境背景下产品的质量。

1.3.1 产品的质量

我们可以要求人们命名影响整体质量的软件特性,但是,从每一个我们询问的人那里得到的答案很可能是不同的。之所以出现这种差异,是因为特性的重要性取决于分析这个软件的人。如果软件用易于学习和易于使用的方式做了用户想要它做的事情,用户就断定软件是高质量的。但是,有时质量和功能是相互关联的。如果一个软件难以学习或难以使用,但是它的功能值得这些付出,那么仍然可以认为它是具有高质量的软件。

我们设法测量软件质量,以便能够将一个产品与其他产品进行比较。要做到这一点,就要找出影响系统整体质量的那些方面。因此,在测量软件质量的时候,用户从故障数目和故障类型等外部特性进行评价。例如,他们可能将失效分为次要的、主要的以及灾难性的,并且希望所发生的任何故障都是次要的。

软件还必须由那些设计和编写代码的人员以及维护该程序的人员来评价。这些实践人员倾向于考虑产品的内部特性,有时甚至会在产品交付给用户之前就考虑这些内部特性。尤其是,实践人员通常会把故障的数目和类型看作为产品质量(或缺乏质量)的证据。例如,开发人员跟踪在需求、设计和代码审查中发现的故障数目,并把它们作为最终产品可能的质量指示器。

由于这样的原因,我们通常要建立模型,把用户的外部视图和软件开发人员的内部视图联系起来。图1-5所示是一个早期质量模型的例子,由McCall和他的同事构建,说明外部的质量因素(在图的左边)与产品质量标准(在图的右边)是如何联系在一起的。McCall为右边的每一个标准都关联一种测度,以说明一个质量要素受关注的程度(McCall,Richards and Walters 1977)。我们将在第12章研究若干产品质量模型。

1.3.2 过程的质量

有很多活动会影响到最终的产品质量。只要有活动出了差错,产品的质量就会受到影响。因此,许多软件工程师认为开发和维护过程的质量与产品的质量是同等重要的。对过程进行建模的一个优点是,我们能够研究它,并寻找方法对它加以改进。例如,我们可以提出下面的问题。

在什么时间、什么地点,我们可能发现某种特定类型的故障?

如何能够在开发过程的更早期阶段发现故障?

如何构建容错机制以便把故障演变为失效的可能性降到最低?

是否有一些其他的做法能够在确保质量的前提下使我们的过程更加高效或有效?

这些问题可以应用于整个开发过程或其中一个子过程(如配置管理、复用或测试等);我们将在后面的章节中研究这些过程。

20世纪90年代,软件工程重点宣扬的是过程建模和过程改进。受到Deming和Juran工作的启发并由IBM等公司实现的能力成熟度模型(CMM)、ISO 9000、软件过程改进及能力确定(SPICE)等过程指导原则提出:通过改进软件开发过程,可以提高最终产品的质量。在第2章中,我们将看到如何识别相关的过程活动,并用模型表示它们对中间产品和最终产品的影响。第12章和第13章将深入分析过程模型并改进框架。

1.3.3 商业环境背景下的质量

当质量评价集中于产品和过程的时候,我们通常使用包含故障、失效和计时的数学表达式来测量质量。人们很少会拓展到商业的视角。在商业环境中,质量是根据软件所处的商业环境提供的产品和服务来看待的。也就是说,我们考虑的是产品的技术价值,而不是更广泛的商业价值,而且我们只根据最终产品的技术质量来做出决策。换句话说,我们假定改进的技术质量会自动转化为商业价值。

一些研究人员仔细研究了商业价值和技术价值之间的关系。例如,Simmons访问了很多澳大利亚企业,以确定他们是如何做出与信息技术相关的商业决策的。她提出了一种理解公司的“商业价值”含义的框架(Simmons 1996)。在Favaro和Pfleeger的报告(Favaro and Pfleeger 1997)中,一家大型美国保险公司Cigna的信息主管Steve Andriole描述了他的公司是如何区分商业价值与技术价值的:

我们通过显而易见的度量来测量(软件的)质量:运行时间与停机时间、维护成本、与修改相关的成本,等等。换句话说,我们在成本参数范围内根据操作性能来管理开发。比起厂商提供的成本-效益性能,我们更关心工作量的结果……商业价值与技术价值相比,前者与我们更贴近……也是我们重点关心的问题。如果我知道公司为追求技术价值将以牺牲商业价值为代价签订合同,我会非常吃惊。如果两者有所不同,我宁可选择后者。如果没有(所期望的)明确的商业价值(可量化地描述:可处理的要求数量等),那么我们不会启动一个系统项目。在“有目的性的”项目需求阶段,我们非常认真,我们会问“我们为什么需要这个系统?”以及“我们为什么关心它?”

人们一直尝试着以量化的、有意义的方式将技术价值和商业价值联系起来。例如,Humphrey、Snyder和Willis指出,根据CMM“成熟”度等级改进其开发过程(将要在第12章中讨论),Hughes飞机制造公司的生产率提高了4倍,并且节省了数百万美元(Humphrey,Snyder and Willis 1991)。类似地,Dion报道,雷神公司的生产率增加了2倍,并且过程改进中每1美元的投资都有7.7美元的回报(Dion 1993)。在俄克拉何马州的Tinker空军基地,工作人员注意到其生产率提高了6.35倍(Lipke and Butler 1992)。

但是,Brodman和Johnson更仔细地研究了过程改进的商业价值(Brodman and Johnson 1995)。他们调查了33家执行了一定过程改进活动的公司,并且研究了几个关键事项,除此以外,Brodman和Johnson还询问了这些公司是如何定义投资回报(Return on Investment,ROI)的——这是一个在商业界明确定义的概念。他们注意到教科书中的投资回报的定义来自金融界,把投资解释为为了其他目标而做的付出。也就是说,“投资不仅仅是收回原始资本,而且收回必须足够多,至少等于这些资金在其他地方所能赚来的利润,再加上风险金”(Putnam and Myers 1992)。通常,商业界使用下面3种模型中的一种来评价ROI:回收模型、会计收益率模型和折现值现金流模型。

但是,Brodman和Johnson发现,美国政府和美国工业界以不同的方式来解释ROI,并且这两种方式与商学院的标准方法也不同(Brodman and Johnson 1995)。政府根据美元来看待ROI,同时考虑减少运作成本、预测节省的美元以及计算采用新技术的成本。政府投资也是用美元来表示,如引入新技术的成本或启动过程改进的成本等。

另一方面,工业界根据项目工作量而不是成本或美元来看待投资。也就是说,公司感兴趣的是节省时间和使用更少的人,而他们对投资回报的定义也体现出他们将重点放在减少工作量上。在对这些公司的调查中,投资回报包含下列各项:

培训;

进度;

风险;

质量;

生产率;

过程;

客户;

成本;

业务。

定义中的成本包含达到预计成本、提高成本效率,以及维持在预算范围之内,而不是减少运转成本或使项目或组织机构简单化。图1-6总结了许多组织使用ROI定义的投资项目的频率。例如,那些被访问的企业中,大约有5%将质量小组的工作量包含在ROI的工作量计算中,大约有35%的企业在考虑投资规模时包含了软件成本。

观点之间的差异带来了很大麻烦,因为它意味着组织之间ROI的计算是不可比较的。但是,也有其合理之处。因为缩短的进度、更高的质量、提高的生产率等因素节省的美元都返回给了政府而不是承包商。另一方面,承包商常常追求竞争优势、提高的劳动能力以及更大的利润,因此,承包商的ROI更多地是基于工作量而不是基于成本的。尤其是,更精确的成本和进度估算意味着客户的满意度和多次的商业机会,并且缩短投入市场的时间以及提高的产品质量也被认为是提供了商业价值。

尽管每个组织都可以对不同ROI计算方法进行调整,但令人担心的是,软件技术的ROI与金融的ROI是不同的。有些时候,程序的成功必须报告给高层管理机构,其中很多与软件无关而与公司的主要业务(如通信或银行业等)相关。用相同的术语表示截然不同的事物,会造成混淆。因此,成功的标准不仅要对软件项目和过程有意义,而且要对支持更一般的业务实践也有意义。第12章将讨论使用几种公共的商业价值测量来选择技术。

1.4 软件工程涉及的人员

软件开发的一个关键部分是客户与开发人员之间的交流,如果交流失败,那么系统也将失败。必须在构建有助于解决问题的系统之前,理解客户想要什么以及他们需要什么。要做到这一点,我们把讨论的重点转向软件开发所涉及的人员。

从事软件开发工作的人员数目取决于项目的规模和难易程度,然而,不论涉及多少人,都可以区分他们在整个项目生命周期中的角色。因此,对于一个大型项目,一个人或一个小组可能被确定地指派为某一个角色;而在小型项目中,一个人或一个小组可以一次承担几个角色。

通常情况下,参与项目的人员分为三类:客户、用户或开发人员。客户(customer)是为将要开发的软件系统支付费用的公司、组织或个人。开发人员(developer)是为客户构建软件系统的公司、组织或个人,其中包括协调并指导程序员和测试人员的管理人员。用户(user)是将实际使用系统的人,包括坐在终端前的人、提交数据的人或阅读输出的人。尽管就某些项目而言,客户、用户、开发人员是同一个人或同一组人,但多数情况下,他们还是各不相同的。图1-7显示了这三类参与人员之间的基本关系。

客户控制着资金,常常进行合同谈判并在验收文件上签字。但是,有时候客户并不是用户。例如,假定Wittenberg Water Works与Gentle Systems,Inc.签订了一份合同,要为公司建立计算机化的会计系统。Wittenberg的总裁可以向Gentle Systems,Inc.的代表明确地描述他需要什么,并且他将要签订这份合同。但是,总裁并不直接使用这个会计系统,用户将是簿记员或会计职员。因此,开发人员确切地理解客户和用户想要什么以及需要什么是非常重要的。

另一方面,假定Wittenberg Water Works是一家大型公司,它有自己的计算机系统开发部门。该部门可能决定它需要一个自动化的工具来记录其项目的成本和进度,并且决定自己构建这个工具。此时,该部门既是用户、客户,同时也是开发人员。

近几年,客户、用户和开发人员之间的这种简单的区别变得越来越复杂。客户和用户以各种方式介入到开发过程中。客户可能决定购买商业现货(Commercial Off-The-Shelf,COTS)软件,并合成到开发人员将要提供和支持的最终产品中。当发生这种情况的时候,客户会介入到系统体系结构的决策中,并且对开发过程有很多约束。类似地,开发人员可能决定使用额外的开发人员,后者称为分包商(subcontractor),分包商构建子系统并把它交付给开发人员,这些子系统包含在最终产品中。分包商可以与主开发人员在同一地点工作,他们也可能在不同的地点与主开发人员协调地工作,并在开发过程后期交付子系统。子系统可能是个交钥匙系统(turnkey system),其代码是一个合并的整体(不需要集成额外的代码),或者它可能需要一个单独的集成过程将主系统和子系统链接起来。

因此,在软件工程中,“系统”的概念不仅对理解问题分析或解决方案的合成是重要的,对组织开发过程以及为参与者分配合适的角色同样重要。在下一节,我们会讨论好的软件工程实践中系统方法的作用。

1.5 系统的方法

我们开发的项目并不存在于真空中。通常,我们装配在一起的硬件和软件,必须与用户、其他软件任务、其他部分的硬件、现有数据库(即仔细定义的数据集合和数据关系)甚至其他的计算机系统进行交互。因此,为任何项目提供一个背景是非常重要的,该背景就是项目的边界(boundary):项目中包含什么,不包含什么。例如,假设主管让你编写一段程序为办公室的人员打印工资单。你必须知道你的程序是否只是简单地从另一个系统中读入工作时间并且打印结果,还是必须同时计算工资信息。类似地,你必须知道程序是否需要计算税率、养老金以及津贴,或者是否要随每份工资单提供这些项目的报告单。实际上,你真正要问的问题是:项目从哪里开始,到哪里结束?同样的问题可以应用于任何系统。一个系统是对象和活动的集合,再加上对象和活动之间关系的描述。就每个活动而言,典型的系统定义包括需要的输入列表、采取的动作以及产生的输出。因此,要开始一个项目,必须知道系统包含哪些对象或活动。

1.5.1 系统的要素

我们通过命名系统的组成部分并标识这些组成部分是如何与另一个系统相互联系的,来描述这个系统。这种标识是分析摆在我们面前的问题的第一步。

1.活动和对象

首先,我们对活动和对象加以区分。活动(activity)是发生在系统中的某些事情,通常描述为由某个触发器引发的事件,活动通过改变某一特性将一个事物转变成另一个事物。这种转变可能意味着数据元素从一个位置移到另一个位置,从某个值转变为另一个值,或者与其他的数据相结合为另一个活动提供输入。例如,一个数据项可以从一个文件移到另外一个文件。这种情况下,改变的特性是位置。或者,数据项的值可能增加。最后,数据项的地址可以与若干其他数据项的地址一起包含在参数列表中,以便可以调用另外的例程一次性处理所有数据。

活动中涉及的要素称为对象(object)或实体(entity)。通常,这些对象以某种方式相互联系。例如,对象能够排列在表格或矩阵中。对象常常组成记录,其中,每一条记录按规定的格式排列。例如,一个雇员的历史记录中可能包含如下对象(也称字段):

名       邮政编码

教名      每小时的工资

姓       每小时的津贴

街道地址    累计休假

城市      累计病假

记录中不仅定义了每个字段,而且定义了每个字段的大小以及字段之间的关系。因此,记录描述规定了每一个字段的数据类型、记录中的开始位置和字段的长度。依次地,因为每个雇员都有一条记录,所有的记录组合在一起就构成了文件,并且要指明文件特性(如最大的记录数等)。

有时,对象定义得稍有不同。不是将每一项考虑为一个大记录中的字段,而是将对象看作是独立存在的。对象的描述包括每个对象的特性列表,以及所有使用对象或影响对象的动作的列表。例如,考虑“多边形”对象。一个对象描述可以是,这个对象具有诸如边数以及每条边的长度等特性。动作可能包括计算面积和周长。甚至可能还可以有一个属性称为“多边形类型”。这样,可以标识每个“多边形”的实例,例如是“菱形”还是“长方形”等。类型本身也可能有对象描述。例如“长方形”可以由“正方形”和“非正方形”组成。当我们在第4章研究需求分析的时候,将会探讨这些概念,并在第6章讨论面向对象开发的时候进行深入探讨。

2.关系和系统边界

一旦定义了实体和活动,就要把实体和它们的活动进行匹配。实体和活动之间的关系应该要清晰、仔细地予以定义。实体的定义包括实体起源于何处的描述。有些项驻留于已经存在的文件中,有些项在活动的过程中被创建。实体的目的地也是非常重要的。有些项仅仅被一个活动所使用,而有些项会被指定为其他系统的输入。也就是说,系统的某些项会被当前系统范围之外的活动所使用。因此,可以认为我们正在考虑的系统是有边界的。有些项跨越边界进入我们的系统,而另一些是我们系统的产品并为其他系统所使用。

使用这些概念,我们能够把系统(system)定义成一组事物的集合:一组实体、一组活动、实体和活动之间关系的描述以及系统边界的定义。系统的这个定义不仅适用于计算机系统,而且适用于其他任何事物(其中,对象以某种方式与其他对象交互)。

3.系统举例

要了解系统定义是如何进行的,考虑一个呼吸系统的例子:身体吸进氧气排出二氧化碳和水。我们可以很容易地定义它的边界:如果指出身体的一个具体器官,就能说出它是不是呼吸系统的一部分。氧气和二氧化碳分子都是实体或对象,它们按照可以明确定义的方式进出呼吸系统。我们也可以根据实体间的交互来描述系统中的活动。如果必要的话,可以通过什么进入以及什么离开来描述这个系统,也可以用一个表格来描述其中涉及的所有实体和活动。图1-8说明了一个呼吸系统。请注意每个活动都涉及实体,并且可以通过描述哪些实体是输入,它们如何被处理,以及输出的结果来进行定义。

我们还必须清晰地描述计算机系统,与预期的用户一起定义系统的边界:我们的工作从什么地方开始以及在什么地方结束?另外,我们必须知道什么处于系统的边界上,从而可以确定输入的开始和输出的目的地。例如,在打印工资单的系统中,支付信息可能来自公司的计算机,系统输出可能是发送到邮箱的工资单的集合,送到适当的接收者手中。在图1-9所示的系统中,我们可以了解边界并且理解实体、活动和它们之间的关系。

1.5.2 相互联系的系统

边界的概念之所以重要,是因为几乎不存在与其他系统无关的系统。例如,呼吸系统必须与消化系统、循环系统、神经系统以及其他系统交互。呼吸系统没有神经系统就不能发挥作用,循环系统没有呼吸系统也不能正常工作。这种相互依赖可能是非常复杂的(实际上,由于我们不能认清生态系统的复杂性,已经引起并加剧了许多环境问题)。但是,一旦描述了系统的边界,就很容易了解什么在系统内部、什么不在以及什么超出了边界。

此外,一个系统存在于另外一个系统的内部也是可能的。描述一个计算机系统的时候,通常是集中于实际系统的一小部分。这种集中使得我们能够定义和构建一个比包裹它的系统简单得多的系统。如果仔细记录那些影响系统的系统之间的交互,即使集中于更大系统中的较小部分,也不会有任何损失。

我们来讨论一个例子,看一看是如何做到这一点的。假定要开发一个水系监控系统,该系统在整条河流经过的很多地点采集数据。在数据采集点完成若干计算,其结果被传送到中心站点进行汇总报告。这样一个系统的实现方式可能是:有一个中心站点的计算机,它与数十个在远程站点的小型计算机进行通信。其中,必须考虑很多系统活动,包括收集水质数据的方式、在远程站点进行的计算、与中心站点的信息通信、通信数据在数据库或共享数据文件中的存储以及根据数据创建报告。可以把这个系统看成是一些系统的集合,其中每个系统都有特定的目的。尤其是,我们可以只考虑较大的系统的通信方面,并且开发一个通信系统将数据从远程站点传送到中心站点。如果我们仔细地定义通信系统和大系统之间的边界,通信系统的设计和开发就可以独立于大系统来完成。

整个水系监控系统的复杂性要比通信系统大得多,因此,通过对分开的、较小的部分进行处理可以简化我们的工作。如果边界定义详细、正确,那么根据较小的部分构建较大的系统是相对容易的。通过以分层的方式来考虑较大的系统,可以按图1-10所示那样描述系统的构造过程(以水系监控系统为例)。一个层次本身就是一个系统,但是,每一层及其包含的那些层次也构成一个系统,图1-10中的圆圈表示它所代表的系统的边界,所有圆圈的集合构成了水系监控系统。

一个系统可能包含另外一个系统,这一点很重要,因为它反映了这样一个事实:一个系统中的对象或活动是外层所代表的每一个系统的一部分。因为每一层都会引入更多的复杂性,所以随着每一层系统的加入,要理解任何一个对象或活动就会更加困难。因此,首先集中于最小的系统是最简单的方法,这样便于更好地理解随后的系统。

我们使用这种思想来构建一个替换旧版本的新系统(无论是手工方式还是自动方式)。我们希望尽可能多地理解新、旧系统是如何运行的。通常情况下,两个系统之间的差别越大,设计和开发就越困难。之所以出现这样的困难,不仅是因为人们倾向于拒绝改变,而且这种差别使得系统难以学习。在构造或合成大系统的时候,把新系统的构造作为一系列递增的中间系统是极其有用的。不是从A系统直接构建B系统,而是从A到A′,再到A′′,然后到B。例如,假定A是一个包含3个主要功能的手工系统,B是A的自动化版本。我们可以将A′系统定义为一个新的系统,它只有功能1是自动化的,而功能2和功能3仍是手工的。然后,A′′有自动化的功能1和功能2,但其功能3仍是手工的。最后,B具有3个自动化的功能。通过将A到B的“距离”分成三段,我们就得到了一系列小的问题,这比整个问题要更容易处理。

在我们的例子中,两个系统非常相似。它们的功能是相同的,但是实现的方式不同。但是,目标系统常常与现有系统存在着巨大差别。尤其是,通常希望目标系统不受现有硬件和软件所强加的约束的限制。增量开发(incremental development)方法可以包含一系列阶段,其中每一个阶段都使前面的系统不受当前系统约束的限制。例如,阶段1可能增加一个新硬件,阶段2可能替换执行一组特定功能的软件。系统逐渐地从旧的软件和硬件中脱离开,直到它体现出新系统的设计。

因此,系统开发可以首先在实际系统中实现一组变化,然后增加一系列变化以生成完整的设计方案,而不是从当前一步一下跳到将来。使用这种方法,我们必须同时从两个不同的方面看待系统:静态地和动态地。静态视图告诉我们系统如今如何运行,而动态视图展示系统如何演变成最终的系统。缺少任何一方面都是不完整的。

1.6 工程的方法

理解了系统的本质之后,就可以开始构造系统了。在这一刻,软件工程中的“工程”部分就是密切相关的,并且使我们到目前为止所做的工作更加完美。回顾一下,本章开始我们谈到软件的编写不仅是一门科学,而且是一门艺术。生产系统的艺术是指软件产品的工艺。编写软件作为艺术的一面是,我们开发技术和工具,这些技术和工具已经被证明有助于生产有用的、高质量的产品。例如,我们可能将一个优化的编译器作为工具,来生成在我们使用的机器上快速运行的程序。或者,我们可能将特定排序和搜索程序作为节省系统时间和空间的技术。这些基于软件的技术只是作为技术,而工具则被用于打造精美的家具或用于盖房子。实际上,流行的编程工具集被称为程序员的工作平台,因为程序员对它们的依赖如同木匠对工作台的依赖。

因为构建系统类似于盖房子,我们可以将盖房子作为例子,来说明为什么“艺术”方法对软件开发是很重要的。

1.6.1 盖房子

假设Chuck和Betsy Howell要雇人为他们造一所房子。由于工程规模大,比较复杂,通常需要由多人组成的建筑队。因此,Howell夫妇雇佣了McMullen建筑公司。建造房子涉及的第一件事是,Howell夫妇与McMullen公司面谈,解释他们想要什么。这次会谈不仅探讨了Howell夫妇想要的房子的外观,也探讨了房子应该具有什么样的特征。然后,公司草拟出房子的建筑平面图和建筑透视图。在Howell夫妇与McMullen公司讨论了细节之后,又进行了一些修改。一旦Howell夫妇认可McMullen公司的方案,房子的建造就可以开始了。

在盖房子的过程中,Howell夫妇很可能亲临建房的现场,考虑他们想要进行的改变。整个盖房子的过程中可能会发生若干变化,但最终房子会建成。在建造的过程中以及Howell夫妇搬进来之前,会对房子的若干部分进行测试。例如,电工测试配线电路,管道工确保管道没有泄漏,而木匠调整木板的变形,使地板更光滑平整。最后,Howell夫妇搬了进来。如果有什么地方不合适,Howell夫妇可以叫McMullen公司来修复,但最终Howell夫妇完全负责这所房子。

我们现在进一步讨论这个过程涉及了哪些内容。首先,由于房子是由很多人同时建造的,所以文档资料是必需的。不但建筑平面图和建筑透视图是必需的,而且还需要写下细节,以便管道工和电工等专业人员可以随着房子建造的过程,把产品装配在一起。

其次,如果Howell夫妇只在这个过程的开始描述他们的房子,然后直到房子建成再来看,这是不合理的。相反,Howell夫妇在建造的过程中可能会多次修改房子的设计。这些修改可能出于以下几种情况。

最初指定的材料现在无法买到。例如,某种类型的瓦可能已经不再生产了。

在Howell夫妇看到房子已经成形的时候,他们可能又有了新的想法。例如,Howell夫妇可能意识到,再花点钱就可以给厨房增加一个天窗。

材料或资金限制可能迫使Howell夫妇改变他们的需求,以满足进度或预算。例如,房子要在冬天来临之前建造完成,而Howell夫妇想要预订的特殊窗子在这个时间内无法准备好,因此,可能用现成的窗子作为替代品。

最初认为是可行的项目或设计,后来可能被证明是不可行的。例如,土壤渗透性测试表明,房子周围的土地不能支撑Howell夫妇最初要求的那么多个浴室。

在房子开始建造之后,McMullen公司也可能建议进行一些变更,其原因也许是由于有更好的想法,也许是由于建筑队的某个关键成员无法工作了。并且,即使房子的某一方面已经建造完成,McMullen公司和Howell夫妇也有可能改变他们关于该处的想法。

最后,McMullen必须提供蓝图、布线和管道图、用具操作手册和其他文档,使Howell夫妇在入住之后能够进行修改或修理。

这个建造过程的总结如下。

确定和分析需求。

提出并文档化房子的总体设计。

提出房子的详细规格说明。

识别并设计房子的组成部分。

构建房子的每一个组成部分。

测试房子的每一个组成部分。

把房子的各个组成部分集成在一起,在住户搬进来之前做最后的修改。

由房子的住户持续进行维护。

我们已看到,参加人员必须保持灵活性,并能够在建造过程的各个点改变初始的规格说明。

房子是在社会、经济以及它所处的政府体系的背景下建造的,记住这一点很重要。就像图1-10所示的水系监控系统描述的各个子系统之间的依赖关系,我们必须把房子看作是一个大方案中的一个子系统。例如,房子的建造是在市或县的建设法规和条例的背景下完成的。McMullen的员工必须经过市或县的许可才能工作,并且必须按照建设标准进行施工。建设场所要经过建筑监理的检查,他们确保相关标准得到遵守。建筑监理设定质量的标准,检查作为建筑项目质量保证的检查点。社会或习惯的限制也可能成为常识性的或为人们所接受的行为。例如,房子的前门直接对着厨房或卧室是不符合习惯的。

同时,我们必须认识到,不可能一开始就准确地规定建造房子的活动,必须根据经验为决策留有余地,以处理未预料到的或非标准的情形。例如,许多房子都是根据预先制好的部件建造的,门已经装在框架中,浴室使用预制好的淋浴房等。但是,有时需要对标准化的房子建造过程进行修改,使之适应于特殊的特征或请求。假设框架已经搭好,水泥墙已经砌好,地板已经铺好,下一步就是在浴室的地面上铺设瓷砖。建筑人员沮丧地发现,墙和地面并不正好是正方形。这个问题并不是由于过程不当造成的,建造房子的部件有一些自然的或制造上的变形,因此会出现一些不太精确的情况。如果按照标准方式用小正方形铺设地砖的话,这些不精确的地方就会更加突出。这正是艺术和专业技术发挥作用的地方。建筑人员可能从垫层上取下瓷砖,然后每次铺一块,并对每一块做一些小的调整,使得除了辨别能力极强的人,一般人察觉不到这种变形。

因此,盖房子是一项复杂的任务,在建造的过程中,过程、产品或资源很多时候会发生变化,但都可通过艺术和专业知识进行适当的调剂。虽然盖房子过程可以标准化,但是,专家的判断力和创造性总是需要的。

1.6.2 构建系统

软件项目的进展方式与盖房子过程类似。在上面的例子中,Howell夫妇是客户和用户,McMullen是开发人员。假定Howell夫妇让McMullen建造的房子是要让Howell先生的父母住,那么,用户和客户就是不同的人了。同样地,软件开发涉及用户、客户和开发人员。如果让我们为某个客户开发一个软件系统,第一步是与客户会面以确定需求。正如我们前面所看到的那样,这些需求是对系统的描述。如果不了解边界、实体和活动,要描述软件及其与它的环境的交互是不可能的。

一旦定义了需求,我们就可以创建系统设计来满足指定的需求。正如在第5章中将要看到的,系统设计告诉客户,从客户的角度看,系统会是什么样的。就像Howell夫妇查看建筑平面图和建筑透视图一样,我们向客户展示将要使用的视频显示屏幕的图片、将要生成的报表以及任何其他解释用户将如何与完成的系统交互的相关描述。如果系统有手工备份或覆写过程,也要对其进行描述。起先,Howell夫妇只对房子的外观和功能感兴趣。到后来,他们必须决定是用铜管还是用塑料管。同样,软件项目的系统设计(也称为体系结构)阶段只描述外观和功能。

接着,客户要对设计进行评审。当设计得到批准后,整个系统设计将被用来生成其中单个程序的设计。请注意,直到这个步骤才会提到程序。在功能和外观确定之前,考虑编码是没有意义的。在建造房子的例子中,这一阶段准备讨论的是管道的类型或电线的质量。我们之所以能够决定是用塑料管还是用铜管,是因为我们现在知道了在建筑物中哪里会有水流过。同样,当系统设计被大家认可之后,就准备开始讨论程序了。讨论的基础是关于系统的软件项目的定义明确的描述,系统设计包含功能以及所涉及的相互关系在内的完整描述。

当程序编写完之后,必须在把它们链接到一起之前将其作为单独的代码段进行测试。这是测试的第一个阶段,称为模块测试或单元测试。一旦确信每段程序都能够按我们的期望来运行,就把它们组合到一起,并确保它们能够正确运行。这是第二个测试阶段,通常称为集成测试,因为它是逐步加入一部分来构建系统的,直到整个系统可以运转。最后的测试阶段称为系统测试,是对整个系统的测试,用于确保起初指定的功能和交互得以正确实现。在这个阶段,会把系统和需求进行比较。开发人员、客户和用户共同检查系统是否达到了希望的目标。

最后,交付最终产品。随着它的使用,差异和问题就会暴露出来。如果我们的系统是一个交钥匙系统,那么在移交之后客户将对该系统负责。可是,很多系统不是交钥匙系统,如果出现任何问题,或者需求发生变化,开发人员或其他组织要对其进行维护。

因此,软件的开发包含下面的活动。

需求分析和定义。

系统设计。

程序设计。

编写程序(程序实现)。

单元测试。

集成测试。

系统测试。

系统交付。

维护。

理想情况下,一次执行一个活动,当到达上述活动的结尾时,就完成了软件项目。但是,实际上,很多步骤是重复执行的。例如,在评审系统设计时,你和客户可能发现有些需求还没有进行文档化。你可能与客户一起工作,加入需求,有时可能要重新设计系统。类似地,当编写和测试代码时,你可能发现某个设备并没按照其文档描述的那样运行。你可能不得不重新设计代码,重新考虑系统设计,甚至回过头与客户讨论如何满足需求。由于这样的原因,我们将软件开发过程(software development process)定义为包含前面列出的9个活动(部分或全部)的软件开发的描述,对这些活动加以组织,以便生产经过测试的代码。第2章将探讨若干不同的软件开发过程。后面的章节中将研究从需求分析到维护的每个子过程和它们的活动。但是在研究它们之前,先来考虑一下谁开发软件以及软件开发面临的挑战在这些年发生了哪些变化。

1.7 开发团队的成员

在本章的前面,我们看到客户、用户和开发人员在新产品的定义和创建中发挥着重要的作用。开发人员是软件工程师,但是,每一位工程师可能都只擅长于软件开发的某一特定方面。因此,我们在此更深入、详细地讨论开发团队成员的角色。

任何开发过程的第一步都是找出客户想要什么,并且将需求文档化。正如我们已经看到的,分析就是把事物分解成其组成部分的过程,以便我们能够更好地理解它们。因此,开发团队包含一个或多个需求分析员跟客户一起工作,并且把客户想要的分解为离散的需求。

一旦了解了需求并且把需求文档化,分析员就与设计人员一起工作,生成系统层描述(系统要做什么);然后,设计人员与程序员一起工作,以程序员能够编写实现指定需求的代码行的方式来描述系统。

生成代码之后,必须对它进行测试。通常,第一次测试由程序员自己完成;有时,也使用另外的测试人员帮助程序员发现他们忽略的错误。当代码单元被集成为一个个运行的功能组时,测试人员小组与实现小组会在各部分组合构建系统的过程中,验证系统是否能够正确运行,是否符合规格说明。

当开发团队对系统的功能和质量感到满意时,注意力就转向了客户。测试小组和客户一起验证整个系统是否是客户想要的系统。他们通过把系统如何工作与最初需求规格说明进行比较,来完成这项工作;然后,培训人员向用户说明如何使用这个系统。

就很多软件系统而言,客户的验收并不意味着开发工作的结束。如果在系统验收之后发现了故障,维护小组就要修复它们。另外,随着时间的推移,客户的需求可能会发生变化,系统也必须进行相应的改变。因此,维护可能涉及分析人员,由分析人员决定增加或变更哪些需求;还可能涉及设计人员,由设计人员确定改变系统哪个部分的设计;还可能涉及实现这些变化的程序员,确保改动后的系统仍然正确运行的测试人员,以及向用户解释这些变化如何影响系统使用的培训人员。图1-11说明了开发团队的角色与开发步骤的对应关系。

就课程项目而言,学生通常会自己工作或者在一个开发团队的小组中工作。教师所要求的文档是最少的,学生常常不需要编写用户手册或培训文档。再者,布置的作业是相对稳定的,需求在项目的生命周期中不会发生变化。最后,学生构建的系统很可能在课程结束后就丢弃掉,他们的目的是证明他们的能力,而不必为实际的客户解决问题。因此,对于课程项目而言,程序规模、系统复杂性、文档化的需要以及可维护性的需要都是相对很少的。

但是,对一个实际的客户而言,系统的规模可能会很庞大,复杂性可能会很高,可能需要大量的文档,可维护性的要求也可能会很高。对于一个涉及成千上万行代码并涉及开发团队成员间很多交互的项目来说,项目各方面的控制可能是很困难的。为了支持开发团队中的每一个成员,一些人员可能在开发的最开始就要介入系统,并且自始至终都是如此。

资料管理员负责准备和存储在系统生命周期中用到的文档,包括需求规格说明、设计描述、程序文档、培训手册、测试数据、进度等。与资料管理员一起工作的是配置管理小组的成员。配置管理涉及维护需求、设计、实现和测试之间的对应关系。根据这种交叉引用,开发人员可以得知,如果需要改变需求,相应地要改变什么程序;或者如果提议进行某种改变,会影响到程序的哪一部分。配置管理成员还要协调可能建立或支持的系统的不同版本。例如,一个软件系统可能要运行在不同的平台上,或者按一系列不同的发布进行交付。配置管理要确保各个平台上系统的功能都是一致的,而且新发布的功能不会降级。

开发角色可由一个人或几个人承担。就小型项目而言,两三个人就可以承担所有角色。然而,对大型项目,通常要根据开发中的职责,把开发团队分成不同的小组。有时,维护系统的人员与最初设计和编写系统的人员是不同的。对某些规模巨大的开发项目来讲,客户甚至会雇佣一家公司做最初的开发,而用另外一家公司进行维护工作。我们在后面的章节中讨论开发和维护活动时,会讨论每种类型的开发角色需要哪些技能。

1.8 软件工程发生了多大的变化

我们已经对构建软件和建造房子进行了比较。每年全国会建造几百栋房子,觉得满意的客户会搬进去。每年开发人员构造数百个软件产品,但是,更常见的是客户对结果不满意。为什么会存在这种差别呢?如果可以如此容易地列举出系统的开发步骤,那么为什么软件工程师生产高质量的软件却是这样艰难呢?

让我们回过头来再考虑建造房子的例子。在建造房子的过程中,Howell夫妇不断地检查计划。他们还有很多时候会改变自己的想法。同样地,软件开发允许客户在每一个步骤评审计划并且对设计进行改变。毕竟,如果开发人员生产出一个非凡的产品却不能满足客户的要求,那么最终的系统将只会浪费所有人的时间和精力。

由于这样的原因,以灵活的方式运用软件工程工具和技术是至关重要的。过去,作为开发人员,我们假定客户从一开始就知道他们想要什么,但这种稳定性通常不符合实际情况。随着一个项目展开,开始没有预料到的约束就会出现。例如,在选定一个项目要使用的硬件和软件之后,我们可能发现由于客户需求的变化,使得很难使用特定的数据库管理系统生成承诺给客户的菜单;或者发现与系统交互的另外一个系统已经改变了它的过程或数据的格式;我们甚至可能发现硬件或软件并没像厂商的文档所承诺的那样工作。因此,我们必须记住每一个项目都是与众不同的,必须使所选择的工具和技术能够反映对这个项目的约束。

我们还必须承认,大多数系统并不是单独存在的。它们与其他的系统交互,或接受信息或提供信息。开发这样的系统会很复杂,原因很简单,相互通信的系统之间需要大量的协调行为。对于那些并行开发的系统来讲,这种复杂性更是如此。在过去,开发人员很难确保系统间接口的文档的精确性和完整性。在后面的章节中,我们将讨论控制接口问题的相关事项。

1.8.1 变化的本质

有很多问题会影响到软件开发项目的成功,上述那些问题也在其中。无论采用什么样的方法,我们都必须既要展望未来,也要回顾过去。也就是说,我们必须回顾以前的开发项目,看一看关于软件质量保证的有效性以及关于技术和工具的有效性,我们到底学到了什么。另外,还要预测在将来很有可能改变我们实践的软件开发的方式以及软件产品的使用方式。Wasserman指出,自从20世纪70年代以来,软件开发一直发生着巨大的变化(Wasserman 1995)。例如,早期的应用软件是运行在单处理器上的,通常是大型机。输入是线性的,往往是一副卡片或一个输入磁带,而输出是字母数字。系统用两种基本方式来设计:转换(transformation),它将输入转换为输出;事务(transaction),由输入决定哪个功能将被执行。如今,基于软件的系统已经大不相同,并且更为复杂。它们通常运行在多个系统上,有时配置在具有分布式功能的客户/服务器体系结构中。软件不仅执行用户需要的主要功能,而且还要执行网络控制、安全性、用户界面表示和处理,以及数据或对象管理。传统的“瀑布”开发方法假定开发活动是线性前进的,即只有在一个活动完成以后才会进行下一个活动(将在第2章中学习)。这种方法不再灵活也不再适合于当今的系统了。

在Stevens的演讲中,Wasserman通过已经改变软件工程实践的7个关键因素,总结了这些变化(Wasserman 1996),如图1-12中所示。

(1)商用产品投入市场时间的紧迫性。

(2)计算技术在经济上的转变:更低的硬件成本,更高的开发、维护成本。

(3)功能强大的桌面计算的可用性。

(4)广泛的局域网和广域网。

(5)面向对象技术的采用及其有效性。

(6)使用窗口、图标、菜单和指示器的图形用户界面。

(7)软件开发瀑布模型的不可预测性。

例如,市场压力意味着企业必须抢在竞争对手之前准备好新的产品和服务,否则,企业本身的生存将会受到威胁。因此,如果传统的评审和测试技术需要大量的时间投入,但是却没有减少相应的故障或失效率,那么这些技术就不能再使用。类似地,以前花在提高速度或减少空间方面的代码优化的时间不再是明智的投资,因为增加磁盘和内存条可能是更便宜的解决方案。

另外,桌面计算把开发的权利交到用户的手中,用户现在使用他们的系统开发电子表格和数据库应用、小程序甚至是专门的用户界面模拟程序。这种开发责任的转移意味着软件工程师更有可能构建比以前更为复杂的系统。类似地,大多数用户和开发人员可以利用巨大的网络资源,使得用户更容易在没有特殊应用的情况下找到信息。例如,现在在万维网上搜索是快速、容易和有效的。用户不再需要为找到自己所需的内容而编写数据库应用。

开发人员现在发现他们的工作价值也增加了。面向对象的技术、网络和复用库,使开发人员可以直接、快速地将大量可复用模块用于新的应用中。并且,常常用专门的工具开发图形用户界面,有助于使复杂的应用友好地面对用户。由于在分析问题方面我们已经变得精于此道,因此,现在能够对一个系统进行划分,以便并行地开发其子系统,这就需要一个与“瀑布”模型有很大不同的开发过程。将在第2章中看到,我们有很多选择,包括使我们建立原型的过程(用于同客户和用户一起验证需求是否正确,并评价设计的可行性)和活动间正确迭代的过程。这些步骤有助于确保在将需求和设计变成代码之前,使它们尽可能没有故障。

1.8.2 软件工程的Wasserman规范

Wasserman指出,7个技术变化中的任何一个都对软件开发过程有着重大的影响(Wasserman 1996)。它们合在一起,改变了我们的工作方式。在DeMarco的介绍中,描述了这种根本的转变:我们首先解决了容易的问题——这意味着尚未解决的一组问题比以前更加困难了。Wasserman通过提出软件工程中存在的8个基本概念来应对这一挑战。这些概念构成了有效的软件工程规范的基础。在这里给出它们的简要介绍,在后面的章节中,将回过头来探讨它们在什么地方适用于我们所做的事情,以及如何应用于我们所做的事情。

1.抽象

有时,在一个问题的“自然状态”(即如同客户和用户表达的那样)考虑这个问题是一件令人畏惧的事情。在问题的“自然状态”下,我们不可能发现以有效的或者甚至只是可行的方法处理问题的显而易见的方式。抽象(abstraction)是在某种概括层次上对问题的描述,使得我们能够集中于问题的关键方面而不会陷入细节。这个概念与转换(transformation)不同,转换是把问题转移到另外一个我们理解得更好的环境中。转换通常用于将一个问题从现实世界转移到数学世界中,这样我们能够利用数字的知识来解决问题。

通常,我们使用抽象标识对象的类,以便能够把多个项组合在一起。这样,我们处理的事情可以更少,而且可以集中考虑每个类中各个项之间的共性。我们可以讨论一个类中各个项的性质或属性,检查属性以及类之间的关系。例如,假定我们要为一条大的、复杂河流构建一个环境监测系统。监控设备可能包括监测空气质量、水质、温度、流速以及其他环境特性的传感器。但是,为了达到目的,我们可能决定定义一个称为“传感器”的类。类中的每个项具有固定的属性,不论它监测哪个特性:高度、重量、电力需求、维护进度等。我们在了解问题环境的过程中,或在设计解决方案的过程中,处理的是类,而不是它的元素。因此,类的使用有助于简化问题陈述并使我们集中于问题的本质要素或特性。

抽象也可以按层次的方式进行组织。例如,传感器是一种类型的电子设备,而我们可能有两种类型的传感器:水传感器和空气传感器。

因此,可以构成图1-13所示的简单层次结构。通过隐藏其中一些细节,我们可以集中精力考虑必须处理的对象的本质特性,并且得到简单、优雅的解决方案。我们将在第5章、第6章和第7章中更详细地讨论抽象和信息隐藏。

2.分析和设计方法以及表示法

当设计一个作为课程作业的程序时,通常需要自己完成工作。产生的文档是一个正式描述,它告诉你自己为什么选择这个特定的方法、变量名的含义是什么以及实现的算法。但是,当与团队一起工作的时候,必须与开发过程中的其他参与者进行交流。大多数工程师,无论他们是做什么样的工程,都会使用标准的表示法来帮助他们进行交流以及文档化相关决策。例如,建筑师画了一张图或蓝图,任何其他的工程师都能够理解他画的图。更为重要的是,公共的表示法使得建筑承包商能够理解建筑师的意图和想法。正如将在第4章、第5章、第6章和第7章中看到的,软件工程中没有类似的标准,由此产生的误解是当今软件工程中的一个关键问题。

分析和设计方法不止是提供了交流媒介,还使我们能够建立模型并检查模型的完整性和一致性。再者,我们可以更容易地从以前的项目中复用需求和设计组件,从而相对容易地提高生产率和质量。

但是,在我们能够决定一组标准的方法和工具之前,仍然有许多悬而未决的问题需要解决。正如我们将在后面的章节中看到的那样,不同的工具和技术处理的是问题的不同方面,我们需要标识建模原语,以便用一种技术就能获取问题的所有重要的方面。或者我们需要开发一种供所有方法使用的表示技术,当然可能需要某种形式的剪裁。

3.用户界面原型化

原型化(prototyping)意味着构建一个系统的小版本,通常只有有限的功能,它可用于:

帮助用户或客户标识系统的关键需求;

证明设计或方法的可行性。

通常,原型化过程是迭代的:首先构建原型,然后对原型进行评估(利用用户和客户的反馈),考虑如何改进产品或设计,之后再构建另外一个原型。当我们和客户认为手头问题的解决方案令人满意时,迭代过程就终止了。

原型化通常用来设计一个良好的用户界面(user interface),即系统与用户交互的部分。但是,在其他场合也可以使用原型,甚至是在嵌入式系统(embedded system)(即其中的软件功能不是明确地对用户可见的系统)中。原型能够向用户展示系统将会有什么样的功能,而不管它们是用硬件还是用软件实现的。因为从某种意义上讲,用户界面是应用领域和软件开发团队之间的桥梁,所以,原型化可以把使用其他需求分析方法不能明确的问题和假设表面化。我们将在第4章和第5章讨论用户界面原型化的作用。

4.软件体系结构

系统的整个体系结构不仅对实现和测试的方便性很重要,而且对维护和修改系统的速度和有效性也很重要。体系结构的质量可能成就一个系统,也可能损害一个系统。事实上,Shaw和Garlan将体系结构独自作为规范,它影响整个开发过程(Shaw and Garlan 1996)。一个系统的体系结构应该体现我们将在第5章和第7章学习的良好设计的原则。

系统的体系结构根据一组体系结构单元以及单元之间的相互关系来描述系统。单元越独立,体系结构越模块化,就越容易分别设计和开发不同的部分。Wasserman指出,至少有5种方法可以将系统划分为单元(Wasserman 1996)。

(1)模块化分解:基于指派到模块的功能。

(2)面向数据的分解:基于外部数据结构。

(3)面向事件的分解:基于系统必须处理的事件。

(4)由外到内的设计:基于系统的用户输入。

(5)面向对象的设计:基于标识的对象的类以及它们之间的相互关系。

这些方法并不是相互排斥的。例如,可以用面向事件的分解设计用户界面,同时,使用面向对象或面向数据的方法来设计数据库。我们将在后面的章节中进一步详细分析这些技术。这些方法之所以重要,是因为它们体现了我们的设计经验,并通过复用已经做过的和所学到的,充分利用过去的项目。

5.软件过程

自从20世纪80年代后期以来,很多软件工程师已经在密切留意开发软件的过程以及由此产生的产品。活动中的组织和规范对软件的质量和软件开发的速度的积极作用已经得到承认。然而,Wasserman指出:

不同应用类型和组织文化之间的巨大差异使得不可能对过程本身进行预先规定。因此,软件过程不可能以抽象和模块化的方式作为软件工程的基础。(Wasserman 1996)

相反,他提出,不同的软件类型需要不同的过程。尤其是,Wasserman指出企业范围的应用程序需要大量的控制,而单个的或部门级的应用程序可以利用快速应用程序开发,如图1-14所示。

利用目前的工具,很多中小规模的系统可以由一两个开发人员来完成,其中每个开发人员必须担任多个角色。这样的工具可能包含文本编辑器、编程环境、测试支持工具,还可能包含一个获取关于产品和过程的关键数据元素的小型数据库。因为项目的风险相对较低,所以需要很少的管理支持或评审。

但是,大型、复杂的系统需要更多的结构、检查和平衡。这些系统通常涉及很多客户和用户,并且开发会持续很长时间。再者,因为某些关键子系统可能由他人提供或用硬件实现,开发人员并不总是能够控制整个过程。这种类型的高风险系统需要分析和设计工具、项目管理、配置管理、更复杂的测试工具以及对系统更严格的评审和因果分析。第2章将详细讨论若干可选的过程,以了解不同的过程是如何处理不同的目标的。然后,在第12章和第13章中,我们评估一些过程的有效性,并探讨对它们进行改进的方法。

6.复用

在软件开发和维护中,通常通过复用以前开发项目中的项来利用应用程序之间的共性。例如,在不同的开发项目中,我们使用同样的操作系统和数据库管理系统,而不是每次都构建一个新的。类似地,当我们构建一个与以前做过的项目类似但有所不同的系统时,可以复用需求集、部分设计以及测试脚本或数据。Barnes和Bollinger指出,复用并不是一个新的思想,他们还给出了很多有趣的例子,说明复用的不仅仅是代码(Barnes and Bollinger 1991)。

Prieto-Diaz介绍了这样一种理念:可复用构件是一种商业资产(Prieto-Diaz 1991)。公司和组织机构对那些可复用的项进行投资,而当这些项再次用于后面的项目中的时候,就可以获得巨大的收益。但是,制定一个长期、有效的可复用计划可能很困难,因为存在如下这些障碍。

有时候,构建一个小的构件比在可复用构件库中搜索这样一个构件要更快。

要使一个构件足够通用、可以在将来被其他开发人员很容易地复用,则可能需要花费格外多的时间。

由于难以对做过的质量保证和测试的程度进行文档化,可能会导致一个潜在的复用人员认为构件的质量是令人满意的。

如果某个复用的构件失效或需要进行更新,不清楚谁应该对此负责。

理解和复用一个由他人编写的构件,其代价可能是高昂的,也可能很耗时。

在通用性和专业性之间通常存在冲突。

我们将在第12章中更详细地讨论复用,并研究几个成功复用的例子。

7.测度

改进是软件工程研究的驱动力:通过改进过程、资源和方法,我们可以生产和维护更好的产品。但是,我们有时只能概况地表示改进目标,原因是没有量化地描述我们做了什么以及我们的目标是什么。正因为如此,软件测度已经成为好的软件工程实践的一个关键方面。通过量化我们做了什么以及我们的目标是什么,就可以用通用数学语言来描述我们的行动和结果,从而能够评估我们的进展。另外,量化的方法允许我们比较不同项目的进展。例如,当John Young担任惠普公司的CEO时,他设置了“10X”的目标,即无论对于何种应用的类型和领域,对于惠普的每一个项目,在质量和生产率方面都要有10倍的提高(Grady and Caswell 1987)。

在较低的抽象层次上,测度有助于使我们的过程和产品的特定特性更加可见。将我们对现实的、经验世界的理解转换为形式化的、数学世界中的要素和相互关系,通常是有益的,这样,我们可以操纵它们,从而得到进一步的理解。正如图1-15所示的那样,可以使用数学和统计的方法来解决问题、寻找趋势或刻画一种情形(例如使用平均值和标准差)。而这个新的信息可以接着被映射回现实世界,作为我们试图解决的现实问题的解决方案的一部分。在本书中,我们将看到测度如何应用于支持分析和决策的例子。

8.工具和集成环境

多年以来,厂商一直推荐使用CASE(计算机辅助软件工程)工具,其中的标准化的集成开发环境将增强软件开发。但是,我们已经看到,不同的开发人员是如何使用不同的过程、方法和资源的。因此,一个统一的方法说起来容易,做起来就难了。

另一方面,研究人员已经提出了几个框架,使我们能对已有的环境和打算构建的环境进行比较和对照。这些框架还允许我们检验每个软件工程环境提供的服务,决定哪一个环境最适合于给定的问题或应用程序的开发。

对工具进行比较主要的难点之一是厂商很少针对整个开发生命周期。相反,他们集中于小的活动集,例如设计或测试等,并且由用户把选择的工具集成到一个完整的开发环境中。Wasserman指出了在任何工具集成中必须处理的下列5个问题(Wasserman 1990)。

(1)平台集成:工具在异构型网络中的互操作能力。

(2)表示集成:用户界面的共性。

(3)过程集成:工具和开发过程之间的链接。

(4)数据集成:工具共享数据的方式。

(5)控制集成:一个工具通知和启动另一个工具中的动作的能力。

在本书后面的每一章中,我们将研究支持本章描述的活动和概念的工具。

你可以将这里描述的8个概念想象成把本书编织起来的8条线,它们把分离的活动连接起来,称为软件工程。随着我们对软件工程了解得越来越多,我们将重新讨论这些概念,以了解它们是如何把软件工程统一和提升为一个学科的。

1.9 信息系统的例子

本书每一章的结尾都有两个例子,一个是信息系统,另外一个是实时系统。我们把本章中描述的概念应用到每一个例子的相关部分,这样你就能够了解概念在实践中的含义,而不仅仅是理论上的含义。

信息系统的例子是从James和Suzanne Robertson写的Complete Systems Analysis:The Workbook,the Textbook,the Answers(Robertson and Robertson 1994)中抽取的(已获许可)。它是一个销售皮卡地里电视台广告时间的系统开发实例。皮卡地里电视台拥有英国本土一定区域的特许经营权。图1-16显示出皮卡地里电视台的覆盖地区。正如我们看到的那样,电视时段价格方面的约束很多,因此,这个问题很吸引人,难度也很大。本书强调了问题的诸多方面及其解决方案。Robertson的书介绍了获取和分析系统需求的详细方法。

在英国,广播委员会授予商业电视公司为期8年的特许经营权,给予它在国内严格规定区域播放节目的独家权限。作为回报,被授权者必须播放预先规定的短剧、喜剧、体育、儿童以及其他节目。而且对于什么节目在何时可以播放、节目内容和商业广告内容也有相应的规定。

广告商要向中部地区的观众播放广告,可以有若干选择:皮卡地里、有线频道和卫星频道。皮卡地里吸引了大部分观众。因此,皮卡地里必须设定价格以吸引广告商的国内预算的那一部分。吸引广告商注意力的方法之一是靠收视率,收视率反映了一天中不同时段观众的数量和类型。收视率根据节目类型、观众类型、一天的时段、电视公司等进行报告。但是,广告收费不仅仅取决于收视率。例如,如果广告商购买了很多小时的广告时间,那么每小时的价格可能会便宜一些。而且,特定时间和特定节目中广告的类型也是有限制的。例如:

酒类广告要在晚上9点以后才可以播放;

如果某个演员出现在电视剧中,那么有这个演员的广告不能在该电视剧播出后的45分钟内出现;

如果某类产品的广告(如汽车)是为特定的商业插播安排的,那么这一类的任何其他广告都不能在该插播中播出。

随着我们更详细地探讨这个例子,我们将注意到有关广告及其费用的其他规则和条例。从图1-17所示的系统环境图中,我们可以得知,系统的边界以及它如何与这些规则相联系。阴影的椭圆是我们的信息系统例子皮卡地里系统,系统边界是椭圆的圆周。箭头和长方形表示可能影响皮卡地里系统运转的项,但是,我们仅仅把它们作为一组输入和输出,它们分别具有自己的源地和目的地。

在后面的章节中,我们将使阴影椭圆中(即系统边界内部的)的活动和元素可见,用每章描述的软件工程技术检查这个系统的设计和开发。

1.10 实时系统的例子

下面举个实时系统的例子,我们来看阿丽亚娜5型火箭中的嵌入式软件。阿丽亚娜5型火箭属于欧洲航天局(ESA)。1996年6月4日,在它的首次航行中,发射大约飞行了40秒钟后开始偏离航向,在地面控制系统的引导下,火箭通过远程控制被销毁。销毁这个未保过险的火箭不仅损失了火箭本身,而且也损失了它装载的4颗卫星。这次灾难共造成5亿美元的损失(Newsbytes home page 1996;Lions et al.1996)。

从火箭导航系统到它各个组成部分的运行,几乎所有方面都与软件有关。火箭发射的失败以及随后的销毁提出了很多与软件质量有关的问题。在后面的章节中会看到,调查委员会在调查原因时,把重点放在了软件质量及软件质量保证上。在本章,我们根据火箭的商业价值来探讨质量问题。

许多组织机构在阿丽亚娜5型火箭中有投资,包括ESA、法国中央国家航天局(CNES,负责阿丽亚娜计划的全面指挥),以及其他12个欧洲国家。不断的延期和一连串问题损害了阿丽亚娜火箭计划,这些问题还包括在1995年测试引擎的过程中,由于氮气泄漏导致两位工程师死亡的事故。但是,1996年6月的事故是首次直接由软件失效导致的。

这次事故的商业影响远远超过了5亿美元的设备损失。1996年,阿丽亚娜4型及其以前型号获得了全球一半以上的发射合同,领先于美国、俄罗斯以及中国的运载火箭。因此,这次事故不仅使计划的可信度大受损害,也使阿丽亚娜火箭的商业前景充满危机。

新的生意部分基于新火箭能够把更重的负荷运载到轨道。阿丽亚娜5的设计目标是能运载一颗重达6.8吨的卫星或两颗合在一起重达5.9吨的卫星,进一步的目标是希望在2002年之前达到更大的运载能力。这些增加的运载能力具有明显的商业优势。通常,操作人员通过多颗卫星共用一个火箭来减少发射费用,因此,阿丽亚娜能够同时运载多家公司的卫星。

在这个例子的背景下,我们考虑一下质量的含义是什么。阿丽亚娜5型火箭的销毁证实,这个灾难性后果的原因是客户错误地说明了需求。既然这样,开发人员可能声称系统仍然是高质量的,只不过是根据错误的需求规格说明构造了系统。的确,调查委员会在总结事故原因和补救措施时指出:

委员会的调查结果是基于阿丽亚娜5项目组完全的公开介绍和文档,这些文档从总体上表明,从工程技术以及文档的完整性和文档可跟踪性来说,阿丽亚娜5的程序是高质量的(Lions et al.1996)。

但是从用户和客户的观点来看,规格说明的过程已经足够好,能够确定规格说明中的缺陷,并且在灾难发生前,使客户能够改正规格说明中的缺陷。调查委员会认识到:

SRI(最终找到的引起问题的子系统)的厂商仅仅是遵循了给定的规格说明,即在检测到任何异常的情况下,处理器将终止执行。异常的发生并不是因为偶尔的失效,而是由于一个设计错误。异常被检测到了,但是并没有被适当处理。原因是采用了这样一种观点,即在软件发生故障之前,就应该认为它是正确的。委员会有理由相信阿丽亚娜5软件设计的其他部分也采纳了这个观点,而委员会支持相反的观点:应该假定软件是有故障的,直到应用了目前可接受的最好的实践方法并能够证明它是正确的(Lions et al.1996)。

在后面的章节中,我们将更详细地研究这个例子,探讨开发人员和客户决策中的设计、测试和维护的含义。我们将看到,在开发初期,低质量的系统工程是如何导致一系列决策失误,进而又是如何导致一系列灾难发生的。另一方面,包括ESA和调查委员会等所有相关机构的开诚布公,以及高质量的文档和尽快得知真相的最真诚的愿望,促成了眼前问题的快速解决和防止未来类似问题发生的有效计划。

系统的观点使调查委员会和开发人员可以把阿丽亚娜5看作子系统的集合。这个集合反映了对问题的分析,正如我们本章论述的那样,不同的开发人员能够开发具有明显不同功能的独立子系统。例如:

发射器的飞行姿态和它在空间中的运动由惯性参照系统(SRI)测量。它具有自己内部的计算机,根据捷联式惯性平台的信息,通过激光陀螺和加速器计算角度和速度。来自SRI的数据通过数据总线传输到机载计算机(On-Board Computer,OBC),该计算机通过伺服阀门和液压执行器执行飞行程序、控制固体推进器的喷射器和Vulcain低温引擎(Lion.et.al.1996)。

但是综合的解决方案必须包括一个具有所有构件的总体视图,它把各个部件合在一起考虑,以决定各部件之间的“联系”是否紧密、合适。在阿丽亚娜5的例子中,调查委员会提出客户和开发人员应该一起工作以找出关键软件,并确保它不仅能够处理预测到的行为,而且能够处理未预测到的行为。

这意味着,关键软件(从某种意义上来说,该软件的失效会使任务处于危险的境地)必须在一个非常详细的层面标识出来,异常行为必须细化,并且一个合理的备份策略必须将软件失效考虑进去(Lion.et.al.1996)。

1.11 本章对单个开发人员的意义

本章介绍了许多概念,它们对于优秀的软件工程研究和实践来说都很重要。单个的软件开发人员可以通过下面的方法使用这些概念。

当有一个问题需要解决时(无论解决方案是否涉及软件),可以通过把问题分解成不同的组成部分和各部分之间的关系来分析问题。然后,解决单个子问题并把它们合并成为统一的整体,从而产生一个解决方案。

必须理解需求可能发生变化,即使在分析问题、形成解决方案时需求也会变化。因此,解决方案必须是良好文档化的并且具有灵活性的,还应该把假设和使用的算法文档化(以便在以后处理变化时使用)。

必须从几个不同的角度来观察质量,理解技术质量和商业质量可能有很大差异。

可以使用抽象和测度帮助标识哪些是问题和解决方案的本质。

必须牢记系统的边界,这样做出的解决方案才不会与相关系统发生重叠(相关系统是指与正在构建的系统相互交互的系统)。

1.12 本章对开发团队的意义

大部分开发工作都是由大型开发团队的成员来完成的。正如本章所述,开发包括需求分析、设计、实现、测试、配置管理、质量保证以及其他活动。开发团队中的有些成员可能会承担多个角色。项目的成功在很大程度上取决于团队成员之间的交流与协调。在本章我们已经看到,通过以下选择,可以帮助项目获得成功。

一个适合团队规模、风险级别以及应用领域的开发过程。

集成很好的工具,它们提供项目所要求的交流方式。

测度和支持工具,它们提供尽可能多的可见性和易理解性。

1.13 本章对研究人员的意义

本章所讨论的许多问题是值得进一步研究的好课题。我们已经指出了软件工程中一些悬而未决的问题,包括需要找到以下几项。

适当的抽象级别:使得问题易于解决。

适当的测度:使得问题和解决方案的本质可见且有用。

适当的问题分解方法:使得每一个子问题都是可解决的。

通用框架和表示法:允许方便有效的工具集成,使项目参加者之间的交流最有效。

在后面的章节中我们会描述许多技术,其中一些一直在使用,并且是被软件开发实践充分证明的。而其他一些技术是提议性质的,只在一些小的、“玩具式的”或学生项目中进行过演示。我们希望告诉读者如何改进你的工作,并且激发你在将来尝试新的技术和进程方面的创造力和想像力。

1.14 学期项目

如果不与你的同事一起参与软件开发项目,是不可能学会软件工程的。因此,本书的每一章都将介绍你可以与同学一起进行的学期项目的相关信息。学期项目是一个基于真实组织机构的真实系统,是对分析、设计、实现、测试和维护的真正挑战。另外,因为你将会与一个团队一起工作,你会面对团队的多样性和项目管理的问题。

这个学期项目是当你想买一套房子的时候,你可能与银行磋商贷款的方式。银行有很多途径获得收入,通常是以较低利率从储户那里借钱,然后以较高的利率用贷款的方式把这些钱借出。长期的房产贷款(如抵押贷款)的期限一般有15年、25年甚至30年。也就是说,有15年、25年或30年的时间去偿还贷款,包括本金(你最初借的钱)和利息。尽管从这些贷款利息获得的收入是丰厚的,但由于贷款占用时间很长,会妨碍银行进行其他投资。因此,银行常常把它们的贷款出售给财团机构,这虽然减少了长期利润,但可以用其他方式使用资本。

学期项目应用称为Loan Arranger。我们假设金融财团组织(FCO)处理从银行购买的贷款,然后再把它卖给投资者,从中获得利润。银行把贷款卖给FCO,并获得本金作为回报。然后,FCO把贷款转让给那些愿意比银行等更长时间获得回报的投资者。

要了解这一交易如何运作,考虑你获得房贷(称为“抵押贷款”)的情形。你可以首付5万元(称为头期款)、贷款10万元购买一套15万元的房子。从第一国家银行贷款的“期限”可能是30年,利息是5%。这意味着第一国家银行给你30年的时间来偿还你借的钱(“本金”)加上也不用立即偿还的利息。例如,你可以通过每月付款一次的方式偿还10万元(也就是说,360个“分期付款”或“月供”),并包括未付余额的利息。如果最初的差额是10万元,银行根据本金的数额、利率、你偿还贷款的时间和假定你每月等额偿还等因素来计算每月的付款额。

例如,假设银行告诉你每月付款额是536.82元。第一个月的利息是(1/12)×(0.05)×(100 000元)= 416.67元。其余的付款额使本金减少120.15元(536.82元 −416.67元)。第二个月,你现在欠银行是100 000元减去120.15元,因此,利息减少为(1/12)×(0.05)×(100 000元 −120.15元),即416.17元。因此,在第二个月有416.17元的付款是利息,而其余的付款额120.65元支付的是本金。随着时间的推移,你偿还的利息越来越少,并且剩下的本金越来越少,直到你付完所有的本金,完全拥有自己的房子,并收回银行对财产的抵押权为止。

在偿还贷款期间,第一国家银行可能把你的贷款卖给FCO一段时间。第一国家银行与FCO商谈一个价格。依次地,FCO可能把你的贷款卖给ABC投资公司。你仍然必须每月偿付抵押贷款,但是你的款付给了ABC,而不是第一国家银行。通常,FCO用“组合”的方式销售贷款,而不是单个贷款,因此,投资者根据风险、包含的本金和期望的回报率购买贷款。换言之,像ABC这样的投资者可以联系FCO,并指定希望投资多少钱、多长时间、希望承担多少风险(基于贷款人或组织机构偿还贷款的历史)以及预期的利润。

Loan Arranger允许FCO的分析人员选择符合投资者期望的投资特性的一组贷款。该应用访问FCO从各种贷款机构中购买的贷款信息。当一个投资者指定投资标准后,系统选择最优的、满足标准的贷款组合。而系统支持更高级的优化策略,例如,从那些可用的子集中选择最优的贷款组合(例如是从马萨诸塞州所有的贷款而不是从所有可用的贷款中选择),系统也允许分析人员为客户从贷款组合中手工选择贷款。另外,除了贷款组合的选择,系统还能将信息管理活动自动化。诸如,当银行每月提供有关信息时,自动地更新银行信息、贷款信息以及增加新的贷款记录。

现在总结一下上面的信息。Loan Arranger系统允许贷款分析人员访问FCO从多个贷款机构购买的抵押贷款的相关信息(家庭贷款在这里也被简单地描述成“贷款”)。FCO的目的是将贷款重新打包后卖给其他投资者。FCO把为投资的目的购买以及转卖的贷款统称为贷款投资组合。Loan Arranger系统在贷款信息库中跟踪这些贷款投资组合。贷款分析人员可以增加、浏览、更改或删除贷款投资组合中关于借款方和贷款集合的相关信息。另外,系统允许贷款分析人员建立贷款组合以销售给投资者。Loan Arranger的用户是贷款分析人员,他们跟踪FCO购买的抵押贷款。

在后面的章节中,我们将更深入地探讨系统的需求。目前,如果你想理解本金和利息的相关信息,可以复习以前的数学书,或查看http://www.interest.com/hugh/calc/formula.html。

1.15主要参考文献

在风险论坛(Risks Forum)中,可以找到软件失效和软件故障的相关信息,该论坛由Peter Neumann主持。风险论坛中的有些文章发行在每一期的Software Engineering Notes中,该刊由计算机学会软件工程专题小组(SIGSOFT)出版。可以在ftp.sri.com的Risks目录上访问到风险论坛归档的文章。风险论坛新闻组可以从comp.risks在线阅读,或者通过自动列表服务器risks-request@CSL.sri.com订阅。

关于阿丽亚娜5型火箭项目的更多信息可以从欧洲航天局的网站获得:http://www.esrin.esa.it/htdocs/esa/ariane。描述此项任务失败的ESA/CNES的联合新闻稿(英文)在http://www.esrin.esa.it/htdocs/tidc/Press/Press96/pressl9.html。法文版的新闻稿在http://www.cnes.fr/Acces_Espace/Vol_50x.html。阿丽亚娜5型火箭飞行501失败报告的电子版在http://www.esrin.esa.it/htdocs/tidc/Press/Press96/ariane5rep.html。

Leveson和Turner非常详细地描述了Therac软件设计和测试问题(Leveson and Turner 1993)。

IEEE Software 1996年1月刊是软件质量专刊,尤其是Kitchenham和Pfleeger撰写了介绍性文章(Kitchenham and Pfleeger 1996),描述并评论了几个质量框架。Dromey的文章论讨了如何用可测量的方式定义质量(Dromey 1996)。

关于皮卡地里电视例子的更多信息,可以参考(Robertson and Robertson 1994)或访问www.systemsguild.com网站了解Robertson的软件需求方法。

1.16 练习

1.下面是《华盛顿邮报》曾经发表过的一篇文章(美联社1996)。

导航计算机错误导致飞机失事

美国航空公司称单字母编码是喷气式飞机在哥伦比亚坠毁的原因

达拉斯8月23日电——航空公司今天声称,去年12月在哥伦比亚失事的美国航空公司喷气式飞机的机长输入了一条错误的单字母计算机指令,正是这条指令使飞机撞到了山上。

这次失事致使机上163人中除4人生还外,其余全部丧生。

美国航空公司调查人员总结说,显然这架波音757飞机的机长认为他已经输入了目的地Cali的坐标。但是,在大多数南美洲的航空图上,Cali的单字母编码与波哥大(Bogota)的编码相同,而波哥大位于相反方向的132英里处。

据美国航空公司的首席飞行员和飞行副总裁Cecil Ewell的一封信中说,波哥大的坐标引导飞机撞到了山上。Ewell说,在大多数计算机数据库中,波哥大和Cali的编码是不同的。

美国航空公司的发言人John Hotard确认,Ewell的信首先是在《达拉斯早间新闻》中报道,本周交到了所有航空飞行员的手中以警告他们这种编码问题。

美国航空公司的发现也促使联邦航空局向所有的航空公司发布公告,警告他们有些计算机数据库与航空图存在不一致。

计算机错误还不是引起这次失事原因的最终结论。哥伦比亚政府正在调查并希望在10月份以前公布调查结果。

国家运输安全委员会的发言人Pat Cariseo说,哥伦比亚调查人员也在检查飞行员训练和航空交通管制的因素。

Ewell谈到当他们把喷气式飞机的导航计算机与失事计算机的信息相比较时,美国航空公司的调查人员发现了计算机错误。

数据表明,错误持续了66秒钟未被检测到,而同时机组人员匆忙遵照航空交通管制的指令采取更直接的途径到达Cali机场。

3分钟以后,当飞机仍在下降而机组人员设法解决飞机为什么已经转向时,飞机坠毁了。

Ewell说这次失事告诉了飞行员两个重要的教训。

“首先,不管你去过南美或任何其他地方多少次,比如落基山区,你绝对不能假设任何情况。”他对报社记者说。其次,他说,飞行员必须明白他们不能让自动驾驶设备承担飞行的责任。

这篇文章就是软件危机存在的证据吗?软件工程如何使航空飞行变得更安全?在软件开发过程中应该强调什么事项才会使我们在未来防止类似问题的发生?

2.给出一个问题分析的例子,其中问题部分相对简单,但是解决问题的困难在于子问题之间的相互联系。

3.解释错误、故障和失效之间的区别。举出一个关于错误的例子,并且这个错误导致了需求、设计、代码的故障。举出在需求中存在故障的例子,并且这个故障导致了失效。举出在设计中存在故障的例子,并且这个故障导致了失效。举出在测试数据中存在故障的例子,并且这个故障导致了失效。

4.为什么故障数目的计算可能误导产品质量的测量?

5.很多开发人员认为技术质量就等同于产品质量。举出一个具有很高技术质量的产品的例子,而这个产品的客户并不认为该系统是高质量的。是否有道德的因素限制了对质量的认识,使人们仅仅考虑技术质量?使用Therac-25的例子说明你的观点。

6.很多组织机构购买商品化的软件,认为那样比内部开发和维护的软件便宜。论述使用COTS软件的利与弊。例如,如果COTS产品的厂商不再支持该产品,将会发生什么样的情况?当在一个大系统中使用COTS软件设计一个产品的时候,客户、用户和开发人员必须预见到哪些问题?

7.使用COTS软件的法律和道德含义是什么?分包合同的使用呢?例如,当COTS软件中一个故障导致主系统失效时,谁来负责改正问题?当这样的失效直接(就像汽车中刹车失灵)或间接地(就像错误信息被提供给其他系统时,如练习1中看到的那样)导致对用户的伤害时,谁应该为此负责?在COTS被集成到一个大系统之前,需要什么样的检查和权衡以保证COTS软件的质量?

8.图1-17所示的皮卡地里系统的例子包含了大量的规则和约束。对其中3个规则和约束进行讨论,并解释把它们置于系统边界之外的利与弊。

9.阿丽亚娜5型火箭坠毁成为法国和其他国家的头条新闻。法国《解放报》在头版把它称为“一场370亿法郎的焰火表演”。事实上,这次爆炸是几乎所有欧洲报纸的头条新闻,也是大多数欧洲电视网晚间新闻的头条。与此形成对照的是,由于纽约的互联网提供商Panix被黑客入侵,迫使Panix系统关闭了几个小时。而这次事件的新闻仅仅出现在《华盛顿邮报》商业版的头版。当报道基于软件的突发事件时,新闻的责任是什么?如何评价和报道软件失效的潜在影响?

第2章 过程和生命周期的建模

本章讨论以下内容:

过程的含义;

软件开发的产品、过程和资源;

软件开发过程的若干模型;

过程建模的工具和技术。

我们在第1章中看到,软件工程既是一个创造的过程,又是一个逐步进行的过程,它涉及很多人员,这些人员生产不同类型的产品。在这一章,我们会详细分析这些步骤,讨论各种活动的组织方式,以便我们能够协调所做的各种活动以及决定什么时候进行这些活动。本章首先定义什么是过程,以便我们能够理解对软件开发建模时必须包含哪些内容。接着讨论几种软件过程模型。在理解了我们想要使用的模型类型后,再详细地讨论静态建模和动态建模这两种技术。最后,把其中一些技术应用到信息系统和实时系统的例子中。

2.1 过程的含义

当提供一项服务或创建一种产品的时候,无论是开发软件、撰写报告还是进行商务旅行,人们总是遵循一系列的步骤来完成任务。通常,每次都使用同样的顺序来完成任务。例如,在房子的配线安装之前通常不能搭建水泥墙,或者把所有的成分混合之后才能烤制蛋糕。可以将一组有序的任务称为过程(process):它涉及活动、约束和资源使用的一系列步骤,用于产生某种想要的输出。

正如第1章中定义的那样,一个过程常常涉及一系列的工具和技术。任何过程都应具有如下特性。

过程规定了所有主要的过程活动。

过程遵从一组约束(例如项目进度)使用资源,并产生中间结果和最终产品。

过程可以由用某种方式链接起来的子过程构成。过程可以组织成层次结构,以便使每一个子过程具有自己的过程模型。

每一个过程活动具有进入和退出标准,这样可以知道活动什么时候开始以及什么时候结束。

活动可以按一定的顺序加以组织,这样就可以清楚地知道应当在什么时候执行一个活动(相对于其他活动)。

每一个过程都有其指导原则,用于解释每一个活动的目标。

约束或控制可以应用于活动、资源或产品。例如,预算或进度可以制约完成活动所需的时间,工具可以限制资源使用的方式。

我们有时把涉及产品构建的这种过程称为生命周期(life cycle)。因为软件开发过程描述了软件产品从概念到实现、交付、使用和维护的整个过程,因此,有时把软件开发过程称为软件生命周期(software life cycle)。

过程之所以重要,是因为它强制活动具有一致性和一定的结构。当我们知道如何把事情做好而且希望其他人以同样的方式做事时,这些特性就特别有用。例如,如果Sam是一个好的瓦工,他可以写下他砌砖过程的说明,这样Sara也就能够学会如何把砖砌得同样好。他可能考虑人们做事方式之间的差别,例如,他可以这样书写相关说明,使得Sara不论用左手还是右手都能够学会砌砖。同样,软件开发过程也能够用灵活的方式加以描述,让人们能够使用自己喜好的技术和工具来设计和构造软件。一个过程模型可能要求在编码之前进行设计,但是它允许采用许多不同的设计技术。因此,过程有助于保持大量不同人员开发的产品和服务之间的一致性和质量。

过程不仅仅是步骤。从第1章可以看到,步骤就像菜谱,它是把工具和技术组合起来生产产品的一种结构化方式。过程是步骤的集合,它将步骤组织起来使人们能够生产满足一系列目标和标准的产品。实际上,过程可能建议我们从若干步骤中进行选择,只要它能满足人们的目标。例如,过程可能要求在编码开始之前检查设计构件,或者可以通过非正式的评审或正式的审查来完成检查。审查和检查有其各自的步骤,但针对的是相同的目标。

过程结构允许我们分析、理解、控制和改进组成过程的活动,并以此来指导我们的行动。要了解它如何进行,考虑一下制作巧克力蛋糕的过程。这个过程可能包含若干步骤,例如购买配料和找到合适的烹饪用具。食谱描述了配料及烤制的过程,其中也包含活动(例如,在与其他的配料混合之前要先打鸡蛋)、约束(例如温度要求:在把巧克力与糖调配之前,需要先把巧克力加热到让其融化的温度)和资源(如糖、面粉、鸡蛋和巧克力)。假设Chuck依据这个食谱烤制巧克力蛋糕。当蛋糕做好后,他尝了一下,觉得蛋糕太甜了。他看了一下食谱,了解到蛋糕甜的原因是糖配得太多。然后,他烤制了另一份蛋糕,这次他减少了糖的用量。烤好后他又尝了一下,觉得现在巧克力味道不足。于是,他在再次修订的配料中增加了一定量的可可粉,然后又烤制了一次。经过几次反复之后,其中每一次都改变某些成分或活动(如把烤蛋糕的时间延长一些,或在巧克力与鸡蛋混合之前,先将巧克力冷却下来),Chuck终于烤出了令自己满意的蛋糕。如果没有记录这部分过程的食谱,Chuck就不可能这么容易地做出改变以及对结果进行评估。

就获取经验并把经验传授给他人而言,过程也是很重要的。就像主厨把他拿手的食谱传授给他的同事和朋友一样,优秀的工匠也能够把记载的过程传授下去。实际上,学员和教师的概念就是基于这样一种思想:共享经验,从而将高级人员的技能传授给初学者。

同样,我们希望从过去的开发项目中获取经验,记录能够产生高质量软件的最佳实践,遵循这样的软件开发过程,在为客户构建产品的时候,就能够理解、控制和改进开发。我们在第1章看到,软件开发通常包含下面几个阶段。

需求分析和定义。

系统设计。

程序设计。

编写程序(程序实现)。

单元测试。

集成测试。

系统测试。

系统交付。

维护。

每一阶段本身就可以描述为一组活动的过程(或一组过程)。并且每一个活动都包含约束、输出和资源。例如,需求分析和定义阶段将用户用某种方式表述的功能和特征作为初始输入,这一阶段的最终输出是一组需求。但是,在用户和开发人员之间进行交流之后,有了变更和其他选择时,就有可能会出现中间产品。这个阶段中还包括一些约束,例如,产生需求文档的预算和进度,包含的需求种类的标准以及用于表达需求的表示法。

本书讨论所有这些阶段。对每一个阶段,我们会仔细研究涉及的过程、资源、活动和输出,我们将了解它们如何影响最终产品(即有用的软件)的质量。处理开发过程中的每一个开发阶段的方式有很多种,活动、资源和输出的每一种配置都构成一个过程,并且过程描述了在每一阶段都发生了什么。例如,设计可能包含一个原型化过程,其中,考虑了很多设计决策,以便开发人员能够选择合适的方法和进行过程复用(该过程的复用使前面产生的设计构件包含在了当前的设计中)。

每一个过程都可以用不同的方式加以描述,文本、图形或两者结合起来使用。软件工程研究人员提出了各种描述格式,通常组织为包含关键过程特征的模型。本章的剩余部分将研究不同的软件开发过程模型,以便读者能够理解组织起来的过程活动是如何使开发更加有效的。

2.2 软件过程模型

软件工程文献描述了很多软件过程模型。有些模型是规定性的(prescription),说明软件开发应该进行的方式;而另一些是描述性的(description),说明软件开发实际进行的方式。从理论上讲,两种类型的模型应该是相似或相同的,但事实并非如此。建立过程模型并讨论它的子过程有助于开发团队理解开发过程的理想情况与实际情况之间的差距。

对过程进行建模的原因还有很多。

当一个小组记录下开发过程的描述时,就会形成对软件开发中的活动、资源和约束的共同理解。

建立过程模型有助于开发团队发现过程及其组成部分中存在的不一致、冗余和遗漏的地方。当注意并改正了这些问题以后,过程会变得更加有效并且集中在构建最终产品上。

模型应该反映开发的目标,例如构建高质量的软件、在开发的早期发现故障以及满足必需的项目预算和开发进度的约束。由于建立了模型,开发团队可以根据目标评估候选活动是否合适。例如,开发团队可能引入需求评审,这样可以在设计开始之前发现和修复需求中的问题。

应当根据具体情况对每一个过程进行裁剪。建立过程模型有助于开发团队理解应该在哪里对过程进行裁剪。

每一个软件开发过程模型都将系统需求作为输入,将要交付的产品作为输出。多年来,人们提出了很多模型。这里探讨几种最流行的模型,以理解它们之间的共性和区别。

2.2.1 瀑布模型

研究人员提出的第一个模型是瀑布模型(waterfall model),如图2-1所示,它将开发阶段描述为从一个阶段瀑布般地转换到另外一个阶段(Royce 1970)。如该图所提示的,一个开发阶段必须在另一个开发阶段开始之前完成。因此,当从客户引发的所有需求都已经过完整性和一致性分析,并形成需求文档之后,开发团队才能够开始进行系统设计活动。瀑布模型从一种非常高层的角度描述了开发过程中进行的活动,并且提出了要求开发人员经过的事件序列。

瀑布模型一直被用来规范软件开发活动。例如,美国国防部标准2167-A规定,瀑布模型是多年来国防部合同中软件开发交付的依据。每一个过程活动都有与其相关联的里程碑和可交付产品,以便于项目经理能够用模型判断在某一时刻项目离最后完成还有多远。例如,在瀑布模型中,“单元测试和集成测试”阶段结束的里程碑是“编写完经过测试和集成的代码模块”,其中间可交付产品是测试过的代码的副本。接着,代码被移交给系统测试人员,这样它可以与其他系统构件(硬件或软件)合并,并作为一个整体进行测试。

在帮助开发人员布置他们需要做的工作时,瀑布模型是非常有用的。它的简单性使得开发人员很容易向不熟悉软件开发的客户做出解释。它明确地说明,为了开始下一阶段的开发,哪些中间产品是必需的。很多其他更复杂的模型实际上是在瀑布模型的基础上的润色,如加入反馈循环以及额外的活动。

瀑布模型的很多问题已经在计算机文献中讨论过,补充材料2-1总结了其中的两个问题。瀑布模型最大的问题是它并不能反映实际的代码开发方式。除了一些理解非常充分的问题之外,实际上软件是通过大量的迭代进行开发的。通常情况下,软件用于解决以前从未解决过的问题,或者其解决方案需要更新以反映业务情况或操作环境的变化。例如,一个飞机制造商需要一种关于新型机体的软件,它要比当前的型号更大、更快。对于这种情况,即使软件开发人员在开发航空软件方面积累了大量的经验,他们仍然面临很多新的挑战。用户和开发人员都不完全了解影响期望结果的所有关键因素,并且在需求分析过程中花费的大量时间都用在了理解受系统及其软件影响的项和过程上,以及系统及其运行环境之间的关系上,就像在第4章将要看到的那样。因此,如果不对实际的软件开发过程加以控制,开发过程可能看起来会像图2-2那样:当开发人员试图搜集关于问题以及提议的解决方案如何解决该问题的有关资料时,他们会翻来覆去地从一个活动转向另一个活动。

补充材料2-1 瀑布模型的缺点

自从瀑布模型提出以来,招致了众多的批评。例如,McCracken和Jackson指出,瀑布模型在系统开发之上强加了一种项目管理结构(McCracken and Jackson 1981)。“主张任何一种生命周期方案(即使它具有各种变种)能够适用于所有的系统开发显然是违背现实的,或者由于假定一个过于简陋的生命周期而显得毫无意义。”

注意,瀑布模型说明了每一个主要开发阶段是如何终止于某些制品(例如需求、设计或代码)的,但并没有揭示每一个活动如何把一种制品转化为另外一种制品,例如,从需求转化为设计。因此,对于如何处理在开发过程中可能出现的产品和活动的变化,模型并没有向管理者和开发人员提供相关指导。例如,当在编码活动的过程中发生需求变化时,随之带来的设计和编码的变化并没有在瀑布模型中加以强调。

Curtis、Krasner、Shen和Iscoe指出,瀑布模型的主要缺点是没有把软件看作一个问题求解的过程(Curtis,Krasner,Shen and Iscoe 1987)。瀑布模型产生于硬件领域,它是从制造业的角度看待软件开发的。但是,制造业是重复生产某一特定的产品,而软件并不是这样开发的,相反,随着人们对问题的逐步理解和对候选方案的评估,软件在不断演化。因此,软件是一个创造的过程,而不是一个制造的过程。瀑布模型并没有说明我们创建最终产品过程中所需的往返活动的任何特有信息。尤其是创造通常包含不同的尝试、开发和评估原型、评价需求的可行性、比较若干种设计以及从失败的教训中学习,从而最终决定问题令人满意的解决方案。

通过引入加强理解的活动和子过程,软件开发过程有助于控制活动之间的往返。原型化就是这样的一个子过程。原型(prototype)是一个部分开发的产品,它使客户和开发人员能够对计划开发的系统的相关方面进行检查,以决定它对最终产品是否合适或恰当。例如,开发人员可以构建一个系统来实现一小部分关键需求,以确保需求是一致、可行和符合实际的。否则,在需求阶段就要进行修正,而不是在测试阶段(测试阶段代价会更高)进行修正。同样,设计的某些部分也可以进行原型化,如图2-3所示。设计的原型化有助于开发人员评价可选的设计策略以及决定对于特定的项目,哪一种策略是最好的。正如我们将在第5章中看到的,设计人员可能用几种完全不同的设计来处理需求,看一看哪一种具有最好的特性。例如,可以在一个原型中把网络设计为环形的,而另一个设计为星形的。然后评价其性能特性,看一看哪一种结构能更好地满足性能目标或约束。

通常,开发人员会构建用户界面,并把它作为原型进行测试,以便用户能够了解新系统将会是什么样子的,并且设计人员也能够更好地理解用户希望如何与系统进行交互。因此,在系统测试进行正式确认之前,主要的需求应该都已经过处理和确定。确认(validation)确保系统实现了所有的需求。每一个系统功能可以回溯到系统规格说明中的一个特定需求。系统测试也对需求进行验证,验证(verification)确保每一项功能都是正确的。也就是说,确认保证开发人员构造的是正确的产品(根据规格说明),而验证检查实现的质量。原型化对于验证和确认都很有用。但是,我们将在后面的章节中看到,这些活动也可以出现在开发过程的其他部分中。

2.2.2 V模型

V模型是瀑布模型的变种,它说明测试活动是如何与分析和设计相联系的(German Ministry of Defense 1992)。如图2-4所示,编码处于V形符号的顶点,分析和设计在左边,测试和维护在右边。正如我们将在后面的章节中看到的那样,单元测试和集成测试针对的是程序的正确性。V模型提出,单元和集成测试也可以用于验证程序设计。也就是说,在单元和集成测试的过程中,编码人员和测试小组成员应当确保程序设计的所有方面都已经在代码中正确实现。同样,系统测试应当验证系统设计,保证系统设计的所有方面都得到了正确实现。验收测试是由客户而不是开发人员进行的,它通过把测试步骤与需求规格说明中的每一个要素关联起来对需求进行确认。这种测试检查在接受系统和付款之前,所有需求是否都已经完全实现。

该模型中连接V形符号左边和右边的连线意味着,如果在验证和确认期间发现了问题,那么在再次执行右边的测试步骤之前,重新执行左边的步骤以修正和改进需求、设计和编码。换言之,V模型使得隐藏在瀑布模型中的迭代和重做活动更加明确。瀑布模型关注的通常是文档和制品,而V模型关注的则是活动和正确性。

2.2.3 原型化模型

我们已经看到如何使用原型化活动修正瀑布模型以改进对系统的理解。但是原型化不仅仅是附属于瀑布模型的,如图2-5所示,它本身也是一种有效的过程模型的基础。由于原型化模型允许开发人员快速构造整个系统或系统的一部分以理解或澄清问题,因此,它与工程化原型具有同样的目的,其中需要对需求或设计进行反复调查,以确保开发人员、用户和客户对需要什么和提交什么有一个共同的理解。依据原型化的目标,可以取消原型化需求、设计或系统中的一个或多个循环。但是,总体目标保持不变,即减少开发中的风险和不确定性。

例如,系统开发可能以客户和用户提出的一组需求为起点,然后,让相关各方一起探讨各种方案,查看可能的屏幕显示、表格、报表以及用户和客户直接使用的其他系统输出。当用户和客户对需要什么做出决定时,对需求进行修正。一旦对需求应该是什么达成了共识,开发人员就可以进行设计了。再次通过同样的过程,开发人员与客户和用户协商来探讨不同的设计。

对初始设计不断修正,直到开发人员、用户和客户对结果满意为止。实际上,考虑不同的设计方案有时会暴露需求中的问题,此时开发人员就需要退回到需求阶段,重新考虑和变更需求规格说明。最后,对系统进行编码并讨论不同的编码方案,还可能对需求分析和设计进行迭代。

2.2.4 可操作规格说明

对许多系统来说,需求的不确定性导致了后期开发的变化和问题。Zave提出了一种过程模型,它允许开发人员和客户在开发的早期检查需求及其隐含含义,在这个过程中,他们可以讨论和解决某些不确定性(Zave 1984)。在可操作规格说明模型(operational specification model)中,通过演示系统行为的方式来评估或执行系统需求。也就是说,一旦指定了需求,就可以用软件包进行演示。这样,在设计开始之前就可以评价它们的隐含含义。例如,如果规格说明要求计划构建的系统能够处理24个用户,规格说明的可执行形式就能够帮助分析人员确定用户数目是否给系统增加了太多的性能负担。

这种模型的过程与诸如瀑布模型这样的传统模型有很大的不同。瀑布模型把系统的功能与设计分离(即把系统要做什么与系统如何做分离开),目的是把客户的需要与实现分开,而可操作规格说明模型允许把功能和设计合并起来。图2-6说明了可操作说明模型是如何运作的。注意,可操作规格说明模型与原型化模型类似,该过程允许用户和开发人员在早期检查需求。

2.2.5 可转换模型

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

转换的样例有:

改变数据表示;

选择算法;

优化;

编译。

由于从规格说明到可交付系统可以采取很多途径,它们所表示的变换序列和决策都保存为形式化的开发记录。

转换方法具有很好的前景。然而,如图2-7所示,应用转换方法的主要障碍在于需要一个精确表述的形式化的规格说明,这样才可以基于它进行操作。随着形式化规格说明方法的普及,转换模型将会被更广泛地接受。

2.2.6 阶段化开发:增量和迭代

在早期的软件开发中,客户愿意为软件系统的最后完成等待很长时间。有时,从编写需求文档到系统交付使用会经过若干年,称为循环周期(cycle time)。但是,今天的商业环境不会再容许长时间的拖延。软件使产品在市场上引人注目,而客户总是期待着更好的质量和新的功能。例如,1996年,惠普公司80%的收入来自过去两年开发的产品,因而,他们开发了新的过程模型来帮助缩短循环周期。

一种缩短循环周期的方法是使用阶段化开发,如图2-8所示。使用这种方法设计系统时使其能够一部分一部分地交付,从而在系统其余部分正在开发的同时,用户已经获得了一部分功能。因此,通常会有两个系统在并行运行:产品系统和开发系统。运行系统(operational system)或产品系统(production system)是当前正在被客户和用户使用的系统,而开发系统(development system)是准备用来替换现行产品系统的下一个版本。通常,用它们的发布代号表示一个系统:开发人员构建发布1,进行测试,然后把它交给用户作为第一个可运行的发布。然后,当用户使用发布1的时候,开发人员正在构建发布2。从而,开发人员总是在开发发布n+1,而与此同时发布n总是正在运行的。

开发人员可以用多种方法决定如何将开发组织为发布。增量开发(incremental development)和迭代开发(iterative development)是两种最常用的方法。在增量开发中,需求文档中指定的系统按功能划分为子系统。定义发布时首先定义一个小的功能子系统,然后在每一个新的发布中增加新功能。图2-9的上半部分显示了增量开发是如何在每一个新的发布中逐步增加功能直到构造全部功能的。

而迭代开发是在一开始就提交一个完整的系统,然后在每一个新的发布中改变每个子系统的功能。图2-9的下半部分说明一个迭代开发的3个发布。

为了理解增量开发和迭代开发之间的区别,我们来看一个用于文字处理的软件包。假设这个软件包要具有3种类型的功能:创建文本、组织文本(即剪切和粘贴)以及格式化文本(例如使用不同的字体大小和类型等)。要使用增量开发模型构建这样一个系统,我们可能在发布1中仅提供创建功能,然后在发布2中提供创建和组织功能,最后在发布3中提供创建、组织和格式化功能。但是,使用迭代开发方法时,我们要在发布1中提供简单的3种类型的功能。例如,可以创建文本,然后剪切并粘贴文本,但是剪切和粘贴功能可能不够灵活快捷。在下一次迭代(即发布2)中,提供相同的功能,但是系统的功能增强了:剪切和粘贴功能变得方便和快捷。每一个发布都在前一个发布的基础上进行了某些改进。

实际上,许多组织都将迭代开发和增量开发方法结合起来使用。一个新的发布版本可能包含新的功能,并且对已有功能做了改进。这种形式的阶段化开发方法是人们想要的,原因如下。

(1)即使还缺少某些功能,但在早期的发布中就可开始进行培训。培训过程可以使开发人员观察某些功能是如何执行的,并为后面的发布提供了改进的建议。这样,开发人员能够很好地对用户的反馈做出反应。

(2)可以及早为那些以前从未提供的功能开拓市场。

(3)当运行系统出现未预料到的问题时,经常性的发布可以使开发人员能全面、快速地修复这些问题。

(4)针对不同的发布版本,开发团队将重点放在不同的专业领域技术上。例如,一个发布可以利用用户界面专家的专业知识将系统从命令驱动的界面改为指向—点击式(point-and-click)的图形用户界面,另外一个发布可集中于改进系统性能。

2.2.7 螺旋模型

Boehm根据系统包含的风险看待软件开发过程并提出了螺旋模型。它把开发活动和风险管理结合起来,以将风险减到最小并控制风险(Boehm 1988)。图2-10所示的螺旋模型在某种意义上类似于图2-9所示的迭代开发模型。它以需求和一个初始的开发计划(包括预算、约束、人员安排方案、设计和开发环境)为起点,在产生“操作概念”文档(它从高层描述系统如何工作)之前,该过程插入一个评估风险以及可选原型的步骤。在操作文档中,一组需求被指定并进行详细检查,以确保需求尽可能完整和一致。因此,操作概念是第一次迭代的产品,而需求则是第二次迭代的主要产品。在第三次迭代中,系统开发产生设计,而第四次迭代能够进行测试。

螺旋模型的每一次迭代都根据需求和约束进行风险分析,以权衡不同的选择,并且在确定某一特定选择之前,通过原型化验证可行性或期望度。当风险确认之后,项目经理必须决定如何消除风险或使风险降到最低。例如,设计人员不能确定用户是否更喜欢某一种界面(相比较于另一种界面)。用户有可能会选择阻碍高效率使用新系统的界面,要把这种选择的风险最小化。设计人员可以原型化每一个界面,并通过运行来检验用户更喜欢哪一种界面。甚至可以在设计中选择包含两种不同的界面,这样用户能够在登录的时候选择其中一个。像预算和进度这样的约束有助于确定要选择哪一种风险管理策略。第3章将更详细地讨论风险管理。

2.2.8 敏捷方法

从20世纪70年代到90年代提出并使用的许多软件开发方法都试图在软件构思、文档化、开发和测试的过程中强加某种形式的严格性。在20世纪90年代后期,一些抵制这种严格性的开发人员系统地阐述了他们自己的原则,试图强调灵活性在快速有效的软件生产中所发挥的作用。他们将他们的思想整理为“敏捷宣言”,概括为以不同的方式思考软件开发的4条原则(Agile Alliance 2001)。

相对于过程和工具,他们更强调个人和交互的价值。这种观点包括给开发人员提供他们所需的资源,并相信他们能够做好自己的工作。开发团队将他们组织起来,让他们进行面对面交互式的沟通而不是通过文档进行沟通。

他们更喜欢在生产运行的软件上花费时间,而不是将时间花费在编写各种文档上。也就是说,对成功的主要测量指标是软件正确工作的程度。

他们将精力集中在与客户的合作上,而不是合同谈判上,从而,客户成为软件开发过程的一个关键方面。

他们专注于对变化的反应,而不是创建一个计划而后遵循这个计划,因为他们相信不可能在开发的初始就能预测到所有的需求。

敏捷开发的总体目标是通过“尽可能早地、持续地交付有价值的软件”使客户满意(Agile Alliance 2001)。很多客户都有一些随着时间变化的业务需求,不仅表现在新发现的需求上,也表现在对市场变化做出反应的需求上。例如,当软件正在设计和构造的时候,某一个竞争对手发布了一个新的产品,因此,需要在已经计划好的功能上做一些改变。类似地,政府机构或标准制订机构可能会强制推行一项规则或标准,而它可能影响到软件的设计或需求。人们认为,通过在软件开发过程中加入灵活性,敏捷方法使用户能够在开发周期的后期增加或改变需求。

在目前的文献中,有很多敏捷过程的典型方法。每一种方法都基于一套原则,这些原则实现了敏捷方法所宣称的理念(敏捷宣言)。具体方法有以下几种。

极限编程(XP):在下面会对它进行详细描述。它是激发开发人员创造性、使管理负担最小的一组技术。

水晶法(Crystal):它认为每一个不同的项目都需要一套不同的策略、约定和方法论。水晶法正是基于这一理念的一组方法。Cockburn是水晶法的创建者(Cockburn 2002)。他认为,人对软件质量有重要的影响,因而随着开发人员素质的提高,项目和过程的质量也随之提高。通过更好的交流和经常性的交付,软件生产力得以提高,因为它较少需要中间工作产品。

并列争球法(Scrum):该方法由对象技术公司于1994年创建,随后Schwaber和Beedle将它产品化(Schwaber and Beedle 2002)。它使用迭代的方法,其中把每30天一次的迭代称为一个“冲刺”(sprint),并按需求的优先级别来实现产品。多个自组织和自治小组并行地递增实现产品。协调是通过简短的日常情况会议(称为“scrum”)来进行的,就像橄榄球中的“并列争球”(scrum)。

自适应软件开发(ASD):它有6个基本的原则。在自适应软件开发中,有一个使命作为指导,它设立项目的目标,但并不描述如何达到这个目标。特征被视作客户价值的关键点,因此,项目是围绕着构造的构件来组织并实现特征的。过程中的迭代是很重要的,因此“重做”与“做”同样关键,变化也包含其中。变化不被视作改正,而是被视作对软件开发实际情况的调整。确定的交付时间迫使开发人员认真考虑每一个生产的版本的关键需求。同时,风险也包含其中,它使开发人员首先解决最难的问题。

通常,“极限编程”是描述敏捷方法最普遍的概念。实际上,XP是敏捷过程的一种具体形式,提供敏捷方法最一般原则的指导方针。XP的支持者强调敏捷方法的4个特性:交流、简单性、勇气以及反馈。交流是指客户与开发人员之间持续地交换看法;简单性鼓励开发人员选择最简单的设计或实现来处理客户的需要;XP创建者将勇气描述为尽早和经常交付功能的承诺;在软件开发过程的各种活动中,都包含反馈循环。例如,程序员们一起工作,针对实现设计的最佳方式,相互提供反馈;客户与开发人员一起工作,以完成计划的任务。

这些特性都包含在XP的12个实践操作中。

规划游戏:在XP的这一方面,由现场的客户定义价值的含义,以便对于每个需求,可以根据实现该需求所增加的价值对其进行评价。用户就系统应该如何运转来编写故事,然后,开发人员估算实现该故事所必需的资源。这些故事描述所涉及的演员和情节,很像在第4章和第6章定义的用例。每一个故事针对一个需求:只需要两三个句子足够详细地解释需求的价值,以便开发人员指定测试用例,估算实现需求所需的资源。故事编完之后,预期的用户对需求划分优先级,不断地拆分、合并需求,直到就需要什么、什么可测试、利用可用资源能够完成什么这些事项达成一致为止。然后,计划人员生成发布图,将发布的内容和交付的时间记录在文档中。

小的发布:系统的设计要能够尽可能早地交付。功能被分解为若干个小的部分,这样,可以尽早地交付一些功能。然后,在后面的版本中对这些功能加以改进和扩展。这些小的发布需要使用增量或迭代生命周期的阶段化开发方法。

隐喻:开发团队对于系统将如何运行的设想取得一致意见。为了支持这个共同的设想,开发团队选取共同的名字,并就处理关键问题的共同方法达成一致意见。

简单设计:只处理当前的需求,使设计保持简单。这种方法体现这样一个基本思想:对将来的需求进行预测可能导致不必要的功能。如果系统的某个特定部分是非常复杂的,那么开发团队可能要构建一个试验性解决方案(spike)(一个快速、有限的实现)以帮助决定如何继续进行。

首先编写测试:为了确保客户的需要成为开发的驱动力,首先编写测试用例,这是一种强迫客户需求在软件构建之后可以被测试和验证的方法。XP使用两种测试:功能测试和单元测试。功能测试由客户指定,由开发人员及用户测试;而单元测试由开发人员编写和测试。在XP中,功能测试是自动执行的,并且在理想情况下,每天都执行。功能测试被认为是系统规格说明的一部分。在编码前后都要进行单元测试,以验证每一个模块都符合设计规格说明。第8章将详细讨论这两种测试。

重构:随着系统的构建,很可能需求将发生变化。因为XP方法的一个主要特征是只针对当前的需求进行设计,所以,经常出现这样的情况:新的需求迫使开发人员重新考虑他们现有的设计。重构(refactoring)是指重新审视需求和设计,重新明确地描述它们以符合新的现有的需要。有时,重构是指重组(restructure)设计和代码,而不扰乱系统的外部行为。重构是以一系列小的步骤完成的,辅之以单元测试和对编程,用简单性指导工作。我们将在第5章讨论重构的难点。

对编程:如第1章指出的,将软件工程视作艺术和将软件工程视作科学这两种观点之间存在着紧张关系。对编程试图强调软件开发的艺术性这一方面,承认学徒-师父这样的隐喻,对于教会软件开发初学者如何逐步具有熟练开发人员的能力是很有用的。使用一个键盘,两个结成对的程序员,根据需求规格说明和设计开发系统,由一个人负责完成代码。但是,配对是灵活的:一个开发人员在一天中可能与多个伙伴配对。传统的开发方法是个人单独工作,直到他们的代码经过单元测试。第7章会将对编程与传统方法进行比较。

集体所有权:在XP中,随着系统的开发,任何开发人员都能够对系统的任何部分进行改变。在第11章,我们将讨论管理变化过程中的难点,包括当两个人试图同时改变同一个模块的时候引入的错误。

持续集成:快速交付功能意味着可以按日为客户提供可运行的版本,有时甚至可以按小时提供。重点是多个小的增量或改进,而不是从一个修正到下一个修正这样的巨大跳跃。

可以忍受的步伐:疲劳可能产生错误。因此,XP的支持者提出每星期工作40个小时的目标。逼迫程序员投入很长的时间来满足最后期限,就表明最后期限不合理,或者是缺乏满足最后期限的资源。

在现场的客户:理想情况下,客户应该在现场与开发人员一起工作以确定需求,并提供如何对它们进行测试的反馈。

代码标准:很多观察者认为XP和其他敏捷方法提供了不受约束的环境,在其中可以做任何事情。但是实际上,XP倡导清晰的代码标准定义,以利于团队改变和理解他人的工作。这些标准支持其他的实践,例如测试和重构。其结果应该是代码整体看起来就像是由一个人编写的,并且其方法和表述一致。

极限编程和敏捷方法是比较新的方法(补充材料2-2)。其有效性的证据很少,但却呈增长趋势。在后面的章节讨论其相关活动的时候,我们将再次讨论很多敏捷方法和概念,以及它们的实证性评估。

本章出现的过程模型仅仅是实际使用或讨论的模型中的一小部分。其他过程模型可以根据用户、客户和开发人员的需要进行定义和剪裁。正如在补充材料2-3中指出的那样,实际上,我们应该用一组过程模型描述软件开发过程,而不是集中于单个模型或视图。

补充材料2-2 什么时候极限编程显得过于极端?

就像大多数软件开发方法一样,敏捷方法也招致了一些批评。例如,Stephens和Rosenberg指出,很多极限编程的实践是相互依赖的,如果其中一个被修改,其他的都会受到影响(Stephens and Rosenberg 2003)。要了解其中的原因,我们假定一些人对于对编程是不满意的。那么,就可能需要更多的协调和文档来解决当人们各行其是时失去的共识。类似地,许多开发人员喜欢在编程之前进行设计。Scrum通过建立每月冲刺来处理这种喜好。Elssamadissy和Schalliol指出,在极限编程中,需求被表示为一系列必须能通过软件审查的测试用例(Elssamadissy and Schalliol 2002)。这种方法可能促使客户代表关注测试用例而不是需求。因为测试用例是需求的详细表述,并且可能是面向解决方案的,所以,将重点放在测试用例上可能会将客户代表的注意力从项目的目标转移开,并且可能导致这样一种情形:系统通过了所有测试,但是却不是客户认为他们应该得到的系统。正像我们将在第5章中看到的,重构可能是敏捷方法的要害,很难做到重做一个系统而不降低体系结构的质量。

补充材料2-3 过程模型的集合

我们在补充材料2-1中看到,开发过程是一个问题求解的活动,但是流行的过程模型很少会包含问题求解。Curtis、Krasner和Iscoe对17个大型项目进行了现场研究,以确定过程模型中应获取哪些问题求解的因素,以帮助我们理解软件开发(Curtis,Krasner and Iscoe 1988)。尤其是,他们考虑了影响项目结果的行为因素和组织因素。他们的研究结果提出了一个关于软件开发层次的行为模型,其中包含5个关键视角:业务环境、公司、项目、开发团队和个人。个人视图提供关于认知和动机的信息,项目和开发团队的视图告诉我们团体动态的相关情况。公司和业务环境提供了可能影响生产率和质量的组织行为的信息。这个模型并不是要替换传统的过程模型,它与传统模型是正交的关系,提供的信息是行为如何影响创建和生产活动,它是对传统模型的补充。

随着开发人员和客户对问题了解的加深,他们把彼此的领域知识、技术和业务结合起来,以得到一个合适的解决方案。通过把开发看作是一组相互协作的过程,我们可以看到学习、技术交流、客户交互和需求协商的效果。不过,当前描述一系列开发任务的模型“对下述问题的分析是没有帮助的:项目成员必须了解多少新信息,如何协商那些有分歧的需求,设计团队如何解决体系结构的冲突以及类似因素对项目内在的不确定性和风险有何影响”(Curtis,Krasner and Iscoe 1988)。然而,当我们引入认知的、社会的、组织的和过程的相关模型时,我们就开始了解瓶颈和低效率的原因。正是这样的认识使得管理人员能够理解和控制开发过程。而通过总体考虑跨越各个模型层的行为,可以了解每个模型对另一个模型因素的效果所产生的影响。

不论使用哪一种过程模型,许多活动都是所有模型所共有的。在后面章节中对软件工程进行探讨的时候,我们将研究每一个开发活动,了解它包含的内容,并找出什么样的工具和技术能够使我们的工作更加有效、生产率更高。

2.3 过程建模工具和技术

一旦你决定了要从过程模型中得到什么,会有很多建模工具和技术可供选择。从前面章节模型的描述中,我们已经了解了一些建模的方法。选择的建模技术是否合适,取决于你的目标和喜欢的工作方式。尤其是,对表示法的选择取决于你想要用模型表示的内容。表示法可以是从文本到图形的各种方式。文本方式把过程表示为函数,图形方式把过程描述成由正方形和箭头组成的层次结构,图形和文本结合的方式把图形化的描述与表格和函数结合在一起,共同对过程从较高层次进行说明。许多建模表示法也可用于表示需求和设计,我们将在后面的章节中对其中一些进行讨论。

在本章中,表示方法是从属于模型类型的。我们集中精力讨论两种主要的模型:静态模型和动态模型。静态模型(static model)描述过程,表明了从输入到输出的转换过程。动态模型(dynamic model)能够动态展现过程,这样用户能够看到中间产品和最终产品是如何随着时间的推移进行转换的。

2.3.1 静态建模:Lai表示法

静态建模的方式有许多种。在20世纪90年代早期,Lai设计了一种全面的过程表示法,目的是让人们能够在任何细节的层次上对任何过程都可以建模(Lai 1991)。它是在一种范型的基础上建立的,在这种范型中:人扮演角色,资源执行活动,从而产生制品。这个过程模型表明了角色、活动和制品之间的相互关系。而状态表说明在给定的时间内,每个制品完成情况的信息。

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

(1)活动:过程中将要发生的事情。该要素可以与下面几项相关联:该活动前后发生的事情、所需的资源、使活动开始的触发器、支配活动的规则、如何描述算法以及得到的经验教训,以及如何将该活动与项目团队联系起来。

(2)序列:活动的顺序。序列可用触发器进行描述,也可以用程序结构、转换、排序或满足的条件来描述。

(3)过程模型:是关于系统兴趣的观点。因此,部分过程可以用单独的模型表示,或者用以预测过程行为,或者用以检查某些特性。

(4)资源:必要的项、工具或人员。资源可以包括设备、时间、办公空间、人员、技术,等等。过程模型确定每个活动对于每一个资源所需的数量。

(5)控制:施加于过程执行的外部影响。控制可以是手工的或自动的、人工的或机械的。

(6)策略:指导原则。它是影响过程执行的高层的过程约束,可能包含一个规定的开发过程、必须使用的工具或强制性的管理模式。

(7)组织:过程代理的层次化结构,使物理的分组与逻辑分组及相关角色相对应。从物理分组到逻辑分组的映射必须足够灵活以便反映物理环境的变化。

过程描述本身具有若干层次的抽象,包括软件开发过程(它指导特定资源用于构造特定的模块)以及类似于螺旋模型或瀑布模型的一般模型。Lai表示法包括若干模板,例如,记录特定制品信息的制品定义模板。

Lai方法可以用于软件开发过程建模。在本章后面的论述中,我们将利用它为开发过程所涉及的风险建模。为了演示如何使用它以及它表示复杂活动多方面信息的能力,我们把它应用到相对简单、但又十分熟悉的过程中:驾驶汽车。表2-1是对这个过程中的关键资源——汽车(car)——的描述。

其他的模板定义了关系、过程状态、操作、分析、动作和角色。绘制的图表示要素之间的相互关系,保存主要关系和次要关系。例如,图2-11说明启动汽车的过程。“initiate”框表示进入条件,“park”框表示退出条件。条件框中的左列列出了制品,右列列出了相应制品的状态。

转换图是对过程模型的补充,它说明状态之间是如何联系的。例如,图2-12显示了汽车的状态转换。

关于如何用多个结构和策略来获取大量关于软件开发过程的信息,Lai表示法是一种很好的例子。而且,如汽车这个例子演示的那样,Lai表示法也可用于组织和描述有关用户需求的过程信息。

2.3.2 动态建模:系统动力学

过程模型的一个良好特性就是演示过程的能力,这样,随着活动的发生,我们就可以观察资源和产品发生了什么情况。换言之,我们想要描述这个过程的模型,并且在软件向我们展示资源流是如何通过活动成为输出的时候可以进行观察。这种动态过程的视图使我们能够模拟过程,并在实际消耗资源之前能够进行修改。例如,可以使用动态过程模型帮助我们确定需要多少名测试人员及必须何时启动测试才能够按进度完成。同样,可以增加或去除活动,看一看它们对工作量和进度的影响。例如,可以增加一个代码评审活动,对评审过程中发现的故障数量做出假设,以便确定评审是否明显地减少了测试时间。

建立动态过程模型的方法有很多种。Forrester于20世纪50年代提出了系统动力学方法。该方法在模拟不同的过程(包括生态、经济和政治系统)中一直很有用(Forrester 1991)。Abdel-Hamid和Madnick曾把系统动力学方法应用到软件开发中,使项目经理在开发人员中强行推行过程之前,能够对他们的过程选择进行“考验”(Abdel-Hamid 1989;Abdel-Hamid and Madnick 1991)。

要了解系统动力学方法是如何运作的,可以考虑一下软件开发过程是如何影响生产率的。我们可以构建包括开发人员时间在内的各种活动的描述性模型,然后考虑模型中的变化是怎样增加或减少设计、编写和测试代码所用的时间的。首先,必须确定哪些因素对总生产率有影响。图2-13描述了Abdel-Hamid对这些因素的理解。箭头表明一个因素中的变化是如何影响另一个因素中的变化的。例如,如果在分配给项目的人员中,有经验的职员所占比例从1/4增加到1/2,那么,我们预期平均生产率也会有所提高。同样,职员所占的比例越大(反映在职员规模上),那么用于项目团队成员之间交流的时间就越多(交流开销)。

从图2-13可以得知,名义平均潜在生产率受下面3种因素影响:有经验职员的生产率、有经验职员的比例以及新职员的生产率。同时,新职员必须了解这个项目。项目完成的部分越多,则新职员在他们能够成为团队中高产的成员之前,就必须了解得越多。

其他问题也会对总体的开发生产率产生影响。首先,必须考虑每个开发人员每天对项目贡献所占的比例,进度带来的压力会影响这个比例,开发人员对工作量的承受力会影响这个比例。职员规模也会影响生产率,但是职员越多,则项目团队成员用于交流信息的时间就越多。交流、动机以及潜在生产率(表示为图2-13的上半部分),三者合在一起给出了一种概括的软件开发生产率关系。

因此,使用系统动力学方法的第一步是将实证性证据、研究报告和直觉这三者结合在一起来标识这些关系。下一步是量化这些关系,量化可以包含直接的关系。例如,职员规模和交流之间的关系。我们知道,如果给一个项目分配n个人,那么可能有n(n−1)/2对人员必须彼此交流和协调。对某些关系,尤其是那些涉及随时间变化的资源的关系,必须进行一些分配来描述资源的增加和减少。例如,在一个项目中,很少会出现每个人都在第一天开始工作的情况。系统分析员首先开始工作,当大量需求和设计构件文档化之后,编程人员才加入到项目中。因此,这种分配就描述了资源的增加和减少(甚至是波动,例如节日或暑假的出勤率)。

一个系统动力学模型可能包含大量信息并且非常复杂。例如,Abdel-Hamid的软件开发模型包含100多个因果链接,图2-14给出了他所定义的关系的总体描述。他定义了影响生产率的4个主要方面:软件生产、人力资源管理、计划以及控制。生产包括质量保证、学习和开发速率等相关问题。人力资源强调雇用、人事变动和经验。计划关心进度安排和由此带来的压力,而控制则强调过程测度和完成项目所需的工作量。

由于系统动力学模型中链接的数目可能相当大,所以存在一些支持软件可以获取链接和它们的量化描述,从而可以模拟整个过程或某些子过程。

系统动力学模型的强大功能给人们留下了深刻的印象,但是必须谨慎地使用这个方法。模拟的结果依赖于量化的关系,但量化的关系常常是启发式的或含糊不清的,它不是明确基于实证性研究的。然而,正如我们在后面的章节中将要看到的那样,使用一个包含开发各方面的测度信息的历史数据库,有助于我们对关系的理解,从而对动态模型的结果更有信心。

补充材料2-4 过程程序设计

在20世纪80年代中期,Osterweil提出应该使用数学描述的方法对软件开发过程进行说明(Osterweil 1987)。也就是说,如果过程被充分理解,我们就能够编写程序来描述这个过程,然后运行这个程序演示这个过程。过程程序设计的目标就是去除不确定性:通过充分理解一个过程来编写软件以抓住它的本质,并将这个过程转化成问题的确定的解决方案。

如果过程程序设计是可能的,那么我们就能够可见地管理所有过程活动、让所有活动自动化,并能轻而易举地协调及改变所有活动。因此,过程程序有可能成为生产软件的自动化环境的基础。

然而,Curtis、Krasner、Shen和Iscoe指出,Osterweil对计算机程序设计的类比没有抓住开发过程内在的变化(Curtis.Krasner.She.an.Isco.1987)。当编写完一个计算机程序之后,程序员假定实现环境工作正常,操作系统、数据库管理器和硬件都是可靠的、正确的。因此,计算机对运算指令的响应几乎不存在变化。但是,当一个过程程序对项目团队中的一个成员发布一条指令时,任务执行的方式和产生的结果都存在着很大的可变性。正如我们将在第3章中看到的那样,技能、经验、工作习惯、对客户需求的理解的差异和许多其他因素都可能极大地增加可变性。Curtis和他的同事建议,过程程序设计应当仅限于那些存在极小变化的情况。而且他们还指出,Osterweil的例子仅仅提供了关于任务序列化的信息;过程程序并没有就那些迫在眉睫的问题向管理人员发出警告。“创造性的智力任务的协调似乎并没有通过当前过程程序设计的实现而显著改善,因为进行协调的最重要的原因是确保所有交互的代理具有同样的关于系统如何运作的概念模型”(Curtis et al.1987)。

2.4 实际的过程建模

很长时间以来,过程建模一直是软件工程研究的焦点。但是,它的实用性如何呢?一些研究人员称,正确地使用过程建模,为理解过程和揭示过程中的不一致性带来了诸多益处。例如,Barghouti、Rosenblum、Belanger和Alliegro进行了两个案例研究,以确定在大型组织中使用过程模型的可行性、效用和限制(Barghouti,Rosenblum,Belanger and Alliegro 1995)。在这一节,我们将讨论他们所做的工作以及他们的研究结果。

2.4.1 MarveI的案例研究

在这两个案例研究中,研究人员都使用MSL(即Marvel规格说明语言)来定义过程,然后,为其生成一个Marvel过程制订环境(Kaiser,Feiler and Popovich 1988;Barghouti and Kaiser 1991)。MSL使用3种主要的结构(类、规则和工具信封(tool envelope))来产生一个3部分的过程描述。

(1)基于规则的过程行为规格说明。

(2)模型的信息过程的面向对象定义。

(3)用作执行该过程的外部软件工具和Marvel之间的接口的一组信封。

第一个案例研究的是美国电话电报公司的呼叫处理网络。该网络处理电话呼叫,还包含一个单独的信令网,它负责为这些呼叫安排路由并平衡网络负载。Marvel用于描述信令故障解析过程,该过程负责检测、维修以及解决信令网络的问题。工作中心1监控该网络、检测故障并把故障提交给其他两个工作中心中的一个进行处理;工作中心2处理需要详细分析的软件故障或人为故障;工作中心3处理硬件故障。图2-15描述了这个过程。双虚线表示哪一个活动使用了工具或数据库,工具或数据库用椭圆表示,矩形框表示任务或活动,菱形框表示判定,箭头指明控制流。该图给出的是概要,并没有提供足够的细节以表示基本的过程要素。

因而,用MSL对每一个实体和工作中心建模。图2-16解释了建模是如何进行的。图的上半部分定义了一个凭单类,这里凭单(ticket)表示当一个失效出现时就记下的一个故障单(fault ticket)(或问题报告)。正像我们将在有关测试的章节中看到的那样,故障单用于跟踪一个问题(从问题的出现到它的解决)。整个网络表示为22个这样的MSL类,一个过程的创建或需要的所有信息都包含在其中。

接着,该模型强调了信令故障解析过程的行为方面的信息。图2-16的下半部分是一个MSL规则,它大致对应于图2-15中标有“诊断”的方框。因而,MSL描述了诊断未决问题的规则,它由每一个打开的凭单激发。当过程模型完成时,需要21个MSL规则来描述这个系统。

第二个案例是研究美国电话电报公司的5ESS交换机软件的部分维护过程。与第一个案例研究不同(第一个案例研究的目标是过程改进),第二个案例研究的目的只是用MSL获取过程步骤和交互从而把它们文档化。该模型包含有25个类和26个规则。

对每一个模型,用MSL过程描述生成“过程制订环境”,它产生一个含有信息模型类实例的数据库。接着,研究人员模拟若干场景以验证模型是否像期望的那样执行。在模拟的过程中,他们收集计时和资源使用情况的数据,为分析合适的过程执行提供基础。通过改变这些规则并重复执行一个场景,对计时进行比较和对照,从而不用在资源方面进行大的投资就得到显著的过程改进。

建模和模拟执行对早期识别问题和解决问题是非常有用的。例如,软件维护过程定义揭示了现有过程文档中的3种类型的问题:缺少任务输入和输出、含义模糊的输入输出标准以及低效率的过程定义。信令故障模型的模拟揭示了各工作中心单独描述造成的低效率。

Barghouti和他的同事指出,把过程建模问题划分成建模信息和建模行为是非常重要的。通过将这两方面分离开,产生的模型清晰且简洁。他们还指出,计算机密集的活动比人员密集的活动更容易建模,Curtis和他的同事也指出了这一经验。

2.4.2 过程建模工具和技术应该具有的特性

有很多过程建模的工具和技术,而且研究人员不断地努力,以确定在给定情况下哪些工具和技术是最合适的。但是,有一些特性对任何一种技术都是有益的。Curtis、Kellner和Over标识了以下5类良好的特性(Curtis,Kellner and Over 1992)。

(1)促进人们的理解和交流。该技术应该使用一种大多数客户和开发人员能理解的方式来表示过程,鼓励关于过程的交流并对其形式和改进达成一致。该技术应该包含足够的信息,以便能够实际执行该过程,并且模型或工具应当成为培训的基础。

(2)支持过程改进。该技术应当标识开发或维护过程的基本构件。它应当允许在后面的项目中复用过程或子过程,并能比较不同的可选方案,而且在过程实际投入使用之前估算变化造成的影响。同样,该技术应该有助于为过程选择工具和技术,有助于有组织的学习,支持持续的过程演化。

(3)支持过程管理。该技术应该允许过程是针对特定项目的。这样,开发人员和客户应该能够推测软件创建或演化的属性。该技术还应该支持计划和预测、监控和管理过程以及测量关键的过程特性。

(4)在执行过程时提供自动化的指导。该技术应该定义所有的或部分的软件开发环境,提供指导和建议,并保留可复用的过程表示供以后使用。

(5)支持自动化的过程执行。该技术应该让全部的或部分过程自动化,支持协同工作,获取相关的测度数据,以及强制规则以保证过程的完整性。

当为开发项目选择过程建模技术时,这些特性可以作为有用的指导。如果你的组织机构试图将其过程标准化,那么第4个特性特别重要。工具能够提示开发人员下一步做什么,并且提供入口和检查点,以确保在下一步之前,制品满足了某些标准。例如,工具可以检查一组代码构件,评估它的规模和结构。如果规模或结构超出了预先定义的限制,那么,可以在测试开始之前通知开发人员,而某些构件可能被重新检查,也可能要重新设计。

2.5 信息系统的例子

让我们考虑一下用哪种开发过程来支持皮卡地里电视广告程序系统,回想到对什么时间可以销售何种类型的广告会有许多约束,并且条例可能随着广告标准局及其他制订条例的团体的管理而变化,我们希望建立一个易于维护、易于改变的软件系统。甚至存在这样一种可能,当构建系统的时候约束也可能发生变化。

就此系统而言,瀑布模型可能太严格了。因为在需求分析阶段完成之后,它几乎不允许变化。原型化方法对开发用户界面来说可能是有用的,因此,我们可能想在模型中包含某种类型的原型化。但是在广告条例和业务约束中有很多不确定因素。我们想要使用这样的过程模型:当系统演化的时候模型仍可以使用和复用。对构建皮卡地里系统而言,螺旋模型的变种可能是一个很好的选择,因为它鼓励重新审视先前的假设、分析风险以及原型化各种系统特性。如螺旋模型的左上1/4部分所显示的那样,对各种方案的反复评估,有助于我们把灵活性融入到需求和设计之中。

Boehm对螺旋模型的表示是高层次的,未提供足够的细节来指导分析人员、设计人员、编码人员和测试人员的行动。但是,有很多技术和工具可以在更精细的详细层次上表示过程模型。技术和工具的选择部分取决于个人的偏好和经验,部分依赖于表示过程的类型的合适程度。让我们来看一下如何使用Lai表示法来表示皮卡地里系统的部分开发过程。

由于我们希望使用螺旋模型来帮助管理风险,因此必须在过程模型中引入关于“风险”的特性描述。这就是说,风险是必须描述的制品,从而能够在螺旋的每一次迭代中测量和跟踪风险。每一个潜在的问题都具有相关联的风险,可以从概率和严重性两方面考虑风险。概率(probability)就是某个特定问题将要发生的可能性。而严重性(severity)就是它将要对系统造成的影响。例如,假定我们正在考虑这样的问题:构建皮卡地里系统正在使用的开发方法是否经过充足的培训?我们可能决定使用面向对象的方法,但是可能会发现,项目开发人员的面向对象的经验很少甚至没有。这个问题发生的概率可能很小,因为所有的新雇员都会被送去参加为期4周的面向对象开发课程的强化培训。另一方面,如果这样的问题真的发生了,那么它将对开发团队在指定的时间内完成该软件的能力产生严重的影响。因此,这个问题发生的概率很低,但是它的严重性很高。

我们可以用Lai制品表来表示这些风险的情况,如表2-2所示。在这里风险(risk)是制品,概率和严重性是它的子制品。为简单起见,对每一个子制品,我们只选定两个状态:概率的高和低,严重性的大和小。事实上,每一个子制品都有很大的状态区间(像极小、非常小、有些小、中等、有些高、很高、极高等),导致制品本身产生许多不同的状态。

我们可以用同样的方式定义开发过程的其他方面,并使用图表说明活动和它们之间的相互关系。用这种方式对过程建模有很多优点,而不仅仅是对开发需要的内容建立了共同的理解。如果用户、客户和开发人员都参与定义和描述皮卡地里的开发过程,那么,每一个人都会对这些方面抱有期望:开发过程包括什么活动、产生什么,以及何时能够得到每个产品。尤其是可以结合使用螺旋模型和风险表格以定期评价风险。螺旋模型的每一次旋转,都要重新评估和表述每一个风险的概率和严重性。当风险高到不可接受时,可以修改过程模型以引入减轻和降低风险的技术,我们将在第3章中了解这些内容。

2.6 实时系统的例子

阿丽亚娜5型火箭的软件系统包含了从阿丽亚娜4复用的软件。复用的目的是为了降低风险、提高生产率和质量。因此,开发新的阿丽亚娜软件的任何过程模型都应当包含复用活动。尤其是过程模型必须包含一些活动以检查可复用构件的质量,还要包含一些安全措施,以确保复用的软件在新系统的设计环境下能够正确地工作。

这样一个过程模型有可能像图2-17所示的简化模型。图示模型中的方框表示活动。从左边进入方框的箭头表示资源,从右边离开方框的箭头表示输出,从顶部进入的箭头表示控制或约束,例如进度、预算或标准。从下部进入的箭头表示机制,这些机制辅助开发人员执行活动,例如工具、数据库或技术。

阿丽亚娜4复用过程开始于软件的任务(即控制一个新的火箭)以及来自以前机体的软件、未满足的需求和从其他可用资源(例如购买的软件或来自其他项目的复用库)获得的软件构件。开发人员可以基于飞船建设人员的商业策略,标识出可复用的子过程,对其进行描述(也许使用与过去的经验相关的注解),并把它们放在库中供需求分析人员考虑。可复用的过程常常包含可复用的构件(即可复用的需求、设计或代码构件,甚至是可复用的测试用例、过程描述以及其他的文档和制品)。

接着,需求分析人员分析关于新机体的需求以及库中可用的复用构件。他们提出一组修订后的需求,其中包括新的需求和可复用的需求。然后,设计人员用这些需求设计软件。一旦完成设计之后,就对所有的复用设计构件进行评估,以证实它们是正确的,并且与新设计的部分和在需求中描述的系统的整体目标相一致。最后,用经过认证的构件构建或修改软件,从而生成最终的系统。正如我们将在后面章节中看到的那样,这样一个过程原本是有可能防止阿丽亚娜5型火箭坠毁的。

2.7 本章对单个开发人员的意义

在这一章,我们看到了软件开发过程包含的活动、资源和产品。当你与一个团队一起工作的时候,过程模型对指导你的行为是非常有用的。当你设计和构建一个系统的时候,详细的过程模型可以让你知道如何与你的同事协调和合作。我们也看到过程模型包含了组织的、功能的、行为的和其他侧面,从而使你能够在开发过程的特定方面集中精力以增强你的理解或指导你的行动。

2.8 本章对开发团队的意义

就开发团队而言,过程模型也具有明显的优势。一个好的模型向每一个团队成员展示什么时候发生了什么活动以及应该由谁执行该活动,从而明确责任分工。另外,为了满足项目的预算和进度,项目经理可以使用过程工具来指定过程、模拟活动以及跟踪资源,以决定最佳的人员和活动组合。这种模拟在资源实际提交之前完成,由于无需反馈或改正错误,从而节省了时间和成本。实际上,可以在过程模型中引入迭代和增量开发,这样团队可以从原型中学习或对演化的需求做出反应,并且还能够满足合适的期限。

2.9 本章对研究人员的意义

过程建模是软件工程中非常受关注的研究领域。许多软件开发人员感到,通过使用好的过程,开发的产品的质量可以得到保证。研究人员目前研究的领域有以下几个。

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

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

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

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

许多工作是与过程改进的研究相互协调的,过程改进领域将在第13章中予以讨论。

2.10 学期项目

现在是FCO的Loan Arranger系统开发过程的早期阶段。你还没有获得该系统全面的需求。现在所有的只是系统功能的概要描述及如何使用该系统来支持FCO业务的感觉。你还不熟悉概要中使用的许多术语,因此,你要求客户代表准备一份术语表。他们给你的术语描述见表2-3。

以上信息澄清了一些概念,但仍然远不是一组好的需求。不过,你能够据此对如何进行开发作出一些初步的决策。请读者回顾在本章中所介绍的过程,决定哪些过程适于开发Loan Arranger系统。针对每一种过程,基于Loan Arranger系统,列出它的优点和缺点。

2.11 主要参考文献

在第5届国际软件过程研讨会(Fifth International Software Process Workshop)上,Kellner主持的一个工作组系统地阐述了用来评估和比较一些较流行的过程建模技术的相关标准问题。他们使标准化问题呈现出充分的多元化,以便能够测试一种技术的能力,包括以下几个方面。

抽象的多层次。

控制流、时序以及对时序的约束。

判定点。

迭代和对早期步骤的反馈。

用户的创造性。

对象和信息管理,以及过程中的流程。

对象的结构、属性和它们之间的相互关系。

特定任务的组织责任。

信息传递的物理通信机制。

过程测度。

时态(绝对的和相对的)。

由人执行的任务。

专业的评判或判断力。

与叙述性解释的关系。

被工具调用或执行的任务。

资源的约束和分配,进度的确定。

过程修改和改进。

多层次聚合和并行。

针对一个共同的问题,使用了18种不同的过程建模技术,每一种技术都得到了不同的满意度。Kellner和Rombach报告了这些结果(Kellner and Rombach 1990)。

Curtis、Kellner和Over给出了一个关于过程建模技术和工具的全面性的综述(Curtis,Kellner and Over 1992)。这篇论文也总结了基本的语言类型和概念,给出了使用那些语言类型的过程建模方法的例子。

Krasner等人描述了在商业环境中实现软件过程建模系统所获取的经验和教训(Krasne.e.al.1992)。

下面几个网站包含有过程建模的一些信息。

美国软件工程研究所(the U.S.Software Engineering Institute,SEI)一直在研究过程建模,这是他们过程改进工作的一部分。可以在http://www.sei.cmu.edu上找到其技术报告和活动的列表。网页http://www.sei.cmu.edu.collaborating/spins/描述了软件过程改进沙龙的相关信息。该沙龙是对过程改进感兴趣的人员按不同地理位置组成的小组,经常聚会、听讲座或讨论过程相关的问题。

欧盟长期资助过程建模和过程模型语言的研究。关于当前研究项目的描述可以从http://cordis.europa.eu/fp7/projects_en.html获得。

软件工程数据和分析中心维护着一个软件过程的资源列表,网址是http://www.thedacs.com/databases/url/key/39。

你可以在David Weiss和Robert Lai的书中查阅到更多的信息,书名是Software Product Line Engineering:A Family-based Software Development Process(Weiss and Lai 1999)。

南加州大学的软件工程中心开发了一个工具,可以帮助你选择适合自己项目需求和约束的过程模型。它可以从ftp://usc.edu/pub/soft_engineering/demos/pmsa.zip下载,可以在该中心网站http://sunset.usc.edu上找到更多的信息。

Software Process-Improvement and Practice等期刊上有文章专门讨论软件开发和维护中过程建模的作用。它们还报道了相关会议的重要内容,如国际软件过程研讨会和软件工程国际会议(International Conference on Software Engineering)。IEEE Software的2000年7月/8月刊重点讨论了过程多样性的问题,其中有几篇文章介绍了过程成熟度方法在软件开发上取得的成功。

有很多学习敏捷方法的资源。敏捷宣言发布于网站http://www.agilealliance.org。Ken.Beck的书(Beck 1999)是关于极限编程的开创性著作。Alistair Cockburn(Cockburn 2002)描述了Crystal方法集。Martin Beck解释了重构,这是极限编程中最困难的步骤之一(Martin Beck 1999)。Robert C.Martin的关于敏捷软件开发的书(Martin 2003)以及Daniel H.Steinberg和Daniel W.Palmer关于极限软件工程的书(Steinberg and Palmer 2004),是关于敏捷方法的两部优秀的参考文献。在http://www.xprogramming.com和http://www.extremeprogramming.org上提供了更多关于极限编程的信息。

2.12 练习

1.如何将一个系统描述与过程模型的表示法联系起来?例如,你如何确定一个过程模型描述的系统边界是什么?

2.针对本章描述的每一种过程模型,讨论使用该模型的优点和缺点分别是什么?

3.针对本章描述的每一种过程模型,讨论该模型是如何处理开发后期重要的需求变化的?

4.画一个图,试描述为一次商务旅行购买一张飞机票的过程。

5.画一张Lai制品表来定义一个模块。确保你包含了制品状态以说明该模块什么时候是未测试的、部分测试的和完全测试的。

6.选择一种表示法,并使用该表示法画出一个软件开发过程的过程图,对3种不同的设计进行原型化,并选出其中最好的一种设计。

7.分析2.4节介绍的好的过程模型的特性。如果一个项目对问题和解决方案并未很好地理解,那么用于该项目的过程应该具有哪些本质特性?

8.本章中,我们认为软件开发是一个创造的过程,而不是一个制造的过程。讨论适用于软件开发的制造特性,并解释软件开发的哪些特性更类似于一种创造性行动。

9.一个开发组织是否应该对它的所有软件开发都采用同一种过程模型?讨论这样做的利与弊。

10.假设你与客户签订的合同中规定必须使用某种特定的软件开发过程。应该怎样进行管理来推行该过程的使用呢?

11.考虑本章介绍的过程。哪些过程在你对需求变化做出反应时给了你最大的灵活性?

12.假设Amalgamated公司在与你签约构建一个系统时,要求你使用一个给定的过程模型。你遵守了约定,在构建软件时使用了规定的活动、资源和约束。在软件交付和安装后,你的系统经历了灾难性的失败。当Amalgamated公司调查失败的原因时,你被指责没有进行代码评审,而代码评审原本可以在软件交付前发现问题。你回答说在公司要求的过程中并没有代码评审。请问这场辩论中的法律和道德问题是什么?

相关图书

有限元基础与COMSOL案例分析
有限元基础与COMSOL案例分析
程序员的README
程序员的README
现代控制系统(第14版)
现代控制系统(第14版)
现代软件工程:如何高效构建软件
现代软件工程:如何高效构建软件
GitLab CI/CD 从入门到实战
GitLab CI/CD 从入门到实战
科学知识图谱:工具、方法与应用
科学知识图谱:工具、方法与应用

相关文章

相关课程