软件开发前沿技术_第1页
软件开发前沿技术_第2页
软件开发前沿技术_第3页
软件开发前沿技术_第4页
软件开发前沿技术_第5页
已阅读5页,还剩55页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

软件工程前沿开发技术

经过近四十年的发展,软件工程在支持软件系统工程化开发方面取得了令人瞩目的成

绩,提出了大量的理论、方法、技术和工具,但是近年来的研究和实践表明软件危机依然存

在,软件开发仍然存在成本高、质量得不到保证、进度和成本难以控制等方面的问题,许多

软件项目被迫延期甚至取消。与此同时,随着网络技术(尤其是Internet技术)的不断发

展,部署在网络基础上的软件系统的规模和复杂性越来越高,并表现出诸如持续性、自适

应性、交互性、动态性、开放性、异构性等特点。因此,如何支持这类复杂系统的开发、缓

解和消除现阶段的软件危机是当前软件工程面临的一项重要挑战。为了迎接上述挑战,近年

来软件工程领域的一些学者提出了许多新的方法和技术,包括:敏捷软件开发(Agile

SoftwareDevelopment)>极限编程(ExtremeProgramming,XP)、测试驱动的开发(Test-Driven

Development,TDD)、面向Agent的软件开发(Agent-OrientedDevelopment)、面向方面的

编程(Aspect-OrienledProgramming,AOP)>模型驱动体系结构(Model-DrivenArchitecture,

MDA)等等。与传统的软件工程方法相比较,这些方法和技术为软件工程实践提供了新的思

路,已在许多软件工程实践中取得了积极的效果。

1敏捷软件开发

软件工程一直以来都面临着一个共同的问题,即如何迅速、高效地开发软件系统,适应

用户需求的快速变化,确保软件系统的质量,控制软件开发成本。传统软件开发方法强调软

件开发需遵循严格的过程模型以及以此为基础的开发计划,并且在软件开发过程中需产生大

量的规范化文档,这一思想和方法很难应对快速、灵活和低成本软件开发所带来的一系列问

题。自二十世纪九十年代以来,软件工程领域涌现出了一批新的软件开发方法。这些方法主

张软件开发只编写少量文档、以用户为中心、主动适应需求变化。这些方法被称为敏捷软件

开发,其代表性的成果是极限编程。

1.1敏捷思想

至今人们已提出了几十种软件开发方法,根据这些方法在对软件开发所提出的要求和约

束等方面的差异,现有的软件开发方法大致可分为两类:重型软件开发方法和轻型软件开发

方法。重型软件开发方法一般具有严格和详尽的软件开发过程,软件开发需产生大量的文档。

轻型软件开发方法则强调软件开发过程的简洁性和灵活性,软件开发只需编写少量的文档。

敏捷软件开发是一类轻型的软件开发方法,它提供了一组思想和策略来指导软件系统

的快速开发并响应用户需求的变化。不同于已有的其它软件开发方法,该方法对软件开发具

有以下四个方面的基本认识:(1)较之于过程和工具,应更加重视人和交互的价值;(2)

较之于面面俱到的文档,应更加重视可运行软件的价值;(3)较之于合同谈判,应更加重

视客户合作的价值;(4)较之于遵循计划,应更加重视响应用户需求变化的价值

敏捷软件开发方法认为人是软件开发中最为重要的因素,软件开发应坚持以人为本;

优秀的软件开发团队离不开人员之间良好的沟通与合作,相比较而言团队的合作与沟通能力

比单纯的编程能力更为重要,改善人员之间的交流与合作将有助于提升团队的软件开发水

平;应根据软件开发团队的特点选择合适的软件开发过程;在软件开发工具的选择方面,敏

捷软件开发主张从使用小工具开始,只有当小工具不能满足要求时才考虑选择和使用功能

强大的工具。

一直以来,人们将文档视为是对软件开发各个阶段成果进行记录、促进人员之间进行

交流的重要媒介和工具,也是软件开发和维护的主要依据。然而,编制过多的文档不仅会耗

费大量时间和精力,而且当用户需求变化时难以实现文档与代码的同步,这势必会影响软件

系统的开发和维护。敏捷软件开发方法提倡在软件开发过程中只编写少量短小精炼的文档。

成功的软件开发不应单纯依赖于合同条款和工作说明,而应将用户和软件开发团队紧

密地结合在一起,让用户积极参与软件开发并提供持续不断、频繁的反馈信息。在软件开发

过程中,用户需求总会发生变化,这是由于用户需求难以一次性完全捕获,开发人员和用户

对于需求的认识会不断地调整。此外,用户的业务本身也可能会动态地发生变化。在复杂软

件系统的开发过程中,响应用户需求变化的能力常常决定着软件项目的成败。为了适应用户

需求的变化,敏捷软件开发认为软件开发计划不应考虑的太远,不要进行过于周密、详细的

计划,只应覆盖短期的工作任务,对于中长期的任务只需有一个粗略的规划即可,要保留计

划的充分灵活性,并根据需求的变化适时地调整计划。

在上述思想的指导下,敏捷软件开发提出了以下十二条原则来指导软件系统的开发

(1)尽早和持续地交付有价值的软件,以使用户满意。

敏捷软件开发最关心的是软件系统的交付。诸多软件工程实践表明,初期交付软件系统

中包含的功能越少,最终交付软件系统的质量就越高;软件产品交付的越频繁,最终软件产

品的质量就越高。尽早的交付可以让软件开发团队尽快获得成就感,提升软件开发团队的激

情和效率,尽早从用户处获取对需求、过程、产品等反馈信息。持续性的交付可以让软件开

发团队保持胜利感和成就感,持续获取用户的反馈信息、,及时调整项目实施的方向和优先级。

该原则主张迭代性的软件开发,并强调每一次迭代都选择对用户最有价值的功能作为本

次迭代的任务,迭代周期不宜太长。每次迭代结束以后,就向用户交付一个可运行的、实

现部分需求的软件产品。

(2)即使到了软件开发后期,也欢迎用户需求的变化。

需求不断变化和调整是软件工程化开发的一个重要特点。敏捷软件开发方法的实践者不

应惧怕变化,而应适应用户需求的变化,从而为用户创造竞争优势。为了支持用户需求的变

化,敏捷软件开发所生成的软件结构应具有足够的灵活性,以便在需求变化时能以最小的代

价迅速地做出调整。因此,敏捷软件开发主张采用模式、迭代和重构等技术,以适应用户需

求的变化,获得软件结构的灵活性。

(3)不断交付可运行的软件系统,交付周期可以从几周到几个月。

敏捷软件开发主张软件开发团队应经常性地向用户交付可运行的软件系统,而不是大

量的文档或者计划。交付的周期要适宜,太长易使用户失去耐性,软件开发团队也无法从用

户处及时获得反馈信息;过短会使用户难以接受持续不断的软件产品版本。

(4)在整个软件项目开发期间,用户和开发人员最好能每天一起工作。

为了使软件开发过程保持“敏捷”性,开发人员应及时从用户处获得各种反馈信息,因

此需要用户与软件开发人员一起工作,以便在需要的时候及时给予反馈。

(5)由积极主动的人来承担项目开发,给他们提供所需环境和支持,信任他们的能力。

在影响软件项目的诸多因素中,人是其中最为重要的因素。因此参与软件项目的人应积极

主动,并要为它们参与软件开发创造良好的环境和条件。

(6)团队内部最有效的信息传递方式是面对面的交谈。敏捷软件开发主张软件开发团

队人员之间采用面对面交谈的方式来进行沟通,文档不作为人员之间交流的默认方式,只

有在万不得已的情况下,才去编写文档。

(7)将可运行的软件作为衡量软件开发进度的首要衡量标准。所谓可运行的软件是指

完成了用户的部分或全部需求,并经过测试,可在目标环境下

运行的软件系统。不同于其它的软件开发方法,敏捷软件开发不是根据所处的软件开发阶

段、已编写的文档数目或者己完成的代码数量来衡量软件开发进度,而是基于可运行的软件

系统实现了多少软件需求来衡量软件开发进度。

(8)可持续性的开发,出资方、开发方和用户方应当保持长期、恒定的开发速度。对于许

多软件项目而言,软件开发是一个长期的过程。敏捷软件开发主张软件开发团队

根据自身的特点选择合适、恒定的软件开发速度。不应盲目追求高速,软件开发速度过快可

能使软件开发人员陷入疲惫状态,可能会出现一些短期行为,以致于给软件项目留下隐患。

(9)关注优秀的技能和良好的设计会增强敏捷性。敏捷的一个重要体现是响应变化的

能力。良好的设计是提高软件系统应变能力的关键。

因此,软件开发人员必须从一开始就努力做好设计,并在整个项目开发期间不断审查和改进

设计。所有的软件开发人员都应致力于编写高质量的代码,不要为了追求短期目标而降低工

作质量,将改进的工作留到以后再做。

(10)简单化。这里所说的简单化是指软件开发工作应着眼于当前欲解决的问题,不要

把问题想的太

复杂(如去预测将来可能出现的问题),并采用最为简单的方法去解决它,不要试图去构建那

些华而不实的系统。

(11)最好的架构、需求和设计出自于自组织的团队。敏捷团队应当是自组织的,以适

应需求的变化。软件开发任务不是从外部直接分配到团

队成员,而是交给软件开发团队,然后再由团队自行决定任务应当怎样完成。软件项目开

发不是划分成若干部分然后交给相应的成员全权负责,所有成员对于软件项目的所有部分

都有权参与。

(12)软件开发团队应定期就如何提高工作效率的问题进行反思,并进行相应的调整。

敏捷软件开发方法不是一成不变的,敏捷本身即含有适时调整的意思。随着项目的推

进,软件开发团队应不断地对其组织方式、规则、关系等方面进行反思,并对这些方面进行

调整,以便不断优化团队结构、提高软件开发效率。

1.2敏捷软件开发特点

敏捷思想对软件开发提出了新的理解和认识。它没有深奥的理论,也没有引入新的概念

和特有的技术,只是将经过数十年检验的一组软件工程准则有机地结合在一起,确保这些软

件工程准则相互支持并能够得到有效执行,从而促进当前软件工程所面临的问题的解决。

敏捷意味着轻盈、灵巧、无过多的负担、能够迅速响应变化。根据敏捷软件开发的指

导思想和实践原则,敏捷软件开发具有以下几个方面的特点。

-小

敏捷软件开发主张软件开发过程只需生成少量的软件文档,每个文档的规模要小;软件

开发应该迭代进行,每次迭代要实现的软件功能需求的数量和规模要小,从而确保每次迭代

的周期要小。

-简

敏捷软件开发建议软件开发过程中所采用的技术、所使用的工具以及每次迭代要解决

的问题要尽可能的简单;软件开发人员在每次迭代中只关注当前欲实现的功能需求,而不要

考虑将来的问题,从而使得软件开发人员能够聚焦关注点,简化问题的解决。

-快

为了快速响应变化、尽快从用户处获得反馈信息,敏捷软件开发要求软件开发人员尽

快地给用户提交有价值的软件产品,快速地对软件产品进行迭代和更新,以向用户持续地

交付不断完善的软件产品。这里所说的软件产品是指可运行的软件系统,而不是软件文档。

-变

敏捷软件开发允许用户需求的动态变化,主张要以变应变,尤其是开发团队应该是自

组织的,软件系统的设计应能够有效地支持用户需求的变化,在整个软件开发过程中项目

开发团队应不断检讨软件开发方法、技术、管理和工具等方面的不足和局限,以便对它们进

行不断的改进和优化。

-体

按照敏捷软件开发思想,软件开发人员和用户应融为一体,形成一个团队;敏捷软

件开发非常强调构成团队的各个成员的素质,包括能力、技能、工作的积极性和主动性;此

外敏捷软件开发还鼓励个体之间的交流,并强调这种交流是以交谈为主,而不是以文档为媒

介。

从总体上看,敏捷软件开发方法与其它一些重型的软件开发方法有以下三个方面的本质

差别。首先,敏捷软件开发强调方法本身的适应性,针对变化不断进行优化和调整,主动适

应变化;而重型软件开发方法以预测性和计划性为主,倾向于预先制定详细的计戈U,通过该

计划来指导软件项目的实施,并期望软件开发过程与计划之间的偏差越少越好。其次,敏捷

软件开发强调以人为本,认为软件开发是面向人的而不是面向过程的,要求让软件开发所需

的各种方法、技术、工具和过程等适应人,而不是让人去适应它们;而重型软件开发方法试

图定义一种广泛适用的软件开发过程并通过团队来执行该软件开发过程,从而来指导软件系

统的开发。第三,敏捷软件开发重点关注和强调可运行的软件系统,弱化了文档在软件开发

中的作用;而重型软件开发方法则非常重视软件文档的撰写和管理。

敏捷软件开发的上述特点使得它更加适合于小规模软件开发团队,因为过多的软件开

发人员势必会使得软件开发人员之间的交流变得非常复杂;同时也使它更加适合于需求易变

的软件系统的开发,从而充分发挥该方法的技术优势。

1.3支持敏捷软件开发的技术和管理手段

敏捷软件开发的基本思想和实践原则为软件系统的开发提供了一组高层的策略,它们明

确了实现敏捷软件开发的目标和要求,因而需要相应技术和管理手段的支持。

从技术的角度来看,敏捷思想和原则对软件系统的开发提出了以下一组要求:尽快开

发出可运行的软件系统:当用户需求改变时应迅速地响应变化;获得良好的软件设计,以便

当需求变化时对软件设计进行不断的调整和优化;保证软件系统的质量;提高敏捷软件开发

的效率等等。现阶段软件工程领域有以下一组技术可以有效地满足上述要求,支持敏捷软件

开发。

-测试驱动开发

测试驱动开发要求软件开发人员在编写程序代码之前,先确定和编写好测试。或者

说,软件开发人员首先要思考如何对某个功能进行测试,设计好相应的测试用例,编写好

相关的测试代码,然后编写相应的程序代码以通过软件测试。这一技术支持软件系统功能的

逐步实现,有助于保证任何程序代码都是可测试的,从而确保软件系统的质量。本章7.2节

将详细介绍测试驱动开发技术。

-敏捷设计

敏捷软件开发对软件系统的设计提出了更高的要求。为了支持用户需求的动态变化以及

由此而引发的对软件设计的持续调整和优化,软件系统的设计应易于改动和调整,具有稳固

性、可理解性、简单性、干净性和简洁性等特点。针对这一要求,RobertC.Martin提出一

组支持敏捷软件开发的设计原则[3],包括:(1)单一职责原则,要求每个模块只承担一个

职责,减少引起模块变化的因素,提高模块的内聚度;(2)开放封闭原则,扩展时无需更

改模块的源代码和可执行代码,要尽可能利用抽象类,以体现软件设计的灵活性和可重用

性;(3)依赖倒置原则,抽象不应该依赖细节,细节应依赖于抽象;(4)接口隔离原则,

接口中的方法都是有意义的,否则就要相互分离等等。

-模式运用

充分利用各种模式,包括体系结构模式和设计模式来进行软件系统的设计,以支持软件

系统的可重用性和应对用户需求的变化。

-快速原型技术快速原型技术有助于迅速生成软件系统的原型,并以此为媒介支持软

件开发人员和用户之间的交流和沟通,促使软件开发人员关注于用户的需求,适应用户需求

的动态变化,帮助软件开发人员尽快从用户处及时获得反馈信息。

-MDAMDA

强调将软件系统的功能规约与实现这些功能的技术和平台相分离,它区分两类不同的

软件系统模型:平台无关的模型和平台相关的模型,并通过模型映射在不同模型之间建立

桥梁,从而有助于保护用户的业务模型,促进软件系统的快速开发和部署。

-CASE工具

目前已有许多支持敏捷软件开发的软件工具,包括由Microtool公司研发的Actif

Exetreme,它支持敏捷过程管理;由Ideogramic公司开发的IdeogramicUML,它支持针对

敏捷过程的UML建模;由Borland公司开发的TogetherToolSet,它支持敏捷开发和极限编

程中的诸多活动等等。

从管理的角度来看,敏捷思想和原则对软件系统的开发提出了以下一组要求:管理好用

户的需求;确保软件过程支持持续性的交付软件系统;管理好软件开发团队;支持软件开发

人员和用户之间的交流、合作以及问题的及时反馈;以人为本,充分发挥人的积极性和主

动性;保证软件开发速度的稳定性和持续性;不断改进和优化软件开发团队等等。为了应对

这些要求,基于敏捷软件开发方法的软件项目应遵循以下的管理方法。

-软件过程模型的选择

基于敏捷软件开发方法的软件项目组应选择那些支持渐进、迭代开发的软件过程模型,

如迭代模型、螺旋模型、Rup和快速原型等。

-团队建设

基于敏捷软件开发方法的软件项目开发团队应充分发挥人的主体作用,将用户作为软件

开发团队中的成员,并与软件开发人员一起工作和交流;为软件开发团队提供良好的交流环

境,如拥有共同的办公区间和时间,基于网络的虚拟环境;支持团队成员,尤其是开发人员

和用户之间的双向交流和沟通。

-需求管理

尽管用户需求在整个软件开发过程中是动态变化的,但是每次迭代欲实现的用户需求

应该是稳定的,所生成的需求文档应处于受控状态,与项目计划、产品和活动相一致,并作

为开展软件开发工作的基础。软件开发人员通过和用户的充分和持续性交流,支持需求确认

和评审。

-软件项目计划

软件开发人员和用户一起参与计划的制定,包括估算规模和进度、确定人员分工;项目

计划的制定者应参照用户需求来制定软件项目计戈I,包括系统应当满足哪些需求、应当首先

满足哪些需求、每次发布的版本应完成多少功能才会对用户的业务有所改善等等。软件项目

计划不应过细,应保留一定的灵活性。同时每次迭代要量力而行,确保要实现的系统功能不

要太多。多个迭代欲实现的系统功能和迭代周期要大致相当,防止软件开发周期的剧烈变

化,支持稳定和可持续的软件开发。此外,每次迭代的软件开发周期要适中,不宜过长否则

用户会失去耐心,无法及时得到反馈;也不宜过短,否则用户难以消化,同样影响反馈。

-跟踪监督

在对敏捷软件开发项目的跟踪和监督过程中,软件项目管理人员要特别关注以下的软件

风险:(1)对规模和工作量的估算过于乐观,该软件风险将影响项目的周期性迭代;(2)软

件开发人员和用户之间的沟通不善,该软件风险将可能导致软件需求得不到用户的认可和

确认;(3)需求定义不清晰和不明确,该软件风险将可能导致需求不清,所开发的软件系统

和用户要求不一致;(4)项目组成员不能有效地在一起工作,该软件风险将可能导致软件开

发效率和软件项目组敏捷度的下降;(5)任务的分配和人员的技能不匹配,该软件风险将导

致软件开发不能做到以人为本;(6)软件设计低劣,该软件风险将可能导致所开发的软件系

统无法适应用户需求的不断变化和调整等等。

1.4极限编程

极限编程是由KentBeck提出的一种特殊的敏捷软件开发方法⑶⑷[5],它提出了更加具

体和实际的指导方法以支持软件系统的敏捷开发。极限编程将其核心思想归结为四条:(1)

交流,极限编程强调交流对于软件系统开发的重要性,但是它侧重于基于口头(而不是文档、

报表和计划)的交流;(2)反馈,极限编程主张通过持续、明确的反馈来获得软件的状态,

它对于软件项目的成功实施是至关重要的;(3)简单,极限编程主张用最简单的技术来解决

当前的问题;(4)勇气,极限编程强调快速开发并在必要时具有重新进行开发的信心。在此

基础上,极限编程定义了五条指导性原则和十二条必须遵循的核心准则。按照极限编程创

始人KentBeck的观点,极限编程并没有引入任何新的概念,它的创新之处在于:将经过

数十年检验的准则结合在一起,确保这些准则相互支持并能够得到有效执行。指导原

则极限编程的四条价值观构成了整个方法学的基础,在此基础上极限编程引出了五条原则

作为行为与实践的指南。1.快速反馈极限编程要求软件开发人员从用户处迅速得到有关

软件系统的反馈情况,比如软件开发

人员通过小步迭代迅速了解用户的反应,以确认当前所做的开发工作是否满足用户的需求,

通过经常性的自动化测试和集成迅速了解软件系统的运行状况。2.简单性假设极限编程

要求软件开发人员只考虑当前迭代所面临的问题,无需考虑将来(如下一次迭代)所面临

的问题,并且用简单的方法和技术来解决问题。3.逐步更改极限编程要求通过一系列细

微的修改来逐步解决问题和完善系统,不要期望一次迭代就开发出一个完整的软件系统。4.

支持变化极限编程要求在软件开发过程欢迎用户改变需求,支持用户需求的动态变化。5.

高质量的工作

极限编程要求采用诸如测试驱动开发等技术高质量地开展工作,确保所开发软件系统的质

量。核心准则极限编程总结出了十二项核心准则以指导软件系统的开发。这些实践在

日常的软件工程

化开发大多为人们所采用,然而单独采用某些准则却有可能会导致混乱,极限编程的独特之

处在于将这些核心准则有机结合在起来以达到最佳效用。(1)计划游戏(PlanningGame)

计划游戏旨在帮助软件开发团队快速制定下一次迭代的软件开发计划J。参与计划游戏

的人员包括软件开发人员和业务人员。业务人员在计划游戏中的职责包括:确定范围即系

统应当满足哪些需求、规定需求的优先级即应当首先满足哪些需求、划分版本即每一次发布

的版本应当完成那些功能才会对用户的业务有所改善、规定发布日期等等。业务人员的计划

决策需要得到软件开发人员的反馈和支持。软件开发人员在计划游戏中的职责包括:估算

实现每项功能所需的时间、解释业务人员的决策在技术上的影响如数据库的选择对软件的影

响、制定日程安排、分配工作等等。(2)隐喻(Metaphor)隐喻是指使用一组与业务相

关的术语来描述用户需求,促使软件开发人员和业务人员

对系统达成共同和一致的理解。由于软件开发人员、业务人员及用户之间使用业务术语(而

不是技术术语)进行交流,因此该准则有助于加强他们之间的沟通和合作,及时从用户处获

得反馈并支持用户更好地参与到软件项目之中。采用隐喻对软件开发人员而言也存在挑

战,即如何将用业务相关的术语所描述的用户需求转换成为软件所应俱备的功能。隐喻的选

择应该仔细、恰当,不好的隐喻不仅无益于软件系统的开发,而且还会带来负面影响。(3)

小型发布经常性地给用户发布能给他带来业务价值的可运行软件系统,每次发布的软件系

统仅提供少量的功能。小型发布不仅有助于缩短软件开发周期,提高软件开发小组对软件开

发进度的估算能力和精度,而且由于每个小型发布包含了对用户最有价值的核心功能,因而

有助于从用户处获得对软件系统使用情况的真实反馈信息。(4)简单设计所谓简单是指

程序代码能够运行所有的测试、没有重复的逻辑、清晰地反映程序的意图、

包含尽可能少的类和方法。与大多数传统软件开发方法不同的是,极限编程要求只为当前

的需求做设计,而不必考虑将来可能的需求。这样做是基于以下几个方面的考虑。首先,对

未知的需求考虑过多势必会影响软件开发人员当前的工作,增加软件系统开发的复杂度。

其次,未来的需求是不确定的,因此过多考虑未来需求将可能导致所开发的软件系统包含用

户不需要的功能,增加了不必要的成本和开销。(5)测试极限编程要求测试应在编写代

码之前进行,而不是等到开发结束后再安排一个专门的

阶段对软件系统进行测试。在简单设计之后,程序员首先编写测试程序,当测试程序完成之

后再正式编写待开发软件系统的程序代码。测试程序是对代码进行重构的基础,通过运行

测试程序,可以检查重构是否引入了新的错误。在软件测试过程中,每发现一个错误,就

增加一项新的测试,因而测试程序是不断增长的。实践表明,采用极限编程的这种测试方法

能使软件系统的质量不断得到提高。(6)重构重构是指在不改变程序代码功能的前提下,

改进程序代码的设计,使程序代码更加简

单,更易于扩展。极限编程通过重构使软件系统具有灵活的结构,易于接受变化。重构是一

个持续的简化过程,适用于代码的设计和测试,甚至对极限编程本身也可进行重构。(7)

结对编程结对编程是指两名程序员同时在一台计算机上共同开展编程工作。极限编程要求

所有

程序代码都通过结对编程来完成。这种编程方式有以下几个方面的优点。首先,软件开发过

程中的每一项决定都至少由两个人来共同完成,对系统的每一部分至少有两个人熟悉,这

可以降低人员流动带来的软件风险。其次,在进行结对编程过程中,操纵键盘的人员着眼于

实现细节,而另一人则可以从全局的角度进行考虑,因而可以有效地分离关注视点,有助于

对软件系统的开发进行全面的考虑。第三,结对编程有助于在编码的同时进行代码复审,有

助于提高程序代码的质量。第四,参与结对编程的程序员之间相互讨论,可以强化知识共享。

(8)代码集体拥有代码集体拥有是指开发小组的任何成员都可以查看并修改任何部分的代

码。代码集体

拥有与传统的做法正好相反,它不是将系统的模块指定给专门的人员,而是要求任何人都

有权修改任何模块代码。这样一方面程序员需要对整个软件系统负责,另一方面也促进了软

件开发团队对整个系统的了解。代码集体拥有与结对编程、编码标准等极限编程准则是相辅

相成的,如果没有这些准则的支持而单独采用代码集体拥有,将使软件项目陷入混乱。(9)

持续集成不要等到所有软件模块完成之后再进行软件系统的集成,而是应经常性地进行集

成。

软件开发小组可指定一台计算机作为集成机器,专门用于自动化构建程序,程序员每完成

一个模块,就将它加入到集成机器中。集成的周期应当尽可能短,可能是几个小时或者几天

(而不是几周或几个月)集成一次。(10)每周工作40小时极限编程倡导质量优先,不

主张为了追求开发速度而片面延长工作时间,即使程序员自愿,也不提倡加班。所谓每周

工作40小时并不是一个绝对标准,它是指一个合适的工作时间。极限编程认为尽管加班

可能增加产量,但却无法确保工作质量,软件开发应持续性地保持恒定的速度。(11)现

场用户在软件开发过程中,极限编程要求用户代表在现场办公,参与软件开发的全过程,

确保软件开发人员能够及时得到反馈信息。(12)编码标准编码标准是关于程序代码格

式的一组约定。在软件开发过程中,程序员遵循统一的编码标准,这有助于提高软件系统的

可理解性和可维护性。编码标准是支持其它极限编程准

则的重要保证。比如,代码集体拥有允许每个软件开发人员都可修改每个模块的程序代码,

如果没有统一的编码标准,这种修改必将导致混乱。

2测试驱动开发

近年来,许多敏捷软件开发方法都主张采用一种新的方式来开发软件,在该方式中程序

员首先依据待实现的功能来确定和编写测试,然后根据测试来编写程序代码,该软件开发方

式被称为测试驱动开发。测试驱动开发的支持者认为这种软件开发方式能够编写出更加简

单、更易于理解和维护的程序代码,有助于提高程序代码的质量,而且当它与敏捷软件开发

方法、极限编程和重构技术等相结合时,有助于获得简单和健壮的软件设计。

2.1测试驱动开发思想

测试是软件开发过程中的一项重要活动,是发现软件系统中的故障、确保软件系统质量

的一条重要途径。在传统的软件工程实践中,程序员首先编写程序代码,然后再对程序单元

进行测试,此时的测试通常称为单元测试。经过单元测试后的程序代码再交给相关的测试人

员对它进行集成测试、确认测试和系统测试。传统的软件测试方法往往会存在以下几个方

面的问题。-当程序员编写完程序代码之后,由于进度方面的压力,往往没有足够的时间

对程序代码进行详尽和充分的测试。如果测试不够充分,那么程序代码中就会遗留许多软

件故障。-如果测试人员是基于其它相关的文档(而不是程序代码)来设计测试用例和编

写测试代码,那么当这些文档与程序代码不一致时,对程序代码进行的测试就会存在诸多

问题,如设计的测试用例不正确、与程序代码不一致。-测试通常是在程序代码编写完成

之后才进行的,因而无法保证编写程序和软件测试同步进行。-对于许多程序员而言,

他们更愿意编写程序代码,而不愿测试程序。因为编写程序是一个创造和生产的过程,让

他们觉得有成就感;而测试通常被视为是一件乏味的工作。测试驱动的软件开发试图克服

传统软件测试(尤其是单元测试)存在的上述问题。所谓

测试驱动软件开发是指在编写程序代码之前,首先确定和编写好测试。也就是说,在明确要

开发某个软件功能后,程序员首先要思考如何对这个功能进行测试,设计好相应的测试用例

并编写好相关的测试代码,然后再编写与该软件功能相对应的程序代码,以运行测试程序

来对程序代码进行测试。如此循环反复,及至实现软件系统的全部功能。测试驱动开发的

精髓在于:将软件测试方案的设计工作提前到编写程序代码之前;从测

试的角度来验证、分析和指导设计;同时将测试方案当作程序编码的准绳,有效地利用它来

检验程序编码的每一个步骤,及时发现其中的问题,实现软件开发的“小步快走”。测试驱

8-11

动开发的基本思路就是通过测试来推动整个软件系统的开发。它不纯粹是一种测试技术,而

是代表了一种新的软件开发方式。与传统软件开发方式相比较,测试驱动开发具有以下特点。

-根据测试来编写代码测试驱动开发强调:要首先编写出用于测试某项功能是否符合要求

的测试项(包括测试

代码和测试用例等),然后再去编写相应的程序代码来实现这一功能。因此,它体现了一种由

测试来驱动软件开发的思想。-程序员设计并维护一组测试,编写测试的目的不仅仅是为

了测试程序代码能否正常工作,而且被用于定义程序代码的内涵。在传统软件开发方式中,

程序员要编写和设计一组测试,其目的是要发现所编写的程序

代码是否存在软件故障。在测试驱动开发方式中,程序员也要设计并维护一组测试。但是,

程序员编写该测试的目的是要将它作为待开发程序代码的行为规约,利用它来引导程序代码

的编写,并最终来检验所编写的程序代码是否遵循了该测试集所定义的行为规约。例如,

假设要编写一个列表类List。传统的做法是先编写完列表类的所有程序代码(包括其所有

的属性和方法),然后设计测试用例和编写测试代码对它进行测试。在测试驱动开发中,其

过程正好相反。程序员首先要确定和设计一个测试,如空列表的长度应该为0,并编写以

下的测试代码。PublicvoidtestEmptyList(){ListemptyList=newList();assertEquals(llThe

sizeofemptylistshouldbe0”,0,emptyList.size());}程序员然后将测试作为列表类的一种行

为规约来指导列表类程序代码的编写。根据上述测试用例和测试代码的描述,程序员首先

要实现和编写List类的方法size()。对于任何空列表而言,该方法的返回值均为0。-确

保任何程序代码都是可测试的由于在测试驱动开发中,程序员首先考虑的是如何测试软件

系统的功能(即确定和编写

测试),然后再考虑如何实现系统的功能(即编写程序代码)。因此,测试驱动开发可以确保

所有的程序代码都是根据程序员所设计的测试集来编写的,所编写的任何程序代码都是可测

试的。这有助于有效地发现程序代码中的故障、提高软件系统的质量。从本质上看,测试

驱动开发的目标是让所编写的程序代码奏效(Work)和洁净(Clean),即所谓的“Clean

CodethatWorks所谓的奏效是指所编写的程序代码实现了软件系统的功能并通过了相应

的测试。所谓的洁净是指软件系统的所有程序代码均是按照测试驱动的方式来开发的,没

有无关的程序代码。一般的,测试驱动开发应遵循以下一组原则。(1)测试隔离,不同代

码的测试应该相互隔离。对某一代码的测试只考虑此代码本身,不要考虑其它的代码细节。

(2)任务聚焦,在测试驱动开发过程中,程序员往往需要实施多种不同形式的工作并进行

多次的迭代,比如设

计测试用例、编写测试代码、编写程序代码、对代码进行重构、运行测试等等。在此情况下,

程序员应将注意力集中在当前工作(即当前欲完成的软件功能),而不要考虑其它方面的内

容,无谓地增加工作的复杂度。(3)循序渐进,一个软件模块的功能很多,程序员应该针对

软件模块的功能,设计相应的测试,并形成测试列表。然后根据测试列表不断地完成相应的

测试用例、测试代码和功能代码,逐步完成整个软件模块的功能。这种循序渐进的做法可以

防止疏漏,避免干扰其它工作。(4)测试驱动,要实现某个功能、编写某个类,程序员首先

应编写相应的测试代码和设计相应的测试用例,然后在此基础上编写程序代码。(5)先写断

言,在编写测试代码时,程序员应首先编写对功能代码进行判断的断言语句,然后再编写相

应的辅助语句。(6)及时重构,程序员在编码和测试过程中应对那些结构不合理、重复的程

序代码进行重构,以获得更好的软件结构,消除冗余代码。与传统的软件编码和测试方式

相比较,测试驱动开发具有以下的一组优点。(1)编码完成后即完工,在程序代码编写完

成并通过测试之后,意味着编码任务的完成。而在传统的方式中,由于编码完成之后需要

进行单元测试,因而很难知道什么时候编码任务结束。(2)易于维护,软件系统与详尽的

测试集一起发布,有助于将来对程序进行修改和扩展,并且在开发过程中及时对程序代码

进行重构,提高了软件系统的可维护性。(3)质量保证,由于任何程序代码都经过了测试,

因而有助于有效发现程序代码中的错误,提高软件系统的质量。

2.2支持测试驱动开发的软件工具

至今人们已经开发了许多可支持测试驱动开发的软件工具,包括cppUnit、csUnit、CUnit、

DUnit、DBUnit、JUnit、NDbUnit、OUnit、PHPUnit、PyUnit、NUnit、VBUnit等等。本节

介绍用Java进行测试驱动开发所使用的标准工具JUnit,下节将详细分析如何利用JUnit

来支持测试驱动开发。JUnit是一个由ErichGamma和KentBeck二人共同开发的开源

Java单元测试框架。JUnit框架提供了一组类来支持单元测试。通过继承重用这些类,程

序员可以方便的编写测试程序代码,运行测试程序以发现程序代码中的故障。JUnit的主

要类结构如图1所示。-Test这是一个接口,所有测试类(包括TestCase和TestSuite)

必须实现该接口。Test提供了两个方法:countTestCases方法用于计算一个测试将要运行

的测试用例的数目;run方法用于运行一个测试并收集它的测试结果。-Assert该类定义

了软件测试时要用到的各种方法。例如assertEquals方法用于判断程序代码的运行结果是

否等同于预期结果;assertNull和assertNotNull方法用于判断对象是否为空等等。-

TestCaseTestCase类实现了Test接口并继承了Assert类,它是程序员在编写测试程序时

必须扩展的类。通过继承,程序员可以方便的利用该类提供的方法对程序单元进行测试。-

TestSuiteTestSuite类实现了Test接口并提供了诸多方法来支持测试,当程序员试图将多个

测试

8-13

集中在一起进行测试时必须扩展该类。junit.framework.Assert

+assertEquals:void+assertNotNull:void+assertNull:void+assertSame:void+assertNotSame:

void+fail:void+failNotEquals:void+failNotSame:void+failSame:void

junit.framework.TestSuite

+addTest:void+addTestSuite:void+countTestCases:int+run:void+runTest:void

junit.framework.Test

+countTestCase:int+run:void

junit.framework.TestCase

+countTestCase:int+run:void

图I.Junit的主要类结构

目前许多软件开发工具和环境(如Eclipse)集成了JUnit以支持软件测试。下面通过一个

简单的例子,说明如何通过重用JUnit提供的上述类以及JUnit的图形化界面来进行单元

测试以发现程序代码中的错误。在该例子中,程序员要编写一个简单的计算器,它具有加法

和减法的两个功能。该计算器的Java程序代码如图2所示。

publicclassCalculator{publicintadd(intaugend,intaddend){return

augend+addend;}

publicintsubtraction(intminuend,intsubtrahend){returnminuend-

subtrahend;}

)

图2.实现计算器的程序代码类Calculator.java

将上述程序代码保存在Calculator.java的文件中,运行javacCalculator.java将该源程序

代码编译为Java中间代码Calculator.classo根据上述代码实现的功能,下面利用JUnit提

供的测试类TestCase编写测试程序并设计相应的测试用例。

importjunit.framework.TestCase;publicclassTestSampleextendsTestCase{publicvoid

testAdd(){Calculatorcal=newCalculator();intresult=cal.add(15,20);

assertEquals(35,result);}publicvoidtestSubtration(){Calculatorcal=newCalculator();

intresult=cal.subtration(20,15);assertEquals(5,result);})

图3.对Calculator.java进行测试的程序代码类TestSample

上述测试程序的第一行代码表示要importJUnit提供的Java类TestCase0第2行程序定

义了一个测试类TestSample来对Calculator类进行测试,该类继承了TestCase类。类

TestSample有两个方法testAdd和testSubstration,分别用于测试计算器的加法和减法功

能。在这两个方法中均有一个语句assertEquals,用于判断该语句中的两个参数是否相等的。

如果相等则意味着通过对该功能的测试,否则测试不通过。运行javacTestSample.java以

将该测试程序编译为Java中间码TestSample.class,然后输入java

junit.swingui.TestRunnerTestSample运行Junit以测试TestSample类。JUnit将弹出一个

如图4所示的窗口。该窗口下部的编辑框显示了测试的结果以及经测试后发现的错误

和失败信息,上部的测试状态框用不同的颜色来表示测试是否通过。如果状态框的颜色是绿

色,说明测试通过,没有错误产生;如果是红色的,则说明测试失败,程序代码中存在故障。

8-15

图4.JUnit的图形化界面

需要注意的是,按照JUnit的规定所有测试类必须继承junit.framework.TestCase类;测试

类中的每个测试方法必须以test文字开头,且是publicvoid而且不能有参数;在测试方法

中使用assertEquals等TestCase类所提供的断言方法来判断待测试程序的运行结果是否

与预期的结果相一致。如果想成批地运行测试用例,程序员必须利用JUnit所提供的

addTestSuite方法。TestSuite可以把一组测试集中在一起,并作为一个整体来运行。在下面

的程序代码中,程序员首先创建了一个TestSuite对象,然后利用TestSuite提供的方法

addTestSuite将测试类TestSample.class加入到测试集中。

8-16

importjunit.framework.Test;importjunit.framework.TestSuite;publicclassTestAll{publicstatic

Testsuite(){TestSuitesuite=newTestSuite("TestSuiteTest");

suite.addTestSuite(TestSample.class);returnsuite;)}

图5.成批测试的代码实例

从总体上看,JUnit具有以下特点:(1)提供了一组API,支持程序员编写可重用的测试

代码;(2)提供了多种方式(文本或者图形界面)来显示测试结果;(3)提供了单元测试用

例成批运行的功能;(4)超轻量级而且使用简单;(5)整个框架设计良好,易于扩展。

2.3测试驱动开发过程

测试驱动开发的思想非常朴素和简单,就是根据要实现的功能编写测试,然后根据测试来

编写程序代码,最后运行程序代码以通过测试。整个测试驱动开发的过程如图6所示。

选择待开发的功能

编写测试程序

编译测试程序完善和修改代码

运行测试程序

重构代码运行测试

没通过

通过

修改代码没通过

通过

通过

修改和优化代码没通过

图6.测试驱动开发过程

本节通过一个简单的案例分析来介绍测试驱动开发的过程。该案例将开发一个机票查询

的功能模块,它能够帮助用户查询航班信息并将查询的结果放置在一个航班列表中。因此,

航班列表大致具有以下的功能:存放所查询的航班信息;可以从航班列表中取出一个或者多

个航班信息;计算航班信息列表的长度等等。步骤1.选择待开发的功能测试驱动开发是

一个迭代的过程.每一次迭代实现一个相对单一和独立的功能。因此,

在每次迭代开始之时,程序员首先要选择本次迭代欲实现的功能,并根据该功能设计相应的

测试用例。对系统功能的选择应遵循先简后繁的原则。针对航班信息列表的案例,程序员可

以考虑先实现空列表,并根据这一功能设计测试用例,即当一个航班列表刚被创建时,它应

该是一个空列表,列表中元素的个数应该为0。步骤2.编写测试程序根据所选择的功能

以及针对该功能所设计的测试用例,编写相应的测试程序代码。例如,为了对空列表的长

度进行测试,程序员编写了如图7所示的测试代码。它定义了一个测试类testAirlineList

用于对航空列表模块单元进行测试。testAirlineList继承了JUnit的TestCase类,包含了一

个方法testEmptyListSize()用于测试空的航空信息列表的长度是否为0。

importjunit.framework.TestCase;publicclasstestAirlineList{publicvoid

testEmptyListSize(){AirlineListemptyList=newAirlineList();

emptyList.assertEquals(O,emptyList.size());}}

图7.测试空列表的程序代码

步骤3.编译测试对测试程序进行编译,此时发现编译无法通过,编译器提示“AirlineList

cannotberesolvedtoatype"。原因非常简单,程序员还没有编写AirlineList这个类及其size()

方法。为此,程序员需要修改和完善程序代码,以使得测试程序能够顺利的通过编译。此

时的工作实际上就是编写待开发功能的程序代码。程序员增加的程序代码如图8所示。

该代码对应于一个类AirlineList及其两个方法:AirlineList。和size。。需要注意的是,程

序员仅仅增加了为了满足本次测试所需的代码,而没有完整地实现整个AirlineList类。这

正体现了测试驱动开发的思想,即根据测试来编写程序。再次编译上述代码,此时编译能

够正常通过。

8-18

publicclassAirlineList{privateintnSize;publicvoidAirlineList(){nSize=0;}

publicintsize(){nSize=nSize+1;returnnSize;}}

图8.航空信息列表类的程序代码

步骤4:运行程序代码运行上述程序代码,此时JUnit将弹出如图9所示的窗口。该窗

口上部测试状态栏的颜色为红色,表明程序代码未通过测试。进一步观察FailureTrace子

窗口,可以注意到它显示以下信息“junit.framework.AssertionFailedError:expected:<0>but

was:<l>",这预示着上述程序代码存在故障。通过进一步的调试可以发现,原来类

AirlineList的size。方法中出现了一行错误的代码"nSize=nSize+1",将该代码行从

AirlineList类中删除,重新编译和运行测试,此时JUnit将弹出测试状态栏颜色为绿色的

JUnit窗口,表明程序代码通过测试。

8-19

图9.未通过测试的JUnit窗口

步骤5:重构代码并运行测试查看程序代码以确认是否有重复的代码需要清理,是否需要

任何形式的重构以优化代码。在该例子中,上述两种情况均不存在。步骤6:返回到步骤

1,进行新一轮的测试驱动开发第一轮的测试驱动开发仅仅完成了AirlineList类最基本的

功能。在第二次的测试驱动开发过程中,程序员选择的功能是向空航班信息列表中增加一

个航班信息,使得航班列表的长度为1,并根据这一功能设计测试用例,然后编写如图10

所示的测试程序。新的测试程序在原有测试程序的基础上增加了一个新的方法

testSizeAfterAddingOne(),它用于测试当向一个空航空信息列表增加一个航空信息后,列表

的长度是否为1。为了使得该程序代码能够通过编译,程序员需要扩展AirlineList类,增

加一个新的方法addOne()(如图11所示)。将上述程序代码进行编译,然后在JUnit中

运行,此时JUnit将弹出测试状态栏颜色为绿色的窗口,表明程序代码通过测试。显然通

过第一轮的开发,软件系统的功能又得到了进一步的丰富和完善。

importjunit.framework.TestCase;publicclasstestAirlineListextendsTestCase{publicvoid

testEmptyListSize(){AirlineListemptyList=newAirlineList();

assertEquals(O,emptyList.size());}publicvoidtestSizeAfterAddingOne(){AirlineList

airlineList=newAirlineList();airlineList.addOne("Airline5362");intntemp

=airlineList.size();assertEquals(1,airlineList.size());}}图10.第二次迭代的测试程序

8-20

publicclassAirlineList{privateintnSize;publicvoidAirlineList(){nSize=0;}

publicvoidaddOne(StringstrLineDesc){nSize=nSize+1;}publicintsize(){return

nSize;}}

图IL第二次迭代的功能程序代码

3面向方面软件开发

传统的软件开发技术倾向于按照模块化和功能分解的方式对软件系统进行分割。这种分

割实际上是在纵向层面对软件系统的业务功能进行分析、设计和组织。例如,按照结构化软

件开发方法的思想,一个软件系统被分解为一组过程和函数,不同的过程和函数间通过调用

相互作用。在面向对象软件开发方法中,一个软件系统由一组对象类组成,每个对象类封装

了属性和方法,不同对象类之间存在诸如继承等关系,并通过消息传递进行交互作用。然

而在大量的软件工程实践中,人们发现软件系统中的许多业务模块经常需要封装一些

相同的行为用于辅助系统业务功能的实现,如日志、事务处理、并发控制、对上下文敏感的

错误处理、性能优化等等。借助于传统的软件开发技术,软件开发人员通常将这些行为与业

务逻辑封装在一起,形成相应的业务功能模块。近年来人们提出了一种面向方面的软件开发

方法。该方法主张将业务功能与辅助业务功能实现的其它行为相分离,强调应该从横切和纵

向两个不同的关注点来支持软件系统的开发。

3.1面向方面软件开发的基本思想

早在二十世纪九十年代初期,人们就注意到了面向对象软件开发方法的局限性。尽管这

一关键技术引入了诸如对象、类、消息传递、继承等概念和机制来自然模拟现实世界中的应

用系统,建立层次性的对象类结构,但是当需要为一组对象引入公共行为(如日志、安全、

异常处理等)时,面向对象程序设计则难以有效地处理这类问题。为了说明问题,考虑一

个电子商务系统例子。该系统具有订单管理和商品管理等功能,

并且这些功能都需要与相应的权限管理相结合,确保只有获得授权的用户方可操作这些功

能。借助于面向对象的程序设计技术,实现该系统的伪代码如图12、图13和图14所示。

8-21

publicclassOrderManager{privateAnayListm_Orders;publicOrderManager(){m_Orders=

newArrayList();}publicvoidAddOrder(Orderorder){Authorizationpermission=new

Authorization();if

(permission.Verify(Authorization.ADMIN)){m_Orders.Add(order);}}

publicvoidRemoveOrder(Orderorder){Authorizationpermission=newAuthorization();if

(permission.Verify(Authorization.ADMIN){m_Orders.Remove(order);}

})

图12.OrderManage模块的伪代码

publicclassProductManager{privateArrayListm_Products;public

ProductManager(){m_Products=newArrayList();}publicvoidAddProduct(Productproduct)

{Authorizationpermission=newAuthorization();if

(permission.Verify(Authorization.ADMIN)){m_Products.Add(product);}}

publicvoidRemoveProduct(Productproduct){Authorizationpermission=new

Authorization();if

(permissions.Verify(Authorization.ADMIN)){m_Products.Remove(product);}}}

图13.ProductManager模块的伪代码

8-22

publicclassAuthorization{publicAuthorization(){};........;publicboolVerify(String

authType){.......〃校验用户权限}}

图14.权限管理模块的伪代码

用户的权限管理被封装为一个对象类Authori

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论