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

下载本文档

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

文档简介

.word编辑文档软件工程前沿开发技术经过近四十年的发展,软件工程在支持软件系统工程化开发方面取得了令人瞩目的成绩,提出了大量的理论、方法、技术和工具,但是近年来的研究和实践表明软件危机依然存在,软件开发仍然存在成本高、质量得不到保证、进度和成本难以控制等方面的问题,许多软件项目被迫延期甚至取消。与此同时,随着网络技术(尤其是Internet技术)的不断发展,部署在网络基础上的软件系统的规模和复杂性越来越高,并表现出诸如持续性、自适应性、交互性、动态性、开放性、异构性等特点。因此,如何支持这类复杂系统的开发、缓解和消除现阶段的软件危机是当前软件工程面临的一项重要挑战。为了迎接上述挑战,近年来软件工程领域的一些学者提出了许多新的方法和技术,包括:敏捷软件开发(AgileSoftwareDevelopment)、极限编程(ExtremeProgramming,XP)、测试驱动的开发(Test-DrivenDevelopment,TDD)、面向Agent的软件开发(Agent-OrientedDevelopment)、面向方面的编程(Aspect-OrientedProgramming,AOP)、模型驱动体系结构(Model-DrivenArchitecture,MDA)等等。与传统的软件工程方法相比较,这些方法和技术为软件工程实践提供了新的思路,已在许多软件工程实践中取得了积极的效果。1敏捷软件开发软件工程一直以来都面临着一个共同的问题,即如何迅速、高效地开发软件系统,适应用户需求的快速变化,确保软件系统的质量,控制软件开发成本。传统软件开发方法强调软件开发需遵循严格的过程模型以及以此为基础的开发计划,并且在软件开发过程中需产生大量的规范化文档,这一思想和方法很难应对快速、灵活和低成本软件开发所带来的一系列问题。自二十世纪九十年代以来,软件工程领域涌现出了一批新的软件开发方法。这些方法主张软件开发只编写少量文档、以用户为中心、主动适应需求变化。这些方法被称为敏捷软件开发,其代表性的成果是极限编程。1.1敏捷思想至今人们已提出了几十种软件开发方法,根据这些方法在对软件开发所提出的要求和约束等方面的差异,现有的软件开发方法大致可分为两类:重型软件开发方法和轻型软件开发方法。重型软件开发方法一般具有严格和详尽的软件开发过程,软件开发需产生大量的文档。轻型软件开发方法则强调软件开发过程的简洁性和灵活性,软件开发只需编写少量的文档。敏捷软件开发是一类轻型的软件开发方法,它提供了一组思想和策略来指导软件系统的快速开发并响应用户需求的变化。不同于已有的其它软件开发方法,该方法对软件开发具有以下四个方面的基本认识:(1)较之于过程和工具,应更加重视人和交互的价值;(2)较之于面面俱到的文档,应更加重视可运行软件的价值;(3)较之于合同谈判,应更加重视客户合作的价值;(4)较之于遵循计划,应更加重视响应用户需求变化的价值[1][2]。敏捷软件开发方法认为人是软件开发中最为重要的因素,软件开发应坚持以人为本;优秀的软件开发团队离不开人员之间良好的沟通与合作,相比较而言团队的合作与沟通能力比单纯的编程能力更为重要,改善人员之间的交流与合作将有助于提升团队的软件开发水平;应根据软件开发团队的特点选择合适的软件开发过程;在软件开发工具的选择方面,敏捷软件开发主张从使用小工具开始,只有当小工具不能满足要求时才考虑选择和使用功能强大的工具。一直以来,人们将文档视为是对软件开发各个阶段成果进行记录、促进人员之间进行交流的重要媒介和工具,也是软件开发和维护的主要依据。然而,编制过多的文档不仅会耗费大量时间和精力,而且当用户需求变化时难以实现文档与代码的同步,这势必会影响软件系统的开发和维护。敏捷软件开发方法提倡在软件开发过程中只编写少量短小精炼的文档。成功的软件开发不应单纯依赖于合同条款和工作说明,而应将用户和软件开发团队紧密地结合在一起,让用户积极参与软件开发并提供持续不断、频繁的反馈信息。在软件开发过程中,用户需求总会发生变化,这是由于用户需求难以一次性完全捕获,开发人员和用户对于需求的认识会不断地调整。此外,用户的业务本身也可能会动态地发生变化。在复杂软件系统的开发过程中,响应用户需求变化的能力常常决定着软件项目的成败。为了适应用户需求的变化,敏捷软件开发认为软件开发计划不应考虑的太远,不要进行过于周密、详细的计划,只应覆盖短期的工作任务,对于中长期的任务只需有一个粗略的规划即可,要保留计划的充分灵活性,并根据需求的变化适时地调整计划。在上述思想的指导下,敏捷软件开发提出了以下十二条原则来指导软件系统的开发[1]。(1)尽早和持续地交付有价值的软件,以使用户满意。敏捷软件开发最关心的是软件系统的交付。诸多软件工程实践表明,初期交付软件系统中包含的功能越少,最终交付软件系统的质量就越高;软件产品交付的越频繁,最终软件产品的质量就越高。尽早的交付可以让软件开发团队尽快获得成就感,提升软件开发团队的激情和效率,尽早从用户处获取对需求、过程、产品等反馈信息。持续性的交付可以让软件开发团队保持胜利感和成就感,持续获取用户的反馈信息,及时调整项目实施的方向和优先级。该原则主张迭代性的软件开发,并强调每一次迭代都选择对用户最有价值的功能作为本次迭代的任务,迭代周期不宜太长。每次迭代结束以后,就向用户交付一个可运行的、实现部分需求的软件产品。(2)即使到了软件开发后期,也欢迎用户需求的变化。需求不断变化和调整是软件工程化开发的一个重要特点。敏捷软件开发方法的实践者不应惧怕变化,而应适应用户需求的变化,从而为用户创造竞争优势。为了支持用户需求的变化,敏捷软件开发所生成的软件结构应具有足够的灵活性,以便在需求变化时能以最小的代价迅速地做出调整。因此,敏捷软件开发主张采用模式、迭代和重构等技术,以适应用户需求的变化,获得软件结构的灵活性。(3)不断交付可运行的软件系统,交付周期可以从几周到几个月。敏捷软件开发主张软件开发团队应经常性地向用户交付可运行的软件系统,而不是大量的文档或者计划。交付的周期要适宜,太长易使用户失去耐性,软件开发团队也无法从用户处及时获得反馈信息;过短会使用户难以接受持续不断的软件产品版本。(4)在整个软件项目开发期间,用户和开发人员最好能每天一起工作。为了使软件开发过程保持“敏捷”性,开发人员应及时从用户处获得各种反馈信息,因此需要用户与软件开发人员一起工作,以便在需要的时候及时给予反馈。(5)由积极主动的人来承担项目开发,给他们提供所需环境和支持,信任他们的能力。在影响软件项目的诸多因素中,人是其中最为重要的因素。因此参与软件项目的人应积极主动,并要为它们参与软件开发创造良好的环境和条件。(6)团队内部最有效的信息传递方式是面对面的交谈。敏捷软件开发主张软件开发团队人员之间采用面对面交谈的方式来进行沟通,文档不作为人员之间交流的默认方式,只有在万不得已的情况下,才去编写文档。(7)将可运行的软件作为衡量软件开发进度的首要衡量标准。所谓可运行的软件是指完成了用户的部分或全部需求,并经过测试,可在目标环境下运行的软件系统。不同于其它的软件开发方法,敏捷软件开发不是根据所处的软件开发阶段、已编写的文档数目或者已完成的代码数量来衡量软件开发进度,而是基于可运行的软件系统实现了多少软件需求来衡量软件开发进度。(8)可持续性的开发,出资方、开发方和用户方应当保持长期、恒定的开发速度。对于许多软件项目而言,软件开发是一个长期的过程。敏捷软件开发主张软件开发团队根据自身的特点选择合适、恒定的软件开发速度。不应盲目追求高速,软件开发速度过快可能使软件开发人员陷入疲惫状态,可能会出现一些短期行为,以致于给软件项目留下隐患。(9)关注优秀的技能和良好的设计会增强敏捷性。敏捷的一个重要体现是响应变化的能力。良好的设计是提高软件系统应变能力的关键。因此,软件开发人员必须从一开始就努力做好设计,并在整个项目开发期间不断审查和改进设计。所有的软件开发人员都应致力于编写高质量的代码,不要为了追求短期目标而降低工作质量,将改进的工作留到以后再做。(10)简单化。这里所说的简单化是指软件开发工作应着眼于当前欲解决的问题,不要把问题想的太复杂(如去预测将来可能出现的问题),并采用最为简单的方法去解决它,不要试图去构建那些华而不实的系统。(11)最好的架构、需求和设计出自于自组织的团队。敏捷团队应当是自组织的,以适应需求的变化。软件开发任务不是从外部直接分配到团队成员,而是交给软件开发团队,然后再由团队自行决定任务应当怎样完成。软件项目开发不是划分成若干部分然后交给相应的成员全权负责,所有成员对于软件项目的所有部分都有权参与。(12)软件开发团队应定期就如何提高工作效率的问题进行反思,并进行相应的调整。敏捷软件开发方法不是一成不变的,敏捷本身即含有适时调整的意思。随着项目的推进,软件开发团队应不断地对其组织方式、规则、关系等方面进行反思,并对这些方面进行调整,以便不断优化团队结构、提高软件开发效率。1.2敏捷软件开发特点敏捷思想对软件开发提出了新的理解和认识。它没有深奥的理论,也没有引入新的概念和特有的技术,只是将经过数十年检验的一组软件工程准则有机地结合在一起,确保这些软件工程准则相互支持并能够得到有效执行,从而促进当前软件工程所面临的问题的解决。敏捷意味着轻盈、灵巧、无过多的负担、能够迅速响应变化。根据敏捷软件开发的指导思想和实践原则,敏捷软件开发具有以下几个方面的特点。−小敏捷软件开发主张软件开发过程只需生成少量的软件文档,每个文档的规模要小;软件开发应该迭代进行,每次迭代要实现的软件功能需求的数量和规模要小,从而确保每次迭代的周期要小。−简敏捷软件开发建议软件开发过程中所采用的技术、所使用的工具以及每次迭代要解决的问题要尽可能的简单;软件开发人员在每次迭代中只关注当前欲实现的功能需求,而不要考虑将来的问题,从而使得软件开发人员能够聚焦关注点,简化问题的解决。−快为了快速响应变化、尽快从用户处获得反馈信息,敏捷软件开发要求软件开发人员尽快地给用户提交有价值的软件产品,快速地对软件产品进行迭代和更新,以向用户持续地交付不断完善的软件产品。这里所说的软件产品是指可运行的软件系统,而不是软件文档。−变敏捷软件开发允许用户需求的动态变化,主张要以变应变,尤其是开发团队应该是自组织的,软件系统的设计应能够有效地支持用户需求的变化,在整个软件开发过程中项目开发团队应不断检讨软件开发方法、技术、管理和工具等方面的不足和局限,以便对它们进行不断的改进和优化。−体按照敏捷软件开发思想,软件开发人员和用户应融为一体,形成一个团队;敏捷软件开发非常强调构成团队的各个成员的素质,包括能力、技能、工作的积极性和主动性;此外敏捷软件开发还鼓励个体之间的交流,并强调这种交流是以交谈为主,而不是以文档为媒介。从总体上看,敏捷软件开发方法与其它一些重型的软件开发方法有以下三个方面的本质差别。首先,敏捷软件开发强调方法本身的适应性,针对变化不断进行优化和调整,主动适应变化;而重型软件开发方法以预测性和计划性为主,倾向于预先制定详细的计划,通过该计划来指导软件项目的实施,并期望软件开发过程与计划之间的偏差越少越好。其次,敏捷软件开发强调以人为本,认为软件开发是面向人的而不是面向过程的,要求让软件开发所需的各种方法、技术、工具和过程等适应人,而不是让人去适应它们;而重型软件开发方法试图定义一种广泛适用的软件开发过程并通过团队来执行该软件开发过程,从而来指导软件系统的开发。第三,敏捷软件开发重点关注和强调可运行的软件系统,弱化了文档在软件开发中的作用;而重型软件开发方法则非常重视软件文档的撰写和管理。敏捷软件开发的上述特点使得它更加适合于小规模软件开发团队,因为过多的软件开发人员势必会使得软件开发人员之间的交流变得非常复杂;同时也使它更加适合于需求易变的软件系统的开发,从而充分发挥该方法的技术优势。1.3支持敏捷软件开发的技术和管理手段敏捷软件开发的基本思想和实践原则为软件系统的开发提供了一组高层的策略,它们明确了实现敏捷软件开发的目标和要求,因而需要相应技术和管理手段的支持。从技术的角度来看,敏捷思想和原则对软件系统的开发提出了以下一组要求:尽快开发出可运行的软件系统;当用户需求改变时应迅速地响应变化;获得良好的软件设计,以便当需求变化时对软件设计进行不断的调整和优化;保证软件系统的质量;提高敏捷软件开发的效率等等。现阶段软件工程领域有以下一组技术可以有效地满足上述要求,支持敏捷软件开发。−测试驱动开发测试驱动开发要求软件开发人员在编写程序代码之前,先确定和编写好测试。或者说,软件开发人员首先要思考如何对某个功能进行测试,设计好相应的测试用例,编写好相关的测试代码,然后编写相应的程序代码以通过软件测试。这一技术支持软件系统功能的逐步实现,有助于保证任何程序代码都是可测试的,从而确保软件系统的质量。本章7.2节将详细介绍测试驱动开发技术。−敏捷设计敏捷软件开发对软件系统的设计提出了更高的要求。为了支持用户需求的动态变化以及由此而引发的对软件设计的持续调整和优化,软件系统的设计应易于改动和调整,具有稳固性、可理解性、简单性、干净性和简洁性等特点。针对这一要求,RobertC.Martin提出一组支持敏捷软件开发的设计原则[3],包括:(1)单一职责原则,要求每个模块只承担一个职责,减少引起模块变化的因素,提高模块的内聚度;(2)开放封闭原则,扩展时无需更改模块的源代码和可执行代码,要尽可能利用抽象类,以体现软件设计的灵活性和可重用性;(3)依赖倒置原则,抽象不应该依赖细节,细节应依赖于抽象;(4)接口隔离原则,接口中的方法都是有意义的,否则就要相互分离等等。−模式运用充分利用各种模式,包括体系结构模式和设计模式来进行软件系统的设计,以支持软件系统的可重用性和应对用户需求的变化。−快速原型技术快速原型技术有助于迅速生成软件系统的原型,并以此为媒介支持软件开发人员和用户之间的交流和沟通,促使软件开发人员关注于用户的需求,适应用户需求的动态变化,帮助软件开发人员尽快从用户处及时获得反馈信息。−MDAMDA强调将软件系统的功能规约与实现这些功能的技术和平台相分离,它区分两类不同的软件系统模型:平台无关的模型和平台相关的模型,并通过模型映射在不同模型之间建立桥梁,从而有助于保护用户的业务模型,促进软件系统的快速开发和部署。−CASE工具目前已有许多支持敏捷软件开发的软件工具,包括由Microtool公司研发的ActifExetreme,它支持敏捷过程管理;由Ideogramic公司开发的IdeogramicUML,它支持针对敏捷过程的UML建模;由Borland公司开发的TogetherToolSet,它支持敏捷开发和极限编程中的诸多活动等等。从管理的角度来看,敏捷思想和原则对软件系统的开发提出了以下一组要求:管理好用户的需求;确保软件过程支持持续性的交付软件系统;管理好软件开发团队;支持软件开发人员和用户之间的交流、合作以及问题的及时反馈;以人为本,充分发挥人的积极性和主动性;保证软件开发速度的稳定性和持续性;不断改进和优化软件开发团队等等。为了应对这些要求,基于敏捷软件开发方法的软件项目应遵循以下的管理方法。−软件过程模型的选择基于敏捷软件开发方法的软件项目组应选择那些支持渐进、迭代开发的软件过程模型,如迭代模型、螺旋模型、Rup和快速原型等。−团队建设基于敏捷软件开发方法的软件项目开发团队应充分发挥人的主体作用,将用户作为软件开发团队中的成员,并与软件开发人员一起工作和交流;为软件开发团队提供良好的交流环境,如拥有共同的办公区间和时间,基于网络的虚拟环境;支持团队成员,尤其是开发人员和用户之间的双向交流和沟通。−需求管理尽管用户需求在整个软件开发过程中是动态变化的,但是每次迭代欲实现的用户需求应该是稳定的,所生成的需求文档应处于受控状态,与项目计划、产品和活动相一致,并作为开展软件开发工作的基础。软件开发人员通过和用户的充分和持续性交流,支持需求确认和评审。−软件项目计划软件开发人员和用户一起参与计划的制定,包括估算规模和进度、确定人员分工;项目计划的制定者应参照用户需求来制定软件项目计划,包括系统应当满足哪些需求、应当首先满足哪些需求、每次发布的版本应完成多少功能才会对用户的业务有所改善等等。软件项目计划不应过细,应保留一定的灵活性。同时每次迭代要量力而行,确保要实现的系统功能不要太多。多个迭代欲实现的系统功能和迭代周期要大致相当,防止软件开发周期的剧烈变化,支持稳定和可持续的软件开发。此外,每次迭代的软件开发周期要适中,不宜过长否则用户会失去耐心,无法及时得到反馈;也不宜过短,否则用户难以消化,同样影响反馈。−跟踪监督在对敏捷软件开发项目的跟踪和监督过程中,软件项目管理人员要特别关注以下的软件风险:(1)对规模和工作量的估算过于乐观,该软件风险将影响项目的周期性迭代;(2)软件开发人员和用户之间的沟通不善,该软件风险将可能导致软件需求得不到用户的认可和确认;(3)需求定义不清晰和不明确,该软件风险将可能导致需求不清,所开发的软件系统和用户要求不一致;(4)项目组成员不能有效地在一起工作,该软件风险将可能导致软件开发效率和软件项目组敏捷度的下降;(5)任务的分配和人员的技能不匹配,该软件风险将导致软件开发不能做到以人为本;(6)软件设计低劣,该软件风险将可能导致所开发的软件系统无法适应用户需求的不断变化和调整等等。1.4极限编程极限编程是由KentBeck提出的一种特殊的敏捷软件开发方法[3][4][5],它提出了更加具体和实际的指导方法以支持软件系统的敏捷开发。极限编程将其核心思想归结为四条:(1)交流,极限编程强调交流对于软件系统开发的重要性,但是它侧重于基于口头(而不是文档、报表和计划)的交流;(2)反馈,极限编程主张通过持续、明确的反馈来获得软件的状态,它对于软件项目的成功实施是至关重要的;(3)简单,极限编程主张用最简单的技术来解决当前的问题;(4)勇气,极限编程强调快速开发并在必要时具有重新进行开发的信心。在此基础上,极限编程定义了五条指导性原则和十二条必须遵循的核心准则。按照极限编程创始人KentBeck的观点,极限编程并没有引入任何新的概念,它的创新之处在于:将经过数十年检验的准则结合在一起,确保这些准则相互支持并能够得到有效执行。指导原则极限编程的四条价值观构成了整个方法学的基础,在此基础上极限编程引出了五条原则作为行为与实践的指南。1.快速反馈极限编程要求软件开发人员从用户处迅速得到有关软件系统的反馈情况,比如软件开发人员通过小步迭代迅速了解用户的反应,以确认当前所做的开发工作是否满足用户的需求,通过经常性的自动化测试和集成迅速了解软件系统的运行状况。2.简单性假设极限编程要求软件开发人员只考虑当前迭代所面临的问题,无需考虑将来(如下一次迭代)所面临的问题,并且用简单的方法和技术来解决问题。3.逐步更改极限编程要求通过一系列细微的修改来逐步解决问题和完善系统,不要期望一次迭代就开发出一个完整的软件系统。4.支持变化极限编程要求在软件开发过程欢迎用户改变需求,支持用户需求的动态变化。5.高质量的工作极限编程要求采用诸如测试驱动开发等技术高质量地开展工作,确保所开发软件系统的质量。核心准则极限编程总结出了十二项核心准则以指导软件系统的开发。这些实践在日常的软件工程化开发大多为人们所采用,然而单独采用某些准则却有可能会导致混乱,极限编程的独特之处在于将这些核心准则有机结合在起来以达到最佳效用。(1)计划游戏(PlanningGame)计划游戏旨在帮助软件开发团队快速制定下一次迭代的软件开发计划。参与计划游戏的人员包括软件开发人员和业务人员。业务人员在计划游戏中的职责包括:确定范围即系统应当满足哪些需求、规定需求的优先级即应当首先满足哪些需求、划分版本即每一次发布的版本应当完成那些功能才会对用户的业务有所改善、规定发布日期等等。业务人员的计划决策需要得到软件开发人员的反馈和支持。软件开发人员在计划游戏中的职责包括:估算实现每项功能所需的时间、解释业务人员的决策在技术上的影响如数据库的选择对软件的影响、制定日程安排、分配工作等等。(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(“Thesizeofemptylistshouldbe0”,0,emptyList.size());}程序员然后将测试作为列表类的一种行为规约来指导列表类程序代码的编写。根据上述测试用例和测试代码的描述,程序员首先要实现和编写List类的方法size()。对于任何空列表而言,该方法的返回值均为0。−确保任何程序代码都是可测试的由于在测试驱动开发中,程序员首先考虑的是如何测试软件系统的功能(即确定和编写测试),然后再考虑如何实现系统的功能(即编写程序代码)。因此,测试驱动开发可以确保所有的程序代码都是根据程序员所设计的测试集来编写的,所编写的任何程序代码都是可测试的。这有助于有效地发现程序代码中的故障、提高软件系统的质量。从本质上看,测试驱动开发的目标是让所编写的程序代码奏效(Work)和洁净(Clean),即所谓的“CleanCodethatWorks”。所谓的奏效是指所编写的程序代码实现了软件系统的功能并通过了相应的测试。所谓的洁净是指软件系统的所有程序代码均是按照测试驱动的方式来开发的,没有无关的程序代码。一般的,测试驱动开发应遵循以下一组原则。(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:voidjunit.framework.TestSuite+addTest:void+addTestSuite:void+countTestCases:int+run:void+runTest:voidjunit.framework.Test+countTestCase:int+run:voidjunit.framework.TestCase+countTestCase:int+run:void图1.Junit的主要类结构目前许多软件开发工具和环境(如Eclipse)集成了JUnit以支持软件测试。下面通过一个简单的例子,说明如何通过重用JUnit提供的上述类以及JUnit的图形化界面来进行单元测试以发现程序代码中的错误。在该例子中,程序员要编写一个简单的计算器,它具有加法和减法的两个功能。该计算器的Java程序代码如图2所示。publicclassCalculator{publicintadd(intaugend,intaddend){returnaugend+addend;}publicintsubtraction(intminuend,intsubtrahend){returnminuend-subtrahend;}}图2.实现计算器的程序代码类Calculator.java将上述程序代码保存在Calculator.java的文件中,运行javacCalculator.java将该源程序代码编译为Java中间代码Calculator.class。根据上述代码实现的功能,下面利用JUnit提供的测试类TestCase编写测试程序并设计相应的测试用例。importjunit.framework.TestCase;publicclassTestSampleextendsTestCase{publicvoidtestAdd(){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类TestCase。第2行程序定义了一个测试类TestSample来对Calculator类进行测试,该类继承了TestCase类。类TestSample有两个方法testAdd和testSubstration,分别用于测试计算器的加法和减法功能。在这两个方法中均有一个语句assertEquals,用于判断该语句中的两个参数是否相等的。如果相等则意味着通过对该功能的测试,否则测试不通过。运行javacTestSample.java以将该测试程序编译为Java中间码TestSample.class,然后输入javajunit.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-16importjunit.framework.Test;importjunit.framework.TestSuite;publicclassTestAll{publicstaticTestsuite(){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{publicvoidtestEmptyListSize(){AirlineListemptyList=newAirlineList();emptyList.assertEquals(0,emptyList.size());}}图7.测试空列表的程序代码步骤3.编译测试对测试程序进行编译,此时发现编译无法通过,编译器提示“AirlineListcannotberesolvedtoatype”。原因非常简单,程序员还没有编写AirlineList这个类及其size()方法。为此,程序员需要修改和完善程序代码,以使得测试程序能够顺利的通过编译。此时的工作实际上就是编写待开发功能的程序代码。程序员增加的程序代码如图8所示。该代码对应于一个类AirlineList及其两个方法:AirlineList()和size()。需要注意的是,程序员仅仅增加了为了满足本次测试所需的代码,而没有完整地实现整个AirlineList类。这正体现了测试驱动开发的思想,即根据测试来编写程序。再次编译上述代码,此时编译能够正常通过。8-18publicclassAirlineList{privateintnSize;publicvoidAirlineList(){nSize=0;}publicintsize(){nSize=nSize+1;returnnSize;}}图8.航空信息列表类的程序代码步骤4:运行程序代码运行上述程序代码,此时JUnit将弹出如图9所示的窗口。该窗口上部测试状态栏的颜色为红色,表明程序代码未通过测试。进一步观察FailureTrace子窗口,可以注意到它显示以下信息“junit.framework.AssertionFailedError:expected:<0>butwas:<1>”,这预示着上述程序代码存在故障。通过进一步的调试可以发现,原来类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{publicvoidtestEmptyListSize(){AirlineListemptyList=newAirlineList();assertEquals(0,emptyList.size());}publicvoidtestSizeAfterAddingOne(){AirlineListairlineList=newAirlineList();airlineList.addOne("Airline5362");intntemp=airlineList.size();assertEquals(1,airlineList.size());}}图10.第二次迭代的测试程序8-20publicclassAirlineList{privateintnSize;publicvoidAirlineList(){nSize=0;}publicvoidaddOne(StringstrLineDesc){nSize=nSize+1;}publicintsize(){returnnSize;}}图11.第二次迭代的功能程序代码3面向方面软件开发传统的软件开发技术倾向于按照模块化和功能分解的方式对软件系统进行分割。这种分割实际上是在纵向层面对软件系统的业务功能进行分析、设计和组织。例如,按照结构化软件开发方法的思想,一个软件系统被分解为一组过程和函数,不同的过程和函数间通过调用相互作用。在面向对象软件开发方法中,一个软件系统由一组对象类组成,每个对象类封装了属性和方法,不同对象类之间存在诸如继承等关系,并通过消息传递进行交互作用。然而在大量的软件工程实践中,人们发现软件系统中的许多业务模块经常需要封装一些相同的行为用于辅助系统业务功能的实现,如日志、事务处理、并发控制、对上下文敏感的错误处理、性能优化等等。借助于传统的软件开发技术,软件开发人员通常将这些行为与业务逻辑封装在一起,形成相应的业务功能模块。近年来人们提出了一种面向方面的软件开发方法。该方法主张将业务功能与辅助业务功能实现的其它行为相分离,强调应该从横切和纵向两个不同的关注点来支持软件系统的开发。3.1面向方面软件开发的基本思想早在二十世纪九十年代初期,人们就注意到了面向对象软件开发方法的局限性。尽管这一关键技术引入了诸如对象、类、消息传递、继承等概念和机制来自然模拟现实世界中的应用系统,建立层次性的对象类结构,但是当需要为一组对象引入公共行为(如日志、安全、异常处理等)时,面向对象程序设计则难以有效地处理这类问题。为了说明问题,考虑一个电子商务系统例子。该系统具有订单管理和商品管理等功能,并且这些功能都需要与相应的权限管理相结合,确保只有获得授权的用户方可操作这些功能。借助于面向对象的程序设计技术,实现该系统的伪代码如图12、图13和图14所示。8-21publicclassOrderManager{privateArrayListm_Orders;publicOrderManager(){m_Orders=newArrayList();}publicvoidAddOrder(Orderorder){Authorizationpermission=newAuthorization();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;publicProductManager(){m_Products=newArrayList();}publicvoidAddProduct(Productproduct){Authorizationpermission=newAuthorization();if(permission.Verify(Authorization.ADMIN)){m_Products.Add(product);}}publicvoidRemoveProduct(Productproduct){Authorizationpermission=newAuthorization();if(permissions.Verify(Authorization.ADMIN)){m_Products.Remove(product);}}}图13.ProductManager模块的伪代码8-22publicclassAuthorization{publicAuthorization(){};……;publicboolVerify(StringauthType){……//校验用户权限}}图14.权限管理模块的伪代码用户的权限管理被封装为一个对象类Authorization,产品管理和订单管理两个业务功能被分别封装为两个不同的对象类ProductManager和OrderManager。这两个对象类通过创建Authorization对象,向该对象发送消息来对操作业务功能的用户权限进行验证。面向对象的上述模块封装和交互方式使得所开发出的软件系统存在以下几个方面的问题。−业务逻辑不清晰,从某种意义上看,权限校验并不是业务逻辑执行的一部分,这个工作是属于系统的,但是上述代码把系统的权限校验和业务逻辑执行过程掺杂在一起,造成代码的混乱。−代码纠缠,ProductManager和OrderManager两个对象类中纠缠着多项不同用途的代码,包括业务逻辑代码(如订单管理)和非业务逻辑代码(如权限验证),这势必会影响软件系统的模块性、可重用性和可理解性。−代码冗余和浪费,ProductManager和OrderManager两个对象类中都封装了相同的代码用于用户权限的验证。−模块之间的紧耦合,业务逻辑代码中显式引用Authorization对象类,这就造成了业务逻辑代码同Authorization类的紧耦合,当Authorization对象类发生变化时,可能会影响到所有引用代码。−不易扩展,如果软件系统需要升级或增加发生在多个模块中的公共行为,那么软件开发人员需要同步变更几乎所有的模块。−不灵活,当需要暂时禁止或者添加某项功能时,采用传统的软件开发技术,软件开发人员将不得不采用修改源代码的方式来实现。导致上述问题的根本原因是传统的软件开发技术无法将业务逻辑与辅助业务逻辑的其它行为相分离,其软件设计思想和模块化机制常常将业务逻辑功能与非业务行为纠缠在一起。面向方面的软件开发可有效促进上述问题的解决。面向方面的软件开发是一种“关注点”分离技术。所谓关注点是指一个特定的目的,一块感兴趣的区域或者一组所需的逻辑行为。从软件开发过程的角度来看,软件系统的开发包含以下一组关注点:需求分析、软件设计、编码、软件测试和软件维护。从技术的角度来看,一个软件系统包含了一系列核心关注点和系统关注点。核心关注点侧重于系统需要实现的业务功能;系统关注点侧重于实现核心级关注点所必需的配套设施和约束,它们往往对应于实现系统所需的一些非功能性要求,如安全、日志、异常处理、QoS等等。例如电子商务系统8-23的核心关注点是诸如订单管理、产品管理、交易等业务功能,而系统关注点则是诸如日志、事务完整性、授权、安全及性能等。通过核心关注点,软件开发人员往往将软件系统分解为一组层次化的模块单元,它们代表了在纵向方面对软件系统的理解、分析、设计和组织,体现了自顶向下和自底向上相结合的软件开发方式。许多系统关注点往往会在软件系统的多个模块中出现并跨越这些模块,它们代表了一种横切关注点来理解、分析、设计和组织软件系统,体现了自左向右和自右向左相结合的软件开发方式(见图15)。软件模块横切关注点纵向关注点软件模块软件模块业务逻辑业务逻辑业务逻辑操作日志权限管理事务处理图15.软件系统开发的横切和纵向关注点面向方面的软件开发主张将软件系统的横切关注点和纵向关注点相互分离。通过纵向关注点,软件开发人员可以对软件系统的业务逻辑功能进行封装和组织;通过横切关注点,软件开发人员可以对支持业务逻辑的其它行为进行分析和组织,将它们封装成一个个单独的“方面”,形成横切关注点的模块单元;同时利用织入(Weaving)机制将纵向模块和横切模块相互交融,形成目标软件系统(见图16)。因此,所谓的方面实际上是指设计和实现横切关注点的模块单元,面向方面软件开发技术是将横切关注点局部化和模块化的实现机制。8-24日志aspect认证aspect权限aspect横切关注点模块纵向关注点模块业务逻辑关注点分离织入机制目标软件系统软件需求图16.面向方面软件开发的关注点分离和织入机制一般地,面向方面软件开发大致有以下的过程。首先,根据软件系统的需求,分解和确定软件系统的横切关注点和核心级关注点;其次,利用面向方面的程序设计语言分别实现各个关注点,得到目标软件系统的程序代码;第三,通过定义程序代码的重组规则和织入机制,来实现不同关注点的代码组合。面向方面软件开发思想最初来自于上世纪九十年代施乐公司帕洛阿尔托研究中心(XeroxPARC),GregorKiczales领导下的研究小组提出了一种称为面向方面程序设计的新颖编程范式。它采用关注点分离的思想,通过运用“方面”这种程序设计单元,使软件开发人员更好地将原本不该彼此纠缠在一起的功能和行为(如数学运算和异常处理)分离开来,从而使得软件系统的设计和编码具有更好的模块化和结构性。近年来,人们开始将这一思想延伸到了分析和设计阶段,形成了面向方面的软件开发方法学。面向方面软件开发的优势是显而易见的。它将软件系统的横切关注点和纵向关注点相分离,并使得任一关注点的实现对其它关注点而言是透明的,能够有效解决不同性质的程序代码相互纠缠的问题,提高软件模块的功能单一性和内聚度,降低不同模块之间的耦合度;在分离出横切关注点的同时,确保软件开发人员集中关注于系统的核心业务逻辑,有助于得到松散耦合、可复用与可扩展的软件系统。面向方面的软件开发可以让不同的人专注于不同的事。例如安全事务专家带着他的团队编写有关系统安全方面模块,业务专家集中注意力于业务逻辑。3.2面向方面编程的实现原理面向方面的程序设计用方面来封装软件系统的横切行为,用传统的程序设计语言设施(如面向对象程序设计语言中的对象类)来封装纵向的业务逻辑功能,采用织入机制来实现软件系统的横切方面模块与纵向业务功能模块之间的交融。面向方面编程的织入机制可分为两类:静态织入和动态织入。静态织入技术是指在程序编译期间,通过在业务功能代码的适当位置(比如某段代码执8-25行前、执行后)织入方面代码,从而形成目标软件系统的混合程序代码。这一技术的特点是在软件系统运行前实现方面代码和业务代码二者之间的交融,通过对程序代码进行优化,可以使程序运行的开销最小化。动态织入技术是指在程序运行期间,根据程序运行的上下文,通过截取对象消息的方式,在业务执行流程的适当位置(如某个方法被执行之前、某个属性被修改之前)执行方面的程序代码,从而来实现方面代码和业务代码二者之间的交融。动态织入技术的特点是在软件系统的运行过程中实现织入,这一技术涉及以下一组核心概念。−连接点(JoinPoint)连接点是指程序代码执行中的一个精确执行点(如方法调用处或者方法返回处),在这些位置上可以附加额外的行为(如执行权限检查方面的程序代码),它是面向方面编程的粘合剂。−切入点(PointCut)切入点用于定义或者指定希望在程序代码中截获的连接点。它是一个用来描述一系列连接点的表达式,本质上是一个捕获连接点的结构。在面向方面编程中,程序员可以通过定义一个或者多个切入点来在适当的位置执行方面代码。−通知(Advice)通知是一种可执行的逻辑代码(如安全代码、日志代码、权限检查代码),当程序运行到相关的连接点时,它就需要运行该代码。在面向方面编程中,通知对应于由方面定义的横切行为。−引入(Introduce)引入可以改变一个对象类的方法或属性以及程序的动态结构,从而达到修改对象类结构的目的。有些面向方面编程工具又将其称为mixin。−方面方面封装了一个横切关注点的切入点和通知,它描述了一个模块化的横切行为,定义了在业务逻辑模块的何处织入横切行为。方面类似于面向对象程序设计中的类,不同的是它是对软件系统横切行为的抽象和封装。根据面向方面软件开发的思想,下面对上一节所描述的电子商务系统例子重新进行改造。首先从纵向关注点来看,软件系统有两个业务功能模块OrderManager和ProductManager类,这两个类中均不再包含有任何权限检查的代码,如图17和图18所示。其次从横切关注点来看,该软件系统的业务逻辑需要进行权限检查,为此软件开发人员可以抽象出一个有关权限检查的方面模块。当业务逻辑执行诸如AddOrder、RemoveOrder、AddProduct、RemoveProduct等方法之前,系统需要验证用户是否具有执行这些操作的权限。因此,在该例子中AddOrder、RemoveOrder、AddProduct、RemoveProduct等方法的执行即为连接点。由于软件系统需要在每个方法执行前进行权限检查,所以对于这一系列的连接点,软件开发人员只需要定义一个切入点。当软件系统执行到连接点时,将根据定义去查找对应的切入点,然后执行相应的通知。软件开发人员可以将该软件系统的切入点和通知进行封装,形成关于权限检查的方面模块Authorization。8-26Authorization方面模块定义了相应的切入点和横切行为(见图19)。其中切入点定义部分描述了OrderManager和ProductManager类中AddOrder、RemoveOrder、AddProduct、RemoveProduct方法的调用是该方面的切入点;通知定义部分描述了在执行订单管理和商品管理相关方法之前实施通知行为。这样软件开发人员就定义了一个完整的方面模块,当系统调用AddOrder、RemoveOrder、AddProduct、RemoveProduct等方法时,就触发了相应的切入点,系统将根据权限管理方面的定义,执行相应的通知逻辑。OrderManager和ProductManager两个模块与权限管理模块完全解除了依赖关系,减少了模块之间的耦合度和重复代码。publicclassOrderManager{privateArrayListm_Orders;publicOrderManager(){m_Orders=newArrayList();}publicvoidAddOrder(Orderorder){m_Orders.Add(order);}publicvoidRemoveOrder(Orderorder){m_Orders.Remove(order);}}

图17.修改后OrderManage模块的伪代码publicclassProductManager{privateArrayListm_Products;publicProductManager(){m_Products=newArrayList();}publicvoidAddProduct(Productproduct){m_Products.Add(product);}publicvoidRemoveProduct(Productproduct){m_Products.Remove(product);}}图18.修改后ProductManager模块的伪代码8-27publicaspectAuthorization{publicpointcutauthorizationExecution():execution(publicvoidOrderManager.AddOrder(Order))||execution(publicvoidOrderManager.DeleteOrder(Order))||execution(publicvoidProductManager.AddProduct(Product))||execution(publicvoidProductManager.DeleteProduct(Product));before():authorizationExecution(){//通知代码;}}图19.Authorization方面模块的伪代码3.3面向方面的编程语言面向方面的软件开发需要相应

温馨提示

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

评论

0/150

提交评论