敏捷建模思想(转载)

一、Agile 敏捷建模思想

敏捷建模的价值观

AM的价值观包括了XP的四个价值观:沟通、简单、反馈、勇气,此外,还扩展了第五个价值观:谦逊。 沟通. 建模不但能够促进你团队内部的开发人员之间沟通、还能够促进你的团队和你的project stakeholder之间的沟通。

  简单. 画一两张图表来代替几十甚至几百行的代码,通过这种方法,建模成为简化软件和软件(开发)过程的关键。这一点对开发人员而言非常重要-它简单,容易发现出新的想法,随着你(对软件)的理解的加深,也能够很容易的改进。

  反馈. Kent Beck在Extreme Programming Explained中有句话讲得非常好:“乐观是编程的职业病,反馈则是其处方。”通过图表来交流你的想法,你可以快速获得反馈,并能够按照建议行事。

  勇气. 勇气非常重要,当你的决策证明是不合适的时候,你就需要做出重大的决策,放弃或重构(refactor)你的工作,修正你的方向。

  谦逊. 最优秀的开发人员都拥有谦逊的美德,他们总能认识到自己并不是无所不知的。事实上,无论是开发人员还是客户,甚至所有的project stakeholder,都有他们自己的专业领域,都能够为项目做出贡献。一个有效的做法是假设参与项目的每一个人都有相同的价值,都应该被尊重。

敏捷建模的原则

  敏捷建模(AM)定义了一系列的核心原则和辅助原则,它们为软件开发项目中的建模实践奠定了基石。其中一些原则是从XP中借鉴而来,在Extreme Programming Explained中有它们的详细描述。而XP中的一些原则又是源于众所周知的软件工程学。复用的思想随处可见!基本上,本文中对这些原则的阐述主要侧重于它们是如何影响着建模工作;这样,对于这些借鉴于XP的原则,我们可以从另一个角度来看待。

核心原则:

  主张简单. 当从事开发工作时,你应当主张最简单的解决方案就是最好的解决方案。不要过分构建(overbuild)你的软件。用AM的说法就是,如果你现在并不需要这项额外功能,那就不要在模型中增加它。要有这样的勇气:你现在不必要对这个系统进行过分的建模(over-model),只要基于现有的需求进行建模,日后需求有变更时,再来重构这个系统。尽可能的保持模型的简单。

  拥抱变化. 需求时刻在变,人们对于需求的理解也时刻在变。项目进行中,Project stakeholder可能变化,会有新人加入,也会有旧人离开。Project stakeholder的观点也可能变化,你努力的目标和成功标准也有可能发生变化。这就意味着随着项目的进行,项目环境也在不停的变化,因此你的开发方法必须要能够反映这种现实。

  你的第二个目标是可持续性. 即便你的团队已经把一个能够运转的系统交付给用户,你的项目也还可能是失败的--实现Project stakeholder的需求,其中就包括你的系统应该要有足够的鲁棒性(robust ),能够适应日后的扩展。就像Alistair Cockburn常说的,当你在进行软件开发的竞赛时,你的第二个目标就是准备下一场比赛。可持续性可能指的是系统的下一个主要发布版,或是你正在构建的系统的运转和支持。要做到这一点,你不仅仅要构建高质量的软件,还要创建足够的文档和支持材料,保证下一场比赛能有效的进行。你要考虑很多的因素,包括你现有的团队是不是还能够参加下一场的比赛,下一场比赛的环境,下一场比赛对你的组织的重要程度。简单的说,你在开发的时候,你要能想象到未来。

  递增的变化. 和建模相关的一个重要概念是你不用在一开始就准备好一切。实际上,你就算想这么做也不太可能。而且,你不用在模型中包容所有的细节,你只要足够的细节就够了。没有必要试图在一开始就建立一个囊括一切的模型,你只要开发一个小的模型,或是概要模型,打下一个基础,然后慢慢的改进模型,或是在不在需要的时候丢弃这个模型。这就是递增的思想。

  令Stakeholder投资最大化. 你的project stakeholder为了开发出满足自己需要的软件,需要投入时间、金钱、设备等各种资源。stakeholder应该可以选取最好的方式投资,也可以要求你的团队不浪费资源。并且,他们还有最后的发言权,决定要投入多少的资源。如果是这些资源是你自己的,你希望你的资源被误用吗。

  有目的的建模.对于自己的artifact,例如模型、源代码、文档,很多开发人员不是担心它们是否够详细,就是担心它们是否太过详细,或担心它们是否足够正确。你不应该毫无意义的建模,应该先问问,为什么要建立这个artifact,为谁建立它。和建模有关,也许你应该更多的了解软件的某个方面,也许为了保证项目的顺利进行,你需要和高级经理交流你的方法,也许你需要创建描述系统的文档,使其他人能够操作、维护、改进系统。如果你连为什么建模,为谁建模都不清楚,你又何必继续烦恼下去呢?首先,你要确定建模的目的以及模型的受众,在此基础上,再保证模型足够正确和足够详细。一旦一个模型实现了目标,你就可以结束目前的工作,把精力转移到其它的工作上去,例如编写代码以检验模型的运作。该项原则也可适用于改变现有模型:如果你要做一些改变,也许是一个熟知的模式,你应该有做出变化的正确理由(可能是为了支持一项新的需求,或是为了重构以保证简洁)。关于该项原则的一个重要暗示是你应该要了解你的受众,即便受众是你自己也一样。例如,如果你是为维护人员建立模型,他们到底需要些什么?是厚达500页的详细文档才够呢,还是10页的工作总览就够了?你不清楚?去和他们谈谈,找出你想要的。

  多种模型.开发软件需要使用多种模型,因为每种模型只能描述软件的单个方面,“要开发现今的商业应用,我们该需要什么样的模型?”考虑到现今的软件的复杂性,你的建模工具箱应该要包容大量有用的技术(关于artifact的清单,可以参阅AM的建模artifact)。有一点很重要,你没有必要为一个系统开发所有的模型,而应该针对系统的具体情况,挑选一部分的模型。不同的系统使用不同部分的模型。比如,和家里的修理工作一样,每种工作不是要求你用遍工具箱里的每一个工具,而是一次使用某一件工具。又比如,你可能会比较喜欢某些工具,同样,你可会偏爱某一种模型。有多少的建模artifact可供使用呢,如果你想要了解这方面的更多细节,我在Be Realistic About the UML中列出了UML的相关部分,如果你希望做进一步的了解,可以参阅白皮书The Object Primer -- An Introduction to Techniques for Agile Modeling。

  高质量的工作.没有人喜欢烂糟糟的工作。做这项工作的人不喜欢,是因为没有成就感;日后负责重构这项工作(因为某些原因)的人不喜欢,是因为它难以理解,难以更新;最终用户不喜欢,是因为它太脆弱,容易出错,也不符合他们的期望。

  快速反馈.从开始采取行动,到获得行动的反馈,二者之间的时间至关紧要。和其他人一共开发模型,你的想法可以立刻获得反馈,特别是你的工作采用了共享建模技术的时候,例如白板、CRC卡片或即时贴之类的基本建模材料。和你的客户紧密工作,去了解他们的的需求,去分析这些需求,或是去开发满足他们需求的用户界面,这样,你就提供了快速反馈的机会。

  软件是你的主要目标. 软件开发的主要目标是以有效的方式,制造出满足project stakeholder需要的软件,而不是制造无关的文档,无关的用于管理的artifact,甚至无关的模型。任何一项活动(activity ),如果不符合这项原则,不能有助于目标实现的,都应该受到审核,甚至取消。

  轻装前进.你建立一个artifact,然后决定要保留它,随着时间的流逝,这些artifact都需要维护。如果你决定保留7个模型,不论何时,一旦有变化发生(新需求的提出,原需求的更新,团队接受了一种新方法,采纳了一项新技术...),你就需要考虑变化对这7个模型产生的影响并采取相应的措施。而如果你想要保留的仅是3个模型,很明显,你实现同样的改变要花费的功夫就少多了,你的灵活性就增强了,因为你是在轻装前进。类似的,你的模型越复杂,越详细,发生的改变极可能就越难实现(每个模型都更“沉重”了些,因此维护的负担也就大了)。每次你要决定保留一个模型时,你就要权衡模型载有的信息对团队有多大的好处(所以才需要加强团队之间,团队和project stakeholder之间的沟通)。千万不要小看权衡的严重性。一个人要想过沙漠,他一定会携带地图,帽子,质地优良的鞋子,水壶。如果他带了几百加仑的水,能够想象的到的所有求生工具,一大堆有关沙漠的书籍,他还能过得去沙漠吗?同样的道理,一个开发团队决定要开发并维护一份详细的需求文档,一组详细的分析模型,再加上一组详细的架构模型,以及一组详细的设计模型,那他们很快就会发现,他们大部分的时间不是花在写源代码上,而是花在了更新文档上。
补充原则:
  内容比表示更重要.一个模型有很多种的表示方法。例如,可以通过在一张纸上放置即时贴的方法来建立一个用户界面规格(基本/低精度原型)。它的表现方式可以是纸上或白板上的草图,可以是使用原型工具或编程工具建立和传统的原型,也可以是包括可视界面和文本描述的正式文档。有一点很有意思,一个模型并不一定就是文档。它们通常作为其它artifact的输入,例如源代码,但不必把它们处理为正式的文档,即使是使用CASE工具建立的复杂的图表,也是一样。要认识到一点,要利用建模的优点,而不要把精力花费在创建和维护文档上。

  三人行必有我师.你不可能完全精通某项技术,你总是有机会学习新的知识,拓展知识领域。把握住这个机会,和他人一同工作,向他人学习,试试做事的新方式,思考什么该做,什么不该做。技术的变化非常的快,现有的技术(例如Java)以难以置信的速度在改进,新的技术(例如C#和.NET)也在有规律的产生。现存开发技术的改进相对会慢一些,但也在持续的改进中--计算机产业属于工业,我们已经掌握了其中的试验基本原理,但我们还在不断的研究,不断的实践,不断的改进我们对它的了解。我们工作在一个变化是家常便饭的产业中,我们必须通过训练、教育、思考、阅读、以及和他人合作,抓住每一个机会学习新的处事之道。

  了解你的模型.因为你要使用多种模型,你需要了解它们的优缺点,这样才能够有效的使用它们。

  了解你的工具.软件(例如作图工具、建模工具)有各种各样的特点。如果你打算使用一种建模工具,你就应当了解什么时候适合用它,什么时候不适合用它。

  局部调整. 你的软件开发方法要能够反映你的环境,这个环境包括组织特征,project stakeholder的特征,项目自身的特征。有可能受其影响的问题包括:你使用的建模技术(也许你的用户坚持要看到一个细节的用户界面,而不是初始草图或基本原型);你使用的工具(可能你没有数字照相机的预算,或是你已经拥有某个CASE工具的license);你遵循的软件过程(你的组织采用XP的开发过程,或是RUP,或是自己的过程)。因此你会调整你的方法,这种调整可能是针对项目的,也可能是针对个人的。例如,有些开发人员倾向于使用某一类工具,有些则不用。有些人在编码上花大力气,基本不做建模,有些则宁可在建模上多投入一些时间。

  开放诚实的沟通.人们需要能够自由的提出建议,而且人们还应该能够感受到他们是自由的。建议可能是和模型有关的想法:也许是某些人提出某部分新的设计方法,或是某个需求的新的理解;建议还可能是一些坏消息,例如进度延误;或仅仅是简单的工作状况报告。开放诚实的沟通是人们能够更好的决策,因为作为决策基础的信息会更加准确。

  利用好人的直觉.有时你会感觉到有什么地方出问题了,或是感觉什么地方有不一致的情况,或是某些东西感觉不是很对。其实,这种感觉很有可能就是事实。随着你的软件开发的经验的增加,你的直觉也会变得更敏锐,你的直觉下意识之间告诉你的,很可能就是你工作的关键之处。如果你的直觉告诉你一项需求是没有意义的,那你就不用投入大量的精力和用户讨论这方面的问题了。如果你的直觉告诉你有部分的架构不能满足你的需要,那就需要建立一个快速技术原型来验证你的理论。如果你的直觉告诉设计方法A要比设计方法B好,而且并没有强有力的理由支持你选择某一个方法,那就尽管选择方法A。勇气的价值就已经告诉你,如果未来证实你的直觉是错的,你也有能力来挽救这种情况。你应该有这种自信,这很重要。

敏捷建模的实践

  敏捷建模(AM)在AM原则的基础上定义了一组核心实践(practice)和补充实践,其中的某些实践已经是极限编程(XP)中采用了的,并在Extreme Programming Explained一书中有详细的论述,和AM的原则一样,我们在描述这组实践时,将会注重于建模的过程,这样你可以从另外一个角度来观察这些已或XP采用的素材。

核心实践:

Stakeholder的积极参与.我们对XP的现场客户(On-Site Customer)的概念做了一个扩充:开发人员需要和用户保持现场的接触;现场的用户要有足够的权限和能力,提供目前建构中的系统相关的信息;及时、中肯的做出和需求相关的决策;并决定它们的优先级。AM把XP的“现场客户”实践扩展为“使project stakeholder积极参与项目”,这个project stakeholder的概念包括了直接用户、他们的经理、高级经理、操作人员、支持人员。这种参与包括:高级经理及时的资源安排决策,高级经理的对项目的公开和私下的支持,需求开发阶段操作人员和支持人员的积极参与,以及他们在各自领域的相关模型。

  正确使用artifact.每个artifact都有它们各自的适用之处。例如,一个UML的活动图(activity diagram)适合用于描述一个业务流程,反之,你数据库的静态结构,最好能够使用物理数据(physical data)或数据模型(persistence model)来表示。在很多时候,一张图表比源代码更能发挥作用,一图胜千言,同样,一个模型也比1K的源代码有用的多,前提是使用得当(这里借用了Karl Wieger的Software Requirements中的词汇)。因为你在研究设计方案时,你可和同伴们和在白板上画一些图表来讨论,也可以自己坐下来开发一些代码样例,而前一种方法要有效的多。。这意味着什么?你需要了解每一种artifact的长处和短处,当你有众多的模型可供选择的时候,要做到这一点可没有那么容易。

  集体所有制.只要有需要,所有人都可以使用、修改项目中的任何模型、任何artifact。

  测试性思维.当你在建立模型的时候,你就要不断的问自己,“我该如何测试它?”如果你没办法测试正在开发的软件,你根本就不应该开发它。在现代的各种软件过程中,测试和质保(quality assurance)活动都贯穿于整个项目生命周期,一些过程更是提出了“在编写软件之前先编写测试”的概念(这是XP的一项实践:“测试优先”)。

  并行创建模型.由于每种模型都有其长处和短处,没有一个模型能够完全满足建模的需要。例如你在收集需求时,你需要开发一些基本用例或用户素材,一个基本用户界面原型,和一些业务规则。再结合实践切换到另外的Artifact,,敏捷建模者会发现在任何时候,同时进行多个模型的开发工作,要比单纯集中于一个模型要有效率的多。

  创建简单的内容.你应该尽可能的使你的模型(需求、分析、架构、设计)保持简单,但前提是能够满足你的project stakeholder的需要。这就意味着,除非有充分的理由,你不应该随便在模型上画蛇添足--如果你手头上没有系统认证的功能,你就不应该给你的模型增加这么一个功能。要有这样的勇气,一旦被要求添加这项功能,自己就能够马上做到。这和XP的实践“简单设计”的思想是一样的。

  简单地建模.当你考虑所有你能够使用的图表(UML图、用户界面图、数据模型等)时,你很快会发现,大部分时候你只需要这些图表符号的一部分。一个简单的模型能够展示你想要了解的主要功能,例如,一个类图,只要能够显示类的主要责任和类之间的关系就已经足够了。不错,编码的标准告诉你需要在模型中加入框架代码,比如所有的get和set操作,这没有错,但是这能提供多少价值呢?恐怕很少。

  公开展示模型.你应当公开的展示你的模型,模型的载体被称为“建模之墙”(modeling wall)或“奇迹之墙(wall of wonder)”。这种做法可以在你的团队之间、你和你的project stakeholder之间营造出开放诚实的沟通氛围,因为当前所有的模型对他们都是举手可得的,你没有向他们隐藏什么。你把你的模型贴到建模之墙上,所有的开发人员和project stakeholder都可以看建模之墙上的模型,建模之墙可能是客观存在的,也许是一块为你的架构图指定的白板,或是物理数据模型的一份打印输出,建模之墙也可能是虚拟的,例如一个存放扫描好的图片的internet网页。如果你想要多了解一些相关的资料,你可以看看Ellen Gottesdiener的Specifying Requirements With a Wall of Wonder。

  切换到另外的Artifact.当你在开发一个artifact(例如用例、CRC卡片、顺序图、甚至源码),你会发现你卡壳了,这时候你应当考虑暂时切换到另一个artifact。每一个artifact都有自己的长处和短处,每一个artifact都适合某一类型的工作。无论何时你发现你在某个artifact上卡壳了,没办法再继续了,这就表示你应该切换到另一个artifact上去。举个例子,如果你正在制作基本用例,但是在描述业务规则时遇到了困难,你就该试着把你的注意力转移到别的artifact上去,可能是基本用户界面原型、CRC模型,可能是业务规则、系统用例、或变化案例。切换到另一个artifact上去之后,你可能就立刻不再卡壳了,因为你能够在另一个artifact上继续工作。而且,通过改变你的视角,你往往会发现原先使你卡壳的原因。

  小增量建模. 采用增量开发的方式,你可以把大的工作量分成能够发布的小块,每次的增量控制在几个星期或一两个月的时间内,促使你更快的把软件交付给你的用户,增加了你的敏捷性。

  和他人一起建模.当你有目的建模时你会发现,你建模可能是为了了解某事,可能是为了同他人交流你的想法,或是为了在你的项目中建立起共同的愿景。这是一个团体活动,一个需要大家有效的共同工作才能完成的活动。你发现你的开发团队必须共同协作,才能建立一组核心模型,这对你的项目是至关重要的。例如,为了建立系统的映像和架构,你需要和同组成员一起建立所有人都赞同的解决方案,同时还要尽可能的保持它的简单性。大多数时候,最好的方法是和另一些人讨论这个问题。

  用代码验证.模型是一种抽象,一种能够正确反映你正在构建的系统的某个方面的抽象。但它是否能运行呢?要知道结果,你就应该用代码来验证你的模型。你已经用一些HTML页面建立了接受付款地址信息的草图了吗?编码实现它,给你的用户展示最终的用户界面,并获取反馈。你已经做好了表示一个复杂业务规则逻辑的UML顺序图了吗?写出测试代码,业务代码,运行测试以保证你做的是对的。永远也别忘了用迭代的方法开发软件(这是大多数项目的标准做法),也别忘了建模只是众多任务中的一个。做一会儿建模、做一会儿编码、做一会儿测试(在其它的活动之中进行)。

  使用最简单的工具.大多数的模型都可以画在白板上,纸上,甚至纸巾的背面。如果你想要保存这些图标,你可以用数码相机把它们拍下来,或只是简单的把他们转录到纸上。这样做是因为大多数的图表都是可以扔掉的,它们只有在你画出模型并思考一个问题的时候才有价值,一旦这个问题被解决了它们就不再有意义了。这样,白板和标签往往成为你建模工具的最佳选择:使用画图工具来创建图表,给你重要的project stakeholder看。只有建模工具能够给我们的编程工作提供价值(例如代码自动生成)时才使用建模工具。你可以这样想:如果你正在创建简单的模型,这些模型都是可以抛弃的。你建模的目的就是为了理解,一旦你理解了问题,模型就没有存在的必要了,因此模型都是可以丢弃的,这样,你根本就不必要使用一个复杂的建模工具。

补充实践:

  使用建模标准.这项实践是从XP的编码标准改名而来,基本的概念是在一个软件项目中开发人员应该同意并遵守一套共同的建模标准。遵守共同的编码惯例能够产生价值:遵守你选择的编码指南能够写出干净的代码,易于理解,这要比不这么做产生出来的代码好得多。同样,遵守共同的建模标准也有类似的价值。目前可供选择的建模标准有很多,包括对象管理组织(OMG)制定的统一建模语言(UML),它给通用的面向对象模型定义了符号和语义。UML开了一个好头,但并不充分-就像你在Be Realistic About The UML中看到的,UML并没有囊括所有可能的的建模artifact。而且,在关于建立清楚可看的图表方面,它没有提供任何建模风格指南。那么,风格指南和标准之间的差别在何处呢。对源代码来说,一项标准可能是规定属性名必须以attributeName的格式,而风格指南可能实说在一个单元中的一段控制结构(一个if语句,一段循环)的代码缩进。对模型来说,一项标准可能是使用一个长方形对类建模,一项风格指南可能是图中子类需要放在父类的下方。

  逐渐应用模式.高效的建模者会学习通用的架构模式、设计模式和分析模式,并适当的把它们应用在模型之中。然而,就像Martin Fowler在Is Design Dead中指出的那样,开发人员应当轻松的使用模式,逐渐的应用模式。这反映了简单的价值观。换言之,如果你猜测一个模式可能适用,你应当以这样的方式建模:先实现目前你需要的最小的范围,但你要为日后的重构留下伏笔。这样,你就以一种可能的最简单的方式实现了一个羽翼丰满的模式了。就是说,不要超出你的模型。举一个例子,在你的设计中,你发现有个地方适合使用GoF的Strategy模式,但这时候你只有两个算法要实现。最简单的方法莫过于把算法封装为单独的类,并建立操作,能够选择相应的算法,以及为算法传递相关的输入。这是Strategy模式的部分实现,但你埋下了伏笔,日后如有更多的算法要实现,你就可以重构你的设计。并没有必要因为Strategy模式需要,就建立所有的框架。这种方法使你能够轻松的使用模式。

  丢弃临时模型.你创建的大部分的模型都是临时使用的模型--设计草图,低精度原型,索引卡片,可能架构/设计方案等等--在它们完成了它们的目的之后就再不能提供更多的价值了。模型很快就变得无法和代码同步,这是正常的。你需要做出决定:如果“同步更新模型”的做法能够给你的项目增添价值的话,那就同步更新模型;或者,如果更新它们的投入将抵消它们能够提供的所有价值(即负收益),那就丢弃它们。

  合同模型要正式.在你的系统需要的信息资源为外部组织所控制的时候,例如数据库,旧有系统和信息服务,你就需要合同模型。一个合同模型需要双方都能同意,根据时间,根据需要相互改变。合同模型的例子有API的细节文档,存储形式描述,XML DTD或是描述共享数据库的物理数据模型。作为法律合同,合同模型通常都需要你投入重要资源来开发和维护,以确保它的正确、详细。你的目标是尽量使你系统的合同模型最少,这和XP的原则traveling light是一致的。注意你几乎总是需要电子工具来建立合同模型,因为这个模型是随时需要维护的。

  为交流建模. 建模的次要原因是为了和团队之外的人交流或建立合同模型。因为有些模型是给团队之外的客户的,你需要投入时间,使用诸如文字处理器,画图工具包,甚至是那些“被广告吹得天花乱坠”的CASE工具来美化模型。

  为理解建模.建模的最重要的应用就是探索问题空间,以识别和分析系统的需求,或是比较和对照可能的设计选择方法,以识别可能满足需求的、最简单的解决方案。根据这项实践,你通产需要针对软件的某个方面建立小的、简单的图表,例如类的生命周期图,或屏幕顺序,这些图表通常在你完成目的(理解)之后就被丢弃。

  重用现有的资源.这是敏捷建模者能够利用的信息财富。例如,也许一些分析和设计模式适合应用到系统上去,也许你能够从现有的模型中获利,例如企业需求模型,业务过程模型,物理数据模型,甚至是描述你用户团体中的系统如何部署的模型。但是,尽管你常常搜索一些比较正确的模型,可事实是,在大多数组织中,这些模型要么就不存在,要么就已经过期了。

  非到万不得已不更新.你应当在你确实需要时才更新模型,就是说,当不更新模型造成的代价超出了更新模型所付出的代价的时候。使用这种方法,你会发现你更新模型的数量比以前少多了,因为事实就是,并不是那么完美的模型才能提供价值的。我家乡的街道图已经使用了5年了,5年来我自己街道并没有改变位置,因此这张地图对我来说还是有用的。不错,我可以买一张新地图,地图是每年出一次的,但为什么要这么麻烦呢?缺少一些街道并没有让我痛苦到不得不投资买一份新地图。简单的说,当地图还管用的时候,每年花钱买新地图是没有任何意义的。为了保持模型、文档和源代码之间的同步,已经浪费了太多太多的时间和金钱了,而同步是不太可能做到的。时间和金钱投资到新的软件上不是更好吗?
确实不错的主意:

以下的实践虽然没有包括在AM中,但是可以做为AM的一份补充:

  重构.这是一项编码实践。重构,就是通过小的变化,使你的代码支持新的功能,或使你的设计尽可能的简单。从AM的观点来看,这项实践可以保证你在编码时,你的设计干净、清楚。重构是XP的一个重要部分。

  测试优先设计.这是一项开发实践。在你开始编写你的业务代码之前,你要先考虑、编写你的测试案例。从AM的观点来看,这项实践强制要求你在写代码之前先通盘考虑你的设计,所以你不再需要细节设计建模了。测试优先设计是XP的一个重要部分。

敏捷建模是(不是)什么?

  我坚信当在描述事物的范围时,你需要说明它是什么,它不是什么。不管你谈论的是系统还是案例中的AM都一样。以下就是我对AM的范围的观点:

AM是一种态度,而不是一个说明性的过程。AM是敏捷建模者们坚持的价值观、敏捷建模者们相信的原则、敏捷建模者们应用的实践组成的集合。 AM描述了一种建模的风格。当它应用于敏捷的环境中时,能够提高开发的质量和速度,同时能够避免过度简化和不切实际的期望。 AM可不是开发的“食谱”,如果你寻觅的是一些细节的指导,如建立UML顺序图或是画出用户界面流图,你可以看看在建模Artifacts中列出的许多建模书籍,我特别推荐我的书The Object Primer 2/e(尽管这有失公允)。

AM是对已有方法的补充,而不是一个完整的方法论。 AM的主要焦点是在建模上,其次是文档。也就是说,AM技术在你的团队采用敏捷方法(例如eXtreme Programming,Dynamic Systems Development Method (DSDM),Crystal Clear)的基础上能够提高建模的效果 。AM同样也可以用于那些传统过程(例如Unified Process),尽管这种过程较低的敏捷性会使得AM不会那么成功。

AM是一种有效的共同工作的方法,能够满足Project Stakeholder的需要。敏捷开发者们和Project Stakeholder进行团队协作,他们轮流在系统开发中扮演着直接、主动的角色。在“敏捷”的字典中没有“我”这个单词。

AM是有效的,而且也已开始有效。当你学习到更多的AM知识时,有件事对你来说可能不好接受,AM近乎无情的注重有效性。AM告诉你:要使你的Project Stakeholder的投资最大化;当有清晰的目的以及需要解了受众的需要时要建立模型或文档;运用合适的工件来记录手头的情形;不论何时都尽可能创建简单的模型。

AM是来自于实践中,而不是象牙塔般的理论。AM的目标就是以一种有效的态度描述系统建模的技术,它有效率,足够胜任你手头的工作。我和我在Ronin International (http://www.ronin-intl.com) 的同事将大量的AM技术应用于实践已经很多年了,我们琢磨的这些技术应用于非常广泛的客户,他们遍布各个不同的工业领域。而且,从2001年2月开始,就有数百位的建模专家通过“敏捷建模邮件列表”(http://www.agilemodeling.com/feedback.htm) 对这些技术进行过充分的检查和讨论。

AM不是灵丹妙药。敏捷建模是改进众多专家软件开发成果的有效技术,充其量也就是这样了。它并不是什么了不得的灵丹妙药,能够解决你开发中的所有问题。如果你努力的工作;如果你专注其上;如果打心眼儿里接受它的价值观、它的原则、它的实践;你就可以改进你做为一个开发人员的效果。

AM是面向一般的开发人员的,但并不是要排斥有能力的人。AM的价值观、原则和实践都简单易懂,其中的很多内容,可能你都已经采用或期待多年了。应用AM技术并不是要你去练水上飘,但你需要有一些基本的软件开发技能。AM最难的就是它逼着你去学习更广泛的建模技术,这是个长期的、持续性的活动。学习建模在一开始可能很难,但你可以试着一次学习一样技术来完成你的学习。 AM并不是要反对文档。文档的创建和维护都会增大项目涉众的投资。敏捷文档尽可能的简单,尽可能的小,目的只集中在和目前开发的系统有直接关系的事情上,充分了解受众的需要。

AM也不是要反对CASE工具。敏捷建模者使用那些能够帮助开发人员提高效果,提升价值的工具。而且,他们还尽力使用那些能够胜任工作的最简单的工具。

AM并不适合每一个人。这一点在下面的列出的文章中会进一步讨论。

模型何时是敏捷的?

  要想了解AM,你需要了解模型和敏捷模型之间的区别。模型是一个抽象的概念,它描述了一个的问题的一个或多个方面,或是处理这个问题可能的解决方案。传统意义上,模型被认为是图表加上相应的文档。然而那不够直观的artifact,也可以被视为模型,例如CRC卡片集,单条或多条业务规则的文字描述,或是业务流程的一段结构化英文描述。一个敏捷模型就是一个刚刚足够好的模型。但是你怎么知道什么时候模型才是刚刚足够好呢?当敏捷模型显现出如下的特性时,它就是刚刚足够好的:

  敏捷模型实现了它们的目的。有时你为沟通而建模,或许你需要把你工作的范围告诉高级经理;有时你为理解而建模,或许你需要确定一个设计策略,实现一组Java类。一个敏捷模型是否足够好,要看它是不是满足了创建它时的初衷。

  敏捷模型是可理解的。敏捷模型要能为其预期听众所理解。使用用户能够理解的业务语言来描述需求模型,反之,技术架构模型则需要使用开发人员熟悉的技术术语。你所使用的建模符号会影响易懂性--如果你的用户不了解UML用例图中的符号的含义,那用例图对用户就没有任何价值。这样的话,要么使用另一种方法,要么教授用户学习建模技术。风格问题同样也会影响易懂性,例如避免交叉线。杂乱的图表比清晰的图表难懂。模型的细节程度(见下文),也会影响易懂性,因为相较一个不那么详细的模型来说,一个过于详细的模型要难于理解。简单(见下文)同样是影响易懂性的一个因素。

  敏捷模型是足够正确的。模型通常都不需要100%正确,只要足够正确就行了。举个例子,如果一张街道地图漏画了一条街道,或是它标示某条街道是通行的,但你发现它已经关闭维修了,那你会不会扔掉你的地图开始在城里飙车犯罪呢?不太可能。你会考虑更新你的地图,你可能会拿出笔来自己做修改或是去当地的商店买一张最新版的地图(你原来的那张过期了)。也许你还是会接受那张虽不完美仍可使用的地图,因为它对你来说已经足够好了。你还是可以用这张地图四处转转,因为它还是个正确的模型,标记出了大部分街道的位置。你在发现这张地图不正确的时候,你没有立刻扔掉它,原因是你根本不在乎它是否完美。类似的,当你在需求模型、数据模型中发现错误的时候,你也会选择更新或是接受--虽不完美但已经足够好了。有些项目成员能够容忍这种不正确而有些则不能:这取决于项目的特性,每个团队成员的特性,组织的特性。充分正确性既和模型的听众有关,也和你要处理的问题有关。

  我十几岁的时候在一家餐馆打工,开始是洗盘子,到后来升为出纳。在我做出纳时,我要学习如何关店。关店最重要的一件事就是清点钱柜。教我的那个店长认定一定要把钱柜的每一分钱都点清楚,如果和销售的数字有出入的话,就要重复点。因为不但要点清纸币,还要点清硬币,所以平均每晚都要花20分钟的时间。几个月后,她调到了另一家店,换了一位新店长。一天晚上我们一起关店,他看见我在一个一个的数硬币,他非常的吃惊。他马上演示了一下他是怎么数硬币的,他把同类的硬币抓在手上,看了看,猜了一个数值,只要它们的总价值在几美元之内,他就继续这样点。20分钟的时间被压缩到5分钟,因为他认为数字只要接近就可以了,并不需要非常的精确。他每天可以提早15分钟回家,按照一周6个晚上,一年50周计算,他一年能够节省75个小时,而他的职业生涯已经30年了

  敏捷模型是足够一致的。一个敏捷模型并不需要和自己(或其它有用的artifact)保持完全的一致。如果一个用例在它的一个步骤中显式的调用了另一个用例,那么相应的用例图需要用UML的 <<Include>>版型来标记这两个用例之间的关系。然而,你看了看图表,发现它们并没有这样做,天哪!用例和图之间不一致!危险!太危险了!红色警报!快逃命呀!等一下,你的用例模型是有不一致的地方,但也没到世界末日啊。是的,理想情况下,你的所有artifact最好是能够完全一致,但这通常是不可能的。当我开发一个简单的商用系统时,我通常都可以容忍部分的不一致。但有时我是不能容忍这种不一致的。最有力的佐证就是1999年 NASA发射火星太空探测器时采用了精密的测量系统。要树立一个观点,敏捷模型只要足够一致就行了,你通常不需要使用那么完美的模型。

  关于正确性和一致性,很明显要考虑权衡问题。如果你要维护一个artifact(我们称之为“保管”),随着时间的流逝,你需要投入资源来更新它。否则它很会就会过期,对你就没用了。例如,我可以容忍一张地图标错了一两条街道,但是我绝对无法容忍一张地图中四分之三的街道都标错了。这就需要权衡了,进行足够的努力,保证artifact足够正确。过多不必要的努力反而会减缓项目的进度,而投入不足就没有办法保证artifact的有效性。

  敏捷模型有足够的细节。一张路线图并不需要标记出每条街道上的每栋房子。那会有太多的细节,使得地图难以使用。然而,在修路的时候,我想施工人员一定会有这条街道的详细地图,包括每幢建筑、下水道、电线盒等足够的细节,这样的地图才是有用的。但是这张地图并不用标记出每个院子和通向它们的路线。因为这样又太繁琐了。足够的细节和听众有关,也和他们使用模型的目的有关--司机需要的是显示道路的地图,施工人员需要的是显示土木工程细节的地图。

  考虑一个架构模型,可能一组画在白板上的图表就足够了--项目的进行中再对它们更新,也许你需要用CASE 工具来生成一些图表,也许这些图表还需要有详细的文档,这依赖于环境。不同的项目有不同的需要。在每一个例子中,实际上你都是在开发、维护一个有足够的细节的架构模型,只是这个“足够的细节”的概念和环境有关。

  敏捷模型能提供正面价值。对项目中的任一artifact,一个基本的要求是它们能够提供正面价值。一个架构模型给你的项目带来的价值是不是能够超过开发它、维护它(可选)的总成本?一个架构模型能够坚定你们团队为之努力的愿景,所以它当然是有价值的。但是,如果它的成本超过了这个价值,那就是说,它无法提供正面价值。投入100,000美元去开发一个详细的、重量级的文档化架构模型,而它的效用,只需一些画在白板上的图表就能够达到,这些图只需要花你5,000美元,看看,这是多么轻率的做法。

  敏捷模型要尽可能的简单。只要能够达到目的,你应当努力让你的模型尽可能保持简单。模型的详细程度会影响简单性,而所使用的符号范围也会影响简单性。例如,UML的类图就包括了无数的符号,包括对象约束语言 (Object Constraint Language OCL) ,但大多数的图使用符号的一部分就能够完成。所以你常常不需要使用所有的符号,你可以限制自己使用符号的一个子集,当然,这个子集是足够让你完成工作的。
  因此呢,一个敏捷模型的定义就是一个实现它的目的,没有画蛇添足的模型;为你的预期听众所理解的模型;简单的模型;足够正确、足够一致、足够详细的模型;创建和维护它的投资能够给项目提供正面价值的模型。

  一个普遍的哲学问题是源代码是不是一个模型,更重要的,它是不是一个敏捷模型。如果你是在我们这篇文章之外问我这个问题,我会回答说,是,源代码是一个模型,虽然是一个高度细节化的模型,因为它是软件的一个抽象。同时我还认为,优秀的代码是一个敏捷模型。但在这里,我还需要把两者区分开来,源代码和敏捷模型还是有区别的--敏捷模型帮助你得到源代码。

你是在敏捷建模吗?

  敏捷开发方法所面临的最大的挑战就是开发人员声称自己遵循了这种方法,而实际上他们并没有这么做。当他们运作出了问题的时候,他们就会去责备方法不好,但其实他们根本就没有真正遵循过这些方法。在极限编程(eXtreme Programming XP)就有这样的例子,一些编程狂宣称他们完全遵循XP所有的原则,但实际上仅仅是他们听到的关于XP的一小部分内容,如你不需要这么多文档。他们的产品要么质量低劣,要么根本就不能满足实际用户需求,但他们将这一切的失败都归罪于XP,真是不公平!

  我很想避免此类问题在AM上出现,但这只是一个理想。现实中,我力所能及的只是能指出你在什么情况下是在以敏捷的方式建模,什么情况下不是。 什么情况下,你是在敏捷建模?
你的客户/用户都能够积极的参与需求建模,分析建模工作。
接受需求的变化,并遵照变化了的需求行事——不存在“需求冻结”。
根据Project Stakeholder定下的优先级顺序,首先解决优先级最高的需求;在随后的工作进展中,集中解决风险最大的问题。
你采用迭代、递增的方法建模。
你的精力主要集中于软件的开发,而不是文档或模型本身。
注重团队协作精神,欢迎任何人提出意见或建议。
尽可能地做到简单——使用你可以获得的最简单的工具;使用能够胜任的最简单的模型。
随着开发的进展,你的大部分(不是所有的)模型都会被丢弃(而不作为正式的文档保存下来)。
客户/业务组织拥有业务决定权,开发人员拥有技术决定权。
大家都一致认为模型的内容比内容的格式/表现手法要重要的多。
在你建模的时候,需要不断考虑的一个关键问题是你该如何测试该模型所描述的内容。
什么情况下,你不是在敏捷建模?
你的目的是产生文档,例如需求文档,并且,这些文档需要一个或几个Project Stakeholder签字认可。
你使用CASE工具进行软件的架构搭建和设计,但却没有进一步在此基础上自动地产生部分或全部的代码。
你的客户/用户和你一起工作的时间很有限。比如,他们加入项目需求的初始开发阶段,但时间非常有限,只是回答一些问题;然后就会撤出;然后在迟些时候再参加一个或几个对你工作的接受审查。
你一次只专注于一种模型。最常见的例子就是“用例建模会议”,“类建模会议”或是“数据建模会议”。这种问题产生的最根本的原因就是“单一的artifact开发者”。例如专门进行数据建模的人,专门进行用户界面建模的人。而在AM中,每个人都是多面手,都能够胜任这项工作。
你工作的方式是做好一个模型以后再做下一个模型-也就是说,你是采取一种“serial”的方法工作。
你所在小组仅负责模型的设计或文档的编写,当完成这些模型或文档后再将其交付给另外的小组进行下一步的开发工作。也就是说,你是以一种“serial”的方式来“传出(handing off)”你的工作。

敏捷建模何时是有(没有)意义的?

何时合适AM运作?

但这不是我的情形...

何时不合适AM运作?

何时敏捷建模是适合你的?

  敏捷建模不是万能的,它并不适合于每一个人、每一种情况。即使是你的条件非常适合于AM,也不能保证它就能良好的运行——你在组织内实施AM时还是有可能犯下错误。我的经验是,在以下条件满足时,AM极有可能发挥其效力:

  软件开发采用了敏捷方法。AM不是一门完整的方法学,它只是某个软件开发流程中的一部分应用。要成功应用AM,你所采用的开发流程的观念必须要与AM的观念相匹配(译注:XP与AM匹配,而CMM与AM就不匹配)。否则,你只是在片面的运用AM中的几项技术,而不是真正的配置了AM。

  采用迭代式、递增式的开发方式。做为AM的两项实践,有效的沟通(communication)和反馈(feedback)要求软件开发采用迭代和渐增的方法。

  需求不确定或不稳定。Martin Fowler在他的新方法学中指出,如果你的项目就像是自然探险(大多数项目都如此),那最佳的选择就是采用敏捷方法来开发软件。当需求不明或易变时,你就应该采用一种能够适应这种情况的开发方式。AM拥抱变化,它采用递增的开发方式,寻求快速的反馈,并且一贯坚持Project Stakeholder的积极参与,这就是AM对付需求变化的法子。使用AM,你就能够迅速而有效地发觉客户的需求。

  开发软件是你的主要目标。这是AM的核心原则之一,但对很多项目来说,这并不是他们的目标。例如,有时候一个项目团队的主要目标是从客户那里圈钱(在外部采购中时有发生),或是简单的制定系统规范,因为系统要交付给另一个团队去实现。更糟的是,一些项目仅仅是出于政治上的考虑,它的目的就是让别人感到他们正在做这件事,至于要做出来什么东西却根本没有考虑。软件开发的目标应该是生产出满足客户需求的有效的软件系统-如果你的目标不是这样,AM就不适合你。

  你需要有stakeholder的积极支持和参与。Fowler同样认为,敏捷软件开发工作要想成功,需要有project stakeholder的积极支持和参与。Project Stakeholder是那些受软件项目的开发和/或部署潜在的影响的人。包括直接用户,非直接用户,经理,高级经理,操作人员,支持人员,测试者,和这个系统有关(整合或交互)的其它系统的开发人员,以及维护人员。AM要想成功,你需要了解你的Project Stakeholder是谁,你还要能够和Stakeholder保持日常的接触,Stakeholder能够及时的为你提供信息、做出决策。此外,还应该要有管理层的大力支持。

  开发团队能够自我决策。敏捷软件开发,特别是敏捷建模,对大多数的组织来讲都是新生事物。接受敏捷方法对大多数的组织来讲都是一件很难的事,因为它对大多数人来说都是一种新的工作方式。想要成功,我的经验是,不管成功还是失败,要根据项目团队的优点,给他们机会。鼓励他们尝试新技术,给他们资源(包括时间),让他们开始学习。应该尽量杜绝玩弄权术的现象,这就意味着组织中的管理层和一些部门要改掉原来的做法。

  要有真正的AM斗士。不论何时,接受新的事物总会面临着挑战。人们不愿意改变,他们喜欢、他们也习惯了以前那种繁琐的慢节奏的工作方式。他们和你看事情的角度不同,你希望引入敏捷方法来解决问题,而他们不认为那是问题。也许他们会改进自己喜爱的开发方法,但这些方法和AM格格不入。也许,AM威胁到他们在组织内的权力分配。即使不考虑这些情况,也还是会有人抗拒变化。要想成功的改变,必须要有真正的AM斗士。他们支持AM,为AM奋斗,他们愿意去收集Project Stakeholder的支持,他们愿意进行AM思想的宣传和培育,让AM能够在组织内生根发芽。改变需要时间,这些斗士就是在争取时间。

  你需要负责、主动的开发人员。Fowler指出敏捷软件开发强调开发人员的纪律性,要求开发人员能够协同工作,开发高质量的软件。这意味着你需要一个健康的团队环境,人们能够相互信任,相互帮助,共同迈向成功。和那些敏捷开发方法的诋毁者告诉你的完全相反,你需要的人并不是一个个都要求能上天入地。根据我的经验,你的要求很简单:愿意完成工作,有合作精神,能够有效工作。

  你需要有足够的资源。你现在知道了敏捷建模需要人们的紧密合作。就是说你需要“协同开发的空间”,例如能够使人专注于工作的建模室,一堵能够演示模型的公用墙,最好还能给每对开发人员配置一台共享工作站。除此之外,你还需要有足够的建模工具,例如白板、索引卡片、标记、和其它必须的CASE工具。我就曾经看过由于基本资源(像样的椅子,桌子,食物,饮料,和高端工作站)的缺乏,使软件开发工作的进展受到阻碍。如果你的项目团队因为这些锱铢之事导致失败,那我倒是想问问你这项目对你的组织是不是真的那么重要。如果不是,那就cancel它,把你的精力投入到其它更有价值的项目中去吧。
但这不是我的情况....
  那么,当其上的一个或某些条件和你目前的情况有出入的时候,你该怎么办?试着去改变你目前的情况。缺乏AM的拥护者吗?那你自己就可以成为一个拥护者。不允许以迭代式和递增的方式工作,和你的老板们谈谈,让他们相信这是一种更好的工作方式,要求他们给你一个机会去证明。没有足够的资源?和你的头儿说明它们的重要性。如果你已经尽力改变了所有的情况,但是仍然还有一些条件是你无能为力的,你可以试试以下的选择:

  部分的接受AM。你可以尽可能多的接受AM中的原则和实践,虽然你不会真正的实现AM,但你极有可能成为高效率的开发人员。一旦你的组织发现这确不失为一个软件开发的好方法,那就有可能会主动的去改变一些必需的要素,从而完全的接受AM。一言以蔽之,循序渐进的实现AM。

  放弃让你的组织接受AM。从我个人的角度,我并不喜欢这种选择,但我不得不承认它是个正确的办法。现实就是这样,AM并不是适合每一个人的,也许你的组织确实是不适合接受AM的。

  跳槽。看看外面的世界,还是有很多的组织期望能够从软件开发的竞赛中获胜,他们希望能有积极主动的软件开发人员加盟。

  何时敏捷建模是不适合你的?
  我猜想敏捷建模在遭遇如下的情形的时候你会陷入麻烦:

  不满足以上列出的一个或多个条件。

  你的组织文化适合采用传统的开发流程。许多组织对采用敏捷的软件开发方法毫无兴趣,他们对目前的状况感觉很好。这主要是那些政府部门、大型的公司(银行、保险公司、电信公司)以及专门为这些组织服务的咨询公司。这并不是说在这类组织中实施AM完全不可能,但要获得成功则要付出超乎寻常的努力才行。

  你的团队很大,分布在各地。只有那些在同一个工作区域中相互协作的团队才能很好的进行敏捷建模的工作,特别是当开发人员在一个公共的工作室内合作的时候(通常我们称之为“tiger team”房间)。你可以尝试着在一个大型或分散的团队中应用敏捷方法,我在架构建模中讨论了这种情形,但你很快会发现你需要挑战这种方式带来的沟通问题。
我不同意将敏捷方法应用于性命攸关的系统上,例如航空管制系统或医疗监测系统,因为我并没有相关的项目经验,无法去研究AM应该怎样应用于这些系统。同样,我也没有嵌入式系统的相关经验,因此我也没有机会将AM技术应用于这种类型的项目。我很怀疑AM是否能够试用于嵌入式软件开发,这是我研究的一部分。我很期待你在敏捷建模的邮件列表中告诉我相关的经验。(详情请访问http://www.agilemodeling.com/feedback.htm)

AM的实践是如何组合的

AM的实践之间是相互促进的,因为他们彼此支持,彼此激发。为了使AM更有效率的工作,你需要了解它的实践是如何组合的。图1显示了AM的实践之间的关系,它们被分为七类。AM的核心实践集中在头四种类别中-验证,迭代和递增,团队协作,和简单,你需要完全接受它们才能真正理解敏捷建模。然后,才轮到属于辅助实践的文档,动机,生产率这三个类别。我们先针对核心实践的四个类别,讨论各类中的实践之间的关系,然后我们再针对辅助实践的三个类别,研究各类中实践之间的关系,最后我们来讨论类别之间的关系。

图1. AM的实践是如何组合的。

clip_image002

核心实践

  在团队协作类别中有四项实践--stakeholder的积极参与,和他人一起建模,公开展示模型,和集体所有制
stakeholder的积极参与对你的成功至关重要,因为你正是为了这些project stakeholder开发系统,正是为了了解和实现他们的需求。换言之,你需要和你的甲方们密切合作,这就自然的想到了和他人一起建模--这个“他人”也包括你的stakeholder。当你的建模工作有多人参加时(至少一个project stakeholder和一个除你之外的开发人员),你就需要和众人共同协作,相互促进,取长补短。一个擅长于业务过程建模和业务规则定义的敏捷建模者看不到的方面,一个精通结构化建模技术(例如UML类图或数据模型)的人极有可能看得到。一样的道理,系统的直接用户给你的团队提供的信息极可能是高级经理提供不了的。所以,要有这样的观点:你要在项目甲方和开发团队中营造一种积极参与的氛围,只有这样,才能够收集各种不同的观点和经验。集体所有制能够提升协作性,因为一个人单独的进行建模的工作,他很快就会遇到瓶颈,而如果每个人都能够为建模工作献计献策,那么你们就能够成为一个团队,轻易的解决问题。公开展示模型能够使得人们对模型“瞻前顾后”变得容易了,能够立刻考虑模型传达的信息,从而提高团队的协作性。当然,我们是假设模型都在众人的视线之内,或者这些模型都是大家目前正在开发或相关的。这方面的主题我在Organizing an Agile Modeling Room中有详细的讨论。

  迭代和递增类别中包括了使用合适的artifact,并行创建模型,切换到另外的artifact,小增量建模这几项实践。不论哪一个artifact,都有它的长处和短处,任何一个单独的模型都不足以充分的描述你的项目的各个主要方面(如需求、架构)。例如你会发现你为了识别系统的需求,常常需要组合使用用例、业务规则定义、和技术需求定义。单单靠用例就能令project stakeholder立马告诉你他们所有的使用需求吗?这恐怕不太可能。你可以试试换用诸如业务规则之类的artifact来捕获他们的所有业务政策,再换用诸如技术需求的artifact来捕获他们的非功能需求。否则,他们会想起点什么就告诉你什么,还会返回去讨论先前提过的细节,甚至是改变他们的原来的主意。你的需求识别工作通常是一个动态的过程,分析、架构、设计工作也是一样。 我相信物力论中指出了人们以此种方式思考的原因,我们思考的方式明显是杂乱的。敏捷建模者要认识到人们是以一种动态的方式在思考,特别是人们处于群体行为时,这样才能制定对策。敏捷建模者并行创建模型,从而能够最广泛的收集信息。这项实践又是由实践切换到另外的artifact和小增量建模来支持的--你可能正在使用用例来捕获使用需求的相关信息,而当stakeholder开始讨论他们对编辑屏幕的要求时,你最好是使用基本用户界面原型或是传统用户界面原型来记录这些需求。你需要在不同的artifact之间来来回回,每一个artifact最好都需要编码来验证,这种方式可以由小增量建模的实践来实现--最典型的方式就是在这个artifact上做一会儿工作,再换一个artifact,依此类推。

  简单这个类别包括了创建简单的内容,简单地建模,使用最简单的工具这几项核心实践。创建简单的内容和简单地建模这两个实践集中于模型的简单性,在建模过程中这两项实践通产是密不可分的。把精力集中在如何简单描述,建模者常常会发现一些使得你手头的模型简单化的方法。举个例子,我曾经参加了一项存储层软件的开发工作,软件的概念类似于EJB的persistence container,封装了领域对象的一些存储操作。结果我们架构和设计非常的复杂,我们试着找到一种办法:建立一张简单的图表,帮助开发人员理解如何使用存储层来工作。其间我们还发现重构能够使我们的设计易于理解。实践使用最简单的工具能够使得过程变得简单。工具越简单就越容易使用,这就降低了他人在你的模型上工作的门槛,也就增加了实际中别人去这么做的机会,这也包括了你的project stakeholder。通过使用最简单的工具,简单描述模型也变得更自然了。此外,当你使用一些简单/低精度的工具,例如索引卡片,即时贴,白板的时候,你就能亲身体验这些简单工具的效力,你在不知不觉中已经强化了一些概念:最简单的解决方法实际上也能非常有效,对你正在开发的系统采用简单的设计。

  验证类别包含了两个核心实践:测试性思维和用代码验证。有一条哲学,我常从中受益:“如果你无法测试它,你就不应该建立它,而你建立的一切都应该加以测试。”这使得我在系统建模时就考虑测试,也使得我积极的去获取我的模型的反馈--实际上,我把该条哲学归纳为“考虑你创建的所有artifact的可测试性,以及验证所有种类的artifact。”但这并不仅仅局限于AM的范围之内。通过这种可测试性的考虑,在我建模时,我能够建立起可测试的模型,而且积极的通过编码来验证模型,这样,我就能够尽快的证实我的模型是真正可以测试的。

辅助实践

  文档类别包括了三项辅助实践:丢弃临时模型,合同模型要正式,非到万不得已不更新。在项目的进行中,需求、对需求的理解、以及对可能的解决方案的理解,都在不断变化(回忆一下原则拥抱变化)。为了反映这种变化,你需要同步改进你大部分的项目artifact,包括模型和文档。就像在敏捷文档讨论的那样,比较好的方法是,不到万不得已不要更新你的模型和文档,这种做法才算是敏捷方法。遵循这项实践,如果你发现一个模型如果不再需要更新,那就是说这个模型对你的团队已经没什么价值了,一个没有价值的模型就可以视为临时模型,可以丢弃。不过,要注意合同模型。它定义了你的系统和其他系统之间的接口,不太可能经常改变,因为它们的重要性是勿庸置疑的。一言以蔽之,如果你有个非合同模型不再更新,那就意味着它已经没有用了。

  为沟通建模和为理解建模这两项实践属于动机类别。实际上,这两项实践并没有太多的联系,有时候你创建模型的目的是为了研究和理解问题,有时候你的目的是为了和其他人交流你的想法,有时候你的目的包括了上述两者。就像你在图1中看到的,这两项实践常常会一起引出另两类的实践,这是下文要讨论的主题。

  最后,生产率类别中包括使用建模标准,逐渐应用模式,以及重用现有的资源这几项实践。重用现有资源这个实践要求你尽量利用他人的工作成果并从中受益,这有很多种思考方向:一种是应用模式,根据经验,我认为它是所有重用方法中最有效率的一种,因为你重用的都是其它开发人员久经验证的解决方案 (Ambler, 1999);另一种是遵循建模标准和指南,实际上,不论是标准还是指南,都能够提高你工作的一致性。是的,你可以自己写指南,有时你必须要这么做,因为你的实际环境中会有一些特别的情况。但是你只需要在Internet上稍做搜索就可以找到很多的开发指南。例如,你可javaCodingStandards找到Java的开发指南。

类别间的联系

  让我们考虑团队协作类别。简单类别中的实践增强了实践stakeholder的积极参与的效果,因为简单性消除了参与的代沟。迭代和递增类别中的实践也使得参与成为可能,尤其是并行创建模型,因为它增大了stakeholder们参加的机会。动机类别中的实践可以提高集体所有制和和他人一起建模的效果,对问题的理解和沟通通常可以激发人们的协作精神,简单类别的实践也也坑达到激发协作效果,因为它降低了参与的门槛。公开展示模型的效果可以通过生产力类别中的实践得到提高。遵循标准,应用模式的做法可以增加一致性和可读性,而重用现有的资源(例如通用架构),则给别人开了一个好头,使别人能够在你模型的基础上继续。迭代和递增类的实践可以支持集体所有制的实行,特别是并行创建模型和切换到另外的artifact,它们使得人们在适当的模型上共同开发。 简单类别的实践由另外几类的实践来辅助。使用建模标准和逐渐应用模式这两项实践支持同一种建模语言(使用标准和容易理解的模式),从而支持了实践简单地建模。文档类别的实践也可以支持简单类别的实践--只有到万不得已才更新模型,这样,你才不会给模型增加不必要的信息。才有可能创建简单的内容以及简单地建模。

  现在再来看迭代和递增类别。很明显,团队协作类别的实践支持该类的实践,由于团队的参与,针对目前的情况选用正确artifact的机会就增大了,你就可以根据需要来切换使用不同的artifact。验证类实践能够赋予你使用递增方法的勇气,特别是在你用代码验证的时候。保证你想法的易测性,你就更有把握同时操作多个artifact,并在它们之间切换,因为测试问题要求你从多个方面来看待它。文档类实践同样可以促进递增方法,特别是非到万不得已不更新。但是合同模型要正式这个实践抑止了递增方法的应用,因为你总是希望能够尽早的建立和其他系统间的接口标准。切换到另外的artifact和丢弃临时模型之间也能产生正面的效果,因为一个模型完成目的之后就把工作切换到另一个模型上去。简单类实践对这个类别也很重要,通过使用最简单的工具,你在不同的artifact间来回切换就变得更容易了,你节省了熟悉工具的时间,只把精力集中在简单的内容和描述上,你也可以较容易记住模型要传达的信息。最后,动机类实践可以令你同时进行多个建模工作,因为对于复杂的系统,你需要从多个方面去沟通,去理解,因此你需要在适当的artifact间来回切换,这样才能有效的做到这一点。

  验证类实践可由简单类实践来支持--创建简单的内容和简单地建模能使你更容易进行测试性思维。迭代和渐增类实践也能提高验证类实践。例如,在你切换到另外的Artifact时,就可能切换到源代码,这样你就可以看到模型确实可以运行。

  简单类实践可以推进生产力类实践。当你使用简单模型工作时,逐渐应用模式就更容易一些;当你简单地建模时,使用建模标准也会容易一些,而模型的简单、易懂,也会使你比较容易的重用现有的资源,例如企业需求模型或通用的架构模型。 简单类实践以及迭代和渐增类实践可以支持文档类实践的进行。文档越简单就越容易使用--如果你的文档容易理解,这样你就有把握万不得已才更新你的文档,因为你知道做到这一点很简单;文档如果很复杂,你的项目风险就很大,因为没有把握什么时候文档需要更新。很明显,非到万不得已不更新和丢弃临时模型的运作环境可以其它的实践来改善,例如切换到另外的artifact、小增量建模。

那,你想成为一个敏捷建模者吗?

个性通才还是专才?

敏捷建模者的个性

Alistair Cockburn指出:很多的方法学都定义了软件开发项目中开发人员所担任的角色,同时还定义个各个角色执行的任务,尽管入席,这些方法并没有定义这些角色最适合的人选。一个人要想成功的担任某个角色,他应当很好的适应它--虽然这并不需要人们掌握所有的技能,但人们必须要慢慢的熟悉这些技术。我的经验告诉我,要成为一个成功的敏捷建模者,下面的列出的个性是必要的:
  团队竞赛 第一点,也是最重要的一点,敏捷建模者总是积极的寻求协作,因为他们意识到他们不是万事通,他们需要不同的观点,这样才能做到最好。软件开发可不是游泳,单干是非常危险的。在敏捷的字典中没有“我”这个词。
畅所欲言 敏捷建模者都有良好的沟通技巧--他们能够表达出他们想法,能够倾听,能够主动获取反馈,并且能够把需要的写出来。
  脚踏实地敏捷建模者应当脚踏实地。他们的精力都集中在满足用户的需求上,他们不会在模型上画蛇添足,即便那双足是多么的好看。他们满足于提供可能的方案中最简单的一种,当然,前提是要能够完成工作。
好奇 敏捷建模者乐衷于研究问题,解决问题。
  凡是都问个为什么 敏捷建模者看问题从不会至于表面,而是会打破沙锅问到底。他们从不会就想当然的认为一个产品或一项技术和它们的广告上说的那样,他们会自己试一试。
  实事求是 敏捷建模者都非常的谦逊,他们从不认为自己是个万事通,所以他们会在建立好模型之后,用代码来小心的证明模型的正确。
  勇气 敏捷建模者应当愿意去计划一个想法,然后做出模型,再想办法用代码来验证。如果结果不理想,他们就会返工,检查他们的方法,或是放弃原先的想法。把你的想法告诉你的同伴,再来验证它的正确,这是需要很大的勇气的。
  根据实验 敏捷建模者应当愿意尝试新的方法,例如一项新的(或是已有的)建模技术。一般而言,他们也会接受敏捷建模开发技术,必要时,为了验证想法,他们愿意同传统的思想做斗争,例如在一个项目中减少文档数量。
  有纪律 要坚持不懈的遵循敏捷建模的实践。对你来说,你可能会在不经意间说,“加上这个功能吧,无伤大雅。”或是,“我比project stakeholder更了解。”在AM的道路上要想不偏离方向,是需要一定的纪律性的。
  如果你不具有上面列出的所有个性,那该怎么办呢,你是不是还想成为一个敏捷建模者呢?不用担心,你只需要少量的努力就能够胜任。相信我,我也没有办法做到100%的脚踏实地和实事求是,我也经常遇到沟通问题。没有人能够拥有所有的个性,大部分人都只能拥有一些个性。每个人都有不同点,这些不同点正是敏捷团队力量的源泉。某些人可能生来就好奇,另一些人的工作积极性可能比较强。人无完人嘛。

通才还是专才?

  当你要增加团队成员时,所要处理的一个至关重要的问题是你希望保持的通才和专才的比率。要回答这个问题,你需要考虑现代软件开发环境。图1是企业统一过程(Enterprise Unified Process EUP) 的生命周期。(译注:原文中并没有提供这副图,根据我的猜测,应该就是RUP的概述部分的那张生命周期图,但是因为没有取得瑞理公司的授权,所以我暂时也不便引用这张图,大家可以参阅RUP的相关资料。)图左边的EUP的工作流程暗示着软件开发的复杂--你需要进行业务建模,收集需求,分析和设计系统等等--而这还只是冰山一角。就像图中列出的那样,从先启到产品化的各个阶段,预示着在项目的过程中,不同的时间需要你集中于不同的地方,这需要不同的技能。有一个观点是很明确的,软件开发非常的复杂,任何一项工作都需要高超的技能和丰富的经验。首要的,要认识到这种复杂性是软件开发与身俱来的,而不是EUP使然的,即便你的团队采用的是XP方法,抑或是DSDM(Stapleton, 1997)方法,或是SCRUM (Beedle & Schwaber, 2001)方法,这种复杂性也还是存在的。尽管这些方法的生命周期看上去并不像EUP那样的复杂,但它们仍然需要配置管理活动,需要管理活动等等,只是它们处理问题的态度不同而已。

  很多的组织对此的第一反应就是建立一个专才的团队。专才的最基本的含义是指那些特别精通某一项任务的人,因此他们的效率也特别的高。这样一支团队,要想高效率的运作,你需要组合这些专才,让每人负责一块任务,解决之后就把手头的工作传给另一个人。这个概念就类似于“流水线”的想法,如果你是在大量的生产汽车,这种方式会非常的有效,但是以我的经验,在手工的软件中采用这种方式并不是太合适。而且,这种方式需要一个大团队的支持--如果软件开发中有N中不同的任务,你至少就需要N位专才才能满足这种方法的要求。但N是多大?20?50?100?这取决于你对专业定义的细节程度,是吧?如果你倾向于每位开发人员只处理一种artifact,那单单处理建模工作,就需要20多位的专才,在modeling artifacts essay列出了各种的artifact。如果你倾向于每位开发人员只负责一种角色,那再一个EUP的项目中也需要11中角色才能完成所有的工作流程。专才通常都很难同人合作,他们缺少谦逊的品质,意识不到其它人的专项技能能够为他的工作增添价值,他们也意识不到他们的所作所为可能为给后续的工作造成麻烦,也许他们需要返工,也许他们现在的努力会白费。关于专才的另一个问题是,即使是在他们擅长的领域,他们的技能也可能根本就没有那么精熟。IT产业的技术高变动率,导致了开发人员使用了几个月的新技术,开始熟悉它,就声称自己已经是这方面的专家了,因为和他具有同样层次经验的人毕竟不多。要建立一个专才组成的团队,这也是一个很明显的问题。

  那么,建立一支仅有通才的团队会怎样呢?每个人都对软件开发有不错的了解,但是都缺乏足够详细的必需知识,完成不了工作。项目需要那些对现阶段使用的技术和技巧都非常熟悉的人。如果你是在使用Enterprise JavaBeans (EJB),那你既需要对Java编程精通的人,也需要对EJB开发精通的人。一个使用Oracle的团队,幕后肯定有一位Oracle数据库管理专家。一个开发经纪人业务软件的团队,就需要一位能够了解股票和债券之间的细微差别的人。

  我的经验是,两种极端的方式都不可取,你应该取它们的中间点。一种方法是团队中一部分人是通才,一部分人是专才。通才能够起到团队的连接剂的作用,通才注重远景,专才注重项目的具体的难点。这样做的好处是通才的长处能够弥补专才的短处,反之也是一样,由于这种平衡性,通才和专才组对能够发挥出极大的优势。一个更好的方法是团队中主要是通才,仅有一两个专才。例如,我认为我应该算是一个通才,我擅长于处理项目中各项技能之间的配合,而且还精通业务应用软件建模,以及对象存储和Java编程。我的另一位同事也是位通才,特别擅长建模,EJB开发,以及测试。还有一位堪称通才的同事则精于网络通信和Java编程。这样一支由通才组成,但又有一项或多项特技的团队,优势是很明显的,他们能够迅速的找到共同点,因为他们毕竟都是通才,而且他们之间有能够做到优势互补。它的劣势在于这种人才一般都比较稀缺,动辄都需花费10年甚至20年的时间才能够培养出这种通才,因此是很难得到的。如果你的团队中有一些这种人,那你的运气真是太好了。 要认识到新手通常一开始都是专才,这很重要。软件开发的新手面对着需要消化的大量知识,往往不知所措,这很正常。大多数人一开始一开始会把精力集中在开发的一两个方面,也许是Java编程,也许是获取用户需求,然后以这方面的经验为基础,再逐渐的拓展知识的覆盖面。随着时间的增长,经验在不断的累积,他们会慢慢的完善自己的技能树,他们会软件开发中各个技能如何配合会更加了解,同时,他们还擅长于一两门特技。 还有一点也很重要,要明白很多的开发人员的专精反而害了这些人。由于软件开发的与身俱来的复杂性,开发人员经常会落入一个名为单一artifact开发者的陷阱中去,他们把自己定位为仅仅从事一种artifact的开发工作,例如代码,用例模型,或数据模型;开发人员还可能遇到的一个陷阱名为单一角色开发者,他们的定位是专门从事一种工作的人,例如建模,测试,或编码。换言之,这些人专精于某一个角色,这种倾向在一些的采用传统过程的大型组织中特别显著,问题就出现了,这些陷阱的落入者的视野往往过于狭窄,难以在一个采用敏捷方法的软件开发项目中作到高生产率。当然,如果他们原意扩展自己的视野,这个问题就容易得到解决。 译注:想必国内的程序员看到这篇文章会很开心吧。毕竟,中国的程序员向来都是以通才自封的。但是,要注意的一点是,这篇文章是针对国外的程序员的,因为国外的程序员通产都只关注于自己的领域,例如数据库的专家对数据库非常的熟悉,但他可能对测试一窍不通。但是他们对自己领域的了解是非常不得了的。可是中国的程序员一般是万金油,哪儿需要,哪儿就有我的丰姿。只要是软件领域的,都无所不能,无所不精。但是人的精力都是有限的,不可能什么都精通。样样都精,也就是样样都庸。这个道理大家务必要了解。国内的很多程序员都算不上是通才,而只能算是庸才。这句话可能不好听,但是事实如此。如果能够意识到这一点,好,我想你已经不是庸才了,而是在往通才迈进的途中了。 本来是不打算译这篇文章的,因为担心有些人看完它后会断章取义,反而成了一项罪过。但是这篇文章的很多思想值得借鉴,再加上为了保证译作的完整性,最后还是把它译了出来,并加上了一段废话,提醒大家注意。最后,我真诚的希望中国的程序员都能够成为作者在文中提到的那种既是通才,又是专才的人。

建模的误区

走出一般性的设计误区,迈向成功之途

英文原文版权由Scott W. Ambler拥有
Original text is copyrighted by Scott W. Ambler

译者:高继荣| 原文出处:www.AgileModeling.com

  无论你遵从的是重量级的方法,比如Enterprise Unified Process(EUP),还是轻量级的开发过程,如Extreme Programming(XP),建模在软件开发中都是不可或缺的。但不幸的是其中充斥着各种谬误与迷思。这来自于各个方面,有从理论家错误的研究、数十年来信息技术领域内的文化沉积、软件工具开发商天花乱坠半的市场宣传以及象Object Management Group (OMG)和IEEE这类组织的标准。这个月,我要揭示建模中的误区,指出其相应的事实真相。

误区一:建模就等于是写文档

  这很可能是其中最具破坏力的一条,因为开发人员可以此为借口而完全放弃建模。许多优秀的软件开发人员会说他们不想把时间浪费在这些“无用的“文档上。他们沉溺于编码之中,制造着一些脆弱而劣质的系统。另外,甚至于许多尽责的开发人员现在也认为建模是一件讨厌的事,而不愿去学习相应的建模技术。

  事实分析:“模型”与“文档”这二者在概念上是风马牛不相及的—你可以拥有一个不是文档的模型和不是模型的文档。一幅设计图就是一个模型,而不论是被画在餐巾纸的背面,或写在一块白板上,或在Class Responsibility Collaboration(CRC)卡片中,还是根据记录在报纸和便签纸上的流程图而生成的一个粗略的用户界面原型。虽然这些都不能说是文档,但他们却都是有价值的模型。

  建模很象是作计划:作计划的价值在于计划编制的过程中,而非计划本身;价值体现在建模的活动中,而非模型本身。实际上,模型不是你系统中的一部分正式的文档,而且在完成它们的使命后可以被丢掉。你会发现值得保留的只有很少的模型,而且它一定是非常完美。

误区二:从开始阶段你可以考虑到所有的一切

  这种说法流行于二十世纪七十年代到八十年代早期,现今的许多经理都是在那个时候学习的软件开发。对这一点的迷信会导致在前期投入可观的时间去对所有的一切建模以期把所有一切都弄正确,试图在编码开始前就“冻结”所有的需求(见误区四),以致于患上“分析期麻痹症” – 要等到模型非常完美之后才敢向前进。基于这个观点,项目组开发了大量的文档,而不是他们真正想要得到的—开发满足需要的软件。

  事实分析:怎么才能走出这个误区呢?首先,你必须认识到你不能考虑到所有的细枝末节。第二,认识到编码员可能会对建模者的工作不以为然(这是可能的,事实上建模者所作的工作在实际价值中只占很少的部分),他们或许会说模型没有反应出真实的情况。第三,认识到不管你的最初所作的规格说明书有多好,但注定代码会很快地与之失去同步,即便是你自己建模自己编码。一个基本的道理就是代码永远只会和代码保持一致。第四,认识到迭代法(小规模地建模,编一些代码,做一些测试,可能还会做一个小的工作版本)是软件开发的准则。它是现代重量级的软件开发过程(如EUP),以及轻量级(如XP)的基本原理。

误区三:建模意味着需要一个重量级的软件开发过程

  走入这个误区(经常与误区一有联系)的项目组常常是连建模都彻底地放弃了,应为这样的软件开发过程对他们来说太复杂太沉重了。这不亚于一场天灾。

  事实分析:你可以用一种敏捷的方式取而代之。关于用简单的工具进行简单地建模的详细内容可参看Agile Modeling(AM)。而且,你可以丢弃你的模型当使命完之后,同样也可以很基本的方式进行建模(比如,从办公桌起来,来到白板前就开始构略草图)。只要你愿意,你就可以轻松地建模。

误区四:必须“冻结”需求

  这个要求常常来自高级经理,他们确切地想知道他们从这个项目组能得到什么东西。这样的好处就是在开发周期的早期确定下需求,就可以确切地知道所要的是一个什么样的东西;缺点就是他们可能没有得到实际上所需要的(不全或错误的需求,译者)。

  事实分析:变化总会发生的。由于优先级的变化和逐渐对系统有了更进一步的理解,都会引起需求的变化。与冻结需求相反,估计项目成功的风险,尽量去接受变化而且相应地采取行动,就象XP所建议的一样。

误区五:设计是不可更改的

  如同误区四,要求每一个开发人员必须严格遵从“设计“,导致开发人员为了符合“设计“而作了错误的事情或以错误的方式作正确的事情。或者是简单地忽略了设计,否定了所有设计可能带来的好处。冻结了设计,你就不能从在项目进程中所学到知识进一步获益。另外一个很大的趋势就是开发出大量的文档而不是实际的软件,使用面向文档的CASE工具而不是能给项目带来实际价值的面向应用的工具。

  事实分析:事实上,设计会经常根据开发人员和数据库管理员的反馈进行修改,因为他们是最接近实际应用的人,通常他们对技术环境的理解要好于建模者。我们必须的面对这样一个事实:人无完人,他们所作的工作也不可能尽善尽美。难道您真的想将一个并不完善的设计固定下来而不再去修改其中的错误吗?另外,如果需求并没有被冻结,其实就意味着你不能冻结你的设计,因为任何需求的修改势必影响设计。对之,正确的态度是:只要你的代码还在改动,涉及就没完。

误区六:必须使用CASE工具

  建模常常被认为是一项复杂的工作,因此需要大量地使用CASE工具辅助进行。

  事实分析:是的,建模可以是很复杂的。但你完全可以建立一个有效而简单的模型表述其中关键的信息,而不是将一些无关紧要的细节包括进来。

  比如,我经常使用UML建立模型来表示类、它们的属性及一些关键的业务操作,但并不画出属性的存取操作(get和set),以及维护与其它类关系的框架代码,或者其他一些琐碎的实现细节。我通过建模寻找解决问题的方法,让我和我的同事能继续前进去实现这个模型。以这样灵活的方式,大多数情况下我并不需要一个CASE工具来支持建模工作,一块白板,或者一台数字相机足以。这样,我就不用花时间去评估CASE工具,不用去和工具供应商讨论许可证的问题,也免去了人员培训开销。CASE工具只有当它能体现最佳性价比时(相对你自己的情况而言),才值得购买。大多数情况下,我都能不用它而达到目的(完成建模)。我经常使用的工具有Together/J(http://www.togethersoft.com/) – 因为它能产生数目可观的Java框架代码;还有ERWin(http://www.cai.com/) -- 因为它能规划数据库。这两个工具真正地帮助我实现了软件开发的目的 – 制造满足用户要求的软件。但我绝大多数得建模工作仍然使用的是简单的工具,而不是CASE工具。

误区七:建模是在浪费时间

  许多新手都这样认为,这主要是因为他们所接受的教育仅仅局限于如何编写代码,对于完整的开发流程鲜有接触。而且他们的经验也仅限于如何实现代码,就如初级程序员。他们放弃了提高效率和学习技能的机会,这些技能能够使他们很容易地适应不同的项目或组织。他们应该为此感到羞愧。

  事实分析:在大多数情况下,在开始编码之前画一个草图、开发一个粗率的原型或者制作一些索引卡片都能提高你的生产效率。高效的开发者在编码之前都要进行建模工作。另外,建模是一种很好的在项目组成员与项目负责人之间沟通途径。你们在这个过程中探讨问题,从而对所要的是一个什么样的东西可以得到更好的理解,涉及到该项目中的每个成员也可得到对该项目有一个从分的了解。

误区八:数据模型(Data Model)就是一切

  许多组织基于数据模型就蹒跚启动新的开发工作,也许正如你所在的组织:IT部门对于数据有非常严格的规定,控制着你的开发项目;或者你以前的数据库是一团糟,别无选择。

  事实分析:数据模型是一个重要的但不是最重要的建模,它最好是建立在另外的模型之上。(参见“Extreme Modeling”,Thinking Objectively,Nov.2000)。这即使在象数据仓库这类面向数据的项目中也如此。如果没有很好的理解用户是如何使用该数据仓库的(在数据模型中没有表示出来),这些项目经常是以可悲的失败而告终。你可以使用的模型有很多 – 使用案例(use cases),业务规则(business rules),activity diagrams,类图(class diagrams),component diagrams,用户界面流程图(user interface flow diagrams)和CRC,等等。数据模型仅仅是其中的一种。每种模型都有其长处和短处,应该正确地使用。

误区九:所有的开发人员都知道如何建模

  我们现在面临照这样一个严重的问题:许多不是开发人员的人,包括高级经理和用户,不知道软件是如何建成的。其结果,他们不能够区分开熟练的开发者和一般的程序员(当然也分不清高级程序员和一般程序员),他们想当然地认为所有的开发人员都具备从头到尾开发整个系统的技能。

  事实分析:这肯定是不正确的。建模的技能,是只有当一个开发者通过学习它,并经过长期的实践才能够掌握。一些非常聪明的程序员常常相信自己无所不能,毕竟他们终究只是程序员。正因为这样的狂妄自大,他们承当的一些任务是他们根本就没有相应的技能去完成的。软件开发是如此的复杂,单单一个人是很难具备所有的技能去成功地进行开发,甚至也不可能去配置有一定复杂程度的系统。开发这应该有自知之明,明白他们自己的弱点,学无止境。通过互相取长补短,建模者可从程序员身上学到一项技术的具体细节,程序员也可从建模者那里学到有价值的设计和体系结构的技术。我个人认为所有的人,包括我自己,都是新手。

 

二、XP方法概述

什么是XP
XP 实际上是一种经历过很多实践考验的一种软件开发的方法,它诞生了大概有5 年,它已经被成功的应用在许多大型的公司,如:Bayeris che Landesbank,Credit Swis s Life,DaimlerChrysler,First Union National Bank Ford Motor Company and UBS.XP 的成功得益于它对客户满意度的特别强调,XP 是以开发符合客户需要的软件为目标而产生的一种方法论,XP 使开发者能够更有效的响应客户的需求变化,哪怕在软件生命周期的后期。  同时,XP 也很强调团队合作。团队包括:项目经理,客户,开发者。他们团结在一起来保证高质量的软件。XP 其实是一种保证成功的团队开发的简单而有效的方法。
XP 强调四种价值:交流,简易,回馈,勇气。XP 程序员之间紧密的相互交流,XP 程序员也和客户紧密的交流。他们总是保持他们的设计简单明了。项目一开始,XP 就强调通过对软件的不断测试来获得反馈,程序员尽可能早的把软件交给客户,并实现客户对软件需求提出的变化,有了这些基础,XP 程序员就可以自信的面对需求和软件技术的变化。
XP 是与众不同的,它有点象快步的舞蹈。XP 开发过程包括许多的小卡片,独立的看,这些小卡片没有什么意义,但是当它们组合在一起,一幅完整的美丽的图片就可以看见,XP方法有别于传统软件开发,它是软件开发的一种新的重要的发展。它改变了我们开发程序的传统思维方式。下面我们将介绍它带给我们那些改变。
第二问题:XP 带给我们的变化  通过软件工程设计的简单而优美的软件并不比那些设计复杂而难以维护的软件有价值。这是真的吗?XP认为事实并非如此。
  一个典型的项目花在人力上的金钱是花在硬件上的时间的20 倍,这意味着一个项目每年要花200 万美元在程序员身上,而仅仅花10 万美元在电脑设备上。很多聪明的程序员说:“我们如此聪明,发现一种方法可以节省20%的硬件开销”,然后他们使得源程序大而且难懂和难以维护,他们会说:“但是我们节省了20%或者2 万美元每年,很大的节省”。反之,如果我们写我们的程序简单而且容易扩展,我们将至少节省10%的人力开销,一笔更大的节省,这是你客户一定会注意到的一些事情。
  另外一个对客户来说很重要的问题就是程序的BUGS 。XP 不只是强调测试,而且要求正确的测试。测试必须是能自动进行的,以便为程序和客户提供一个安全的环境。在编码的所有阶段,我们不断增加测试用例。当找到bug 时,我们就添加新的测试,一个紧密的安全网就这样产生了。同一个BUG 不出现两次,这些一定会引起用户的注意。你的客户必须注意的另外一件事情:XP 开发者拥抱需求变化。XP 使我们能够接受需求的变化。
  一般情况下,客户只有在系统被开发完成以后能真正去体会它。XP 却不一样,它通过加强客户的反馈来缩短开发的周期,同时获得足够的时间来改变功能和获得用户的认同。在XP 中,你的客户应该明确的知道这一点。
XP开发过程的大多的革命是在软件开发的方法上,代码质量的重要程度超出人们一般所认为的。仅仅因为我们的客户不能明白我们的源代码并不意味着我们可以不努力去管理代码的质量。
第三个问题:我们什么时候用XP
XP方法的产生是因为难以管理的需求变化,从一开始你的客户并不是很完全的知道他们要的系统是怎么样的,你可能面对的系统的功能一个月变化多次。在大多数软件开发环境中不断变化的需求是唯一的不变,这个时候应用XP 就可以取得别的方法不可能取得的成功。XP 方法的建立同时也是为了解决软件开发项目中的风险问题。假如你的客户在特定的时间内,需要一个相当难开发的系统,而且对于你的项目组来说,这个系统是一个新的挑战(从来没有做过),那风险就更大了,如果这个系统对于整个软件行业来说都是新的挑战,那么它的风险就更大了,采用XP 将可以减少风险,增加成功的可能。
XP方法是为小团体开发建立的,在2-10 个人之间。假如你的团体恰好合适,你就不需要用其他的软件工程方法了,就用XP ,但是要注意你不能将XP 方法应用于大团体的开发项目中。我们应该注意,在需求一惯呈动态变化或者高具有高风险的项目中,你就会发现XP 方法在小团体的开发中的作用要远远高于在大团体的开发。
XP方法需要一个扩展的开发团体,XP 团体不仅仅包括开发者,经理、客户也是其中的一员,所有的工作一环扣一环,问问题,商讨方法和日程,增加功能测试,这些问题的解决不仅仅涉及到软件的开发者。  另一个需要是可测试性,你必须能增加自动的单元测试和功能测试,然而在你进行这个需求的时候,你会发现有许多的问题很难测试,这需要充分发挥你的测试的经验和智慧,而且你有时还要改变你的设计以便它可以更容易的进行测试。记住:那儿有需求,那儿就应该有测试的方法。
  在XP方法的好处的清单上,最后一条是生产力。在同样的合作环境下,XP 项目都一致的表现出比使用其他方法高的多的生产力。但这从来不是XP 方法学的真正目标。XP 真实追求的目标是:在规定的时间生产出满足客户需要的软件。假如对于你的开发来说,这是很重要的方面,你就可以选择XP 了。

XP设计原则

1 简单是关键
  简单的设计总是花较少的时间完成复杂的任务。因此记住要总是做有可能完成的最简单的事情。如果你发现一个事情很复杂,用简单的事情替换它。与其在复杂的代码上消耗更多的时间,还不如用简单的代码替换,这样更快,而且更省事。尽可能使事情简单化,在执行计划期间,尽可能不增加新的功能。要记住保持简单的设计是长期坚持的工作。
2 选择系统比喻
  选择系统比喻是为了通过对类和方法的一致命名,为团队提供一致的画面。你的对象名称对于系统整体设计的理解和代码重用是非常重要的。如果能取一个名字,使它容易猜出来,并且还是正确的,那么你就是一个能节约时间的人。选择一个涉及广泛的对象命名的方法而没有详细描述,很难获取对于系统的认识。
  例如克莱斯勒汽车公司薪水支付系统象生产线那样被建造了。福特汽车销售系统象材料清单那样被构造了。使用基于你本身领域的美丽的比喻,使它为人所知。但是,除非它相当简单,别选美丽的比喻。
3 CRC 卡片
  使用CRC(Class,Responsibilities,Collaboration)卡片来作为团队的设计。CRC卡的最大价值在于允许人们从思考过程模式中脱离出来,更充分的专注于对象技术。CRC卡允许整个项目组对设计做出贡献。参与系统设计的人越多,能够收集到的好主意也就越多。
  个别的CRC卡被用来描述对象。对象的类写在卡的最上边、责任在左边下边编成表,每个责任的右边是协作的类列表。因为以前CRC会议是大家全力参与的,通常只需要很少的有类名的卡片,实际上没有写出完整的卡片,所以,我们说“要写”。这个例子显示咖啡制造商问题的一部分。
CRC会议继续进行,一些人模拟系统和对象交流,把消息传给其他的对象。通过一步步处理弱点,问题很容易地被解决。设计选择可以通过做建议的模拟实验迅速地做出决定。
  如果你发现很多人在讲话并且移动卡片,立即简单的限制站着的人数,把卡片移到下一人。当一个人座下的时候,其它的人可以站起来。自由讨论会议这项工作,常常发生的情况就像当艰难的问题最终被解答的时候,开发组变得吵闹一样。
CRC卡受到的最大的批评之一是缺乏有记录的设计。CRC卡使设计看上去显而易见,所以这通常不需要。应该要求永久性的记录,每一个类一张卡,每个卡作为文档详细记录和保留。一旦预想已经建立和运行,在一段时间内,设计被人记住了。
4 道钉解决方案
  为了对技术或设计的难题的做出解答,产生道钉解决方案。道钉解决方案是一个探究潜在解决方案的非常简单的程序。构造一个系统,仅仅专注于检查出现的问题,忽略所有其它的方面大部分的道钉不会保持的足够好,所以,要打算扔掉它。使用道钉的目标是减少技术性问题的风险,或者,增加用户故事 评估的可靠性。当技术性的困难阻碍系统开发的时候,在这个难题上配备一对开发者,给上一星期或两个星期的时间以减少潜在的危险。
5 不要过早地增加功能
  保持系统的整洁,把你猜想的额外的素材以后加入。只有10%的额外素材得到使用,所以你在浪费90%的时间。因为我们明白怎样增加,或者我们想使系统变得更好,所以我们都被诱惑到现在增加功能而不是以后。现在增加功能,似乎更快。但是,我们需要常常提醒自己:实际上我们不需要它。额外的功能将总是使我们开发变慢,而且浪费我们的资源。把眼光放到将来的需求和额外的适应性上。只关注于今天的进度。

6 及时地重新构造
  在软件变的不适用以后很长时间,我们的算机程序员仍然保持设计不变。我们继续使用和重用很长时间没有维护的代码,因为在某种方式下还能继续工作,我们害怕修改它。但是,这样去做真的值得吗?极端编程(XP)认为不是这样。当我们除掉冗余,删除未使用的功能,更新陈旧设计的时候,我们进行 重新构造(refactor)。贯穿整个项目生命周期的重构工作节约了时间,保证了质量。
  为了保持设计的简单,避免不必要的混乱和复杂,及时重构。保持代码的简洁、明了,以便它更容易理解、修改和扩展。确认每件事情都被表示一次,并且只一次。结果,花更少的时间产生更好的系统。
  重构有几个要点。首先,很难,因为你必须能够放开你想象中的完美的设计,接受你通过重构偶然发现的设计。你必须认识到你想象的设计只是好向导方向,实际上马上会变得陈旧。
  为了吃到大量的树叶,毛虫是一个完美的设计,但是,它不能找到配偶,它在计划找到它的同类以前,必须把自己变成一只蝴蝶。放弃你认为系统应该是什么或不是什么的想法,试着留心在你面前出现的新的设计。

采用XP方法使软件项目获得更大成功

一〉一种解决方案:灵活方法   最近发生了一些转变,从所谓的“重量型”方法转向了“轻量型”或“灵活”方法,例如 Crystal 方法、适应性软件开发和(当前最流行的)XP。所有这些过程都有这样一个事实,即需要人们共同来开发软件。成功的软件过程必须将人们的长处最大化,将他们的缺点最小化,因为优点和缺点毋庸质疑都存在。在我们看来,XP 最出色的地方在于它能够解决所有影响参加人员的互补力量。
XP 提供了十年来最大的一次机会,给软件开发过程带来彻底变革。就象 Peopleware 作家 Tom DeMarco 所说,“XP 是当今我们所处领域中最重要的一项运动。预计它对于目前一代的重要性就象 SEI 及其能力成熟度模型对上一代的重要性一样。”
XP 规定了一组核心价值和方法,可以让软件开发人员发挥他们的专长:编写代码。XP 消除了大多数重量型过程的不必要产物,通过减慢开发速度、耗费开发人员的精力(例如干特图、状态报告,以及多卷需求文档)从目标偏离。我们认识到一个称为“极端编程”的东西可能很难作为正式的开发过程推荐给管理层,但如果您的公司从事软件行业,您应该帮助管理层绕过其名称认识到 XP 可以提供的竞争优势。
Kent Beck 在他所著的 Extreme Programming Explained: Embrace Change 一书中概括了 XP 的核心价值(请参阅参考资料)。我们对它们进行了总结:
1)交流  项目的问题往往可以追溯到某人在某个时刻没有和其他人一起商量某些重要问题上。使用 XP,不交流是不可能的事。
2)简单  XP 建议您总是尽可能围绕过程和编写代码做最简单的事情。按照 Beck 的说法,“XP 就是打赌。它打赌今天最好做些简单的事...而不是做更复杂但可能永远也不会用到的事。”
3)反馈  更早和经常来自客户、团队和实际最终用户的具体反馈意见为您提供更多的机会来调整您的力量。反馈可以让您把握住正确的方向,少走弯路。

4)勇气  勇气存在于其它三个价值的环境中。它们相互支持。需要勇气来相信一路上具体反馈比预先知道每样事物来得更好。需要勇气来在可能暴露您的无知时与团队中其他人交流。需要勇气来使系统尽可能简单,将明天的决定推到明天做。而如果没有简单的系统、没有不断的交流来扩展知识、没有掌握方向所依赖的反馈,勇气也就失去了依靠。
XP 的方法将这些价值转换成开发人员每天应做的事情。这里没什么新鲜内容。多年以来,行业认识到 XP 方法是“最优方法”。实际上,XP 中的“极端”来自两方面:
XP 采取经过证明的业界最优方法并将其发挥到极致。
XP 将这些方法以某种方式进行结合,使它们产生的结果大于各部分的总和。
  这是怎样的情景呢?代码复查是个好的做法,因此始终通过成对地编写代码来做到。测试也是个好的做法,因此总是通过在编写代码之前编写测试来做到。文档很少与代码保持一致,因此只做那些最需要的事,余下的部分则取决于明确编写的代码和测试。XP 不保证人们总做正确的事,但它允许人们这样做。它将这些“极端”方法以一种相互支持的方式结合起来,显著提高了速度和有效性。
〈二〉XP 的十二种方法
XP 的十二种方法(如图 2 所示)将其定义为规则。让我们仔细研究每一个方法来对“执行 XP”表示什么有个更全面的理解。

clip_image003

图 2. XP 的十二种方法

1)规划策略
  有些人会指责 XP 是一种美其名的剽窃,只是一群牛仔在没有任何规则的情况下将一个系统拼凑在一起。错。XP 是为数不多的几种承认您在开始时不可能事事通晓的方法之一。无论是用户还是开发人员都是随着项目的进展过程才逐步了解事物的。只有鼓励和信奉这种更改的方法才是有效方法。状态限定方法忽视更改。而 XP 则留心更改。它倾听所用的方法就是“规划策略”,一个由 Kent Beck 创造的概念。
  这一方法背后的主要思想是迅速地制定粗略计划,然后随着事物的不断清晰来逐步完善。规划策略的产物包括:一堆索引卡,每一张都包含一个客户素材,这些素材驱动项目的迭代;以及对下一两个发行版的粗略计划,如 Kent Beck 和 Martin Fowler 在他们的 Planning Extreme Programming 中描述的那样(请参阅参考资料)。让这种形式的计划得以发挥作用的关键因素是让用户做企业决策,让开发小组做技术决策。如果没有这一前提,整个过程就会土崩瓦解。
  开发小组要决定:估计开发一个素材要花多长时间 、使用各种技术选项所花费的成本 、团队组织 、每个素材的“风险” 、迭代中素材开发的顺序(先开发风险最大的那一个可以减轻风险)。
客户需要决定: 范围(一个发行版的素材和每一次迭代的素材) 、发行日期 、优先级(根据企业价值先开发哪些特性)规划经常发生。这样,在客户或开发人员学习新事物的同时,就为他们调整计划提供了频繁机会。}
2)成对编程
  使用 XP,成对的开发人员编写所有产品代码。这种方式听上去好象缺乏效率。Martin Fowler 说,“当人们说成对编程降低生产力时,我回答,‘那是因为大多数耗费时间的编程部分是靠输入来完成的。’”实际上,成对编程无论在经济还是其它方面都提供了许多好处: 所有设计决策都牵涉到至少两个人、至少有两个人熟悉系统的每一部分、几乎不可能出现两个人同时疏忽测试或其它任务、改变各对的组合在可以在团队范围内传播知识、代码总是由至少一人复查。

  研究还显示成对的编程实际上比单独编程更有效(有关详细信息,请参阅参考资料中 Alistair Cockburn 和 Laurie Williams 所著的 The Costs and Benefits of Pair Programming)。
3)测试
  在 XP 中有两种测试: 单元测试 、验收测试 。
  开发人员在他们编写代码的同时编写单元测试。客户在他们定义了素材后编写验收测试。单元测试及时告诉开发人员系统在某一点上是否“工作”。验收测试告诉团队系统是否执行用户希望它执行的操作。
  假设团队使用的是如 Java 这样的面向对象语言,开发人员在为一些方法编写代码之前为每种有可能失败的方法编写单元测试。然后他们编写足够的代码使之能通过测试。有时人们会发现这有点奇怪。答案很简单。编写测试首先为您提供:一组可能最完整的测试 、可能能工作的最简单的代码 、代码意图的明确景象 。
  开发人员只有在通过所有单元测试后才可以将代码检入到源代码资源库中。单元测试使开发人员有信心相信他们的代码能够工作。这为其他开发人员留下线索,可以帮助他们理解最早的开发人员的意图(实际上,这是我们看到过的最好的文档)。单元测试还给了开发人员勇气重新划分代码,因为测试失败可以立刻告诉开发人员存在错误。应该将单元测试自动化,并提供明确的通过/失败结果。xUnit 框架(请参阅参考资料)做到的远不止这些,因此大多数 XP 小组都使用它们。
  用户负责确保每个素材都有验收测试来确认它们。用户可以自己编写测试、可以征募组织中的其他成员(例如 QA 人员或业务分析员)编写它们,也可以将这两种方法结合起来。测试告诉他们系统是否具有应该具有的那些特性,以及是否可以正确工作。理想情况下,用户在迭代完成之前就应该写好迭代中那些素材的验收测试了。应该将验收测试自动化,并要经常运行来确保开发人员在实现新特性时没有破坏任何现有的特性。通常情况下,客户需要来自开发团队的某些帮助来编写验收测试。我们对一个项目开发一个可重用的自动验收测试框架,可以让用户在简单编辑器中输入他们的输入和所期望的输出。框架将输入转换成 XML 文件、运行文件中的测试,然后为每个测试显示“通过”或“失败”。客户喜欢这一做法。
  不是所有验收测试都必须在所有情况下通过。问题是验收测试帮助客户衡量项目“完成”的情况如何。它们还可以让客户获悉有关某些事物是否可以发行的决定。
4)重新划分
  重新划分是在不更改功能性的前提下对代码加以改进。XP 小组在进行重新划分时毫不手软。
  开发人员重新划分有两个重要时机:实现特性之前和之后。开发人员尝试确定更改现有代码是否可以让新特性的开发更容易。他们查看刚刚写好的代码,看是否有方法可以对它进行简化。例如,如果他们认为有抽象的机会,就会进行重新划分来从具体实现中除去重复的代码。
XP 建议您应该编写可能运行的最简单的代码,但同时也建议您应该不断学习。重新划分让您将学到的知识加入到代码中,同时又不会破坏测试。它使您的代码简练。这意味着它可以存在相当长的时间、为以后的开发人员引入更少问题,并为他们指引正确的方向。
5)简单的设计
XP 的诽谤者说该过程忽略了设计。事实不是这样。问题是重量型方法建议您做的不过是提前完成大部分琐碎的设计任务。这就象是拍一张静态的地平线的照片,静止不动,然后尝试画一张如何到达那里的完美的地图。XP 说设计不应该在事物将保持不变的前提下预先仓促进行。XP 认为设计非常重要,因此应该是一个持续的事务。我们总是先尝试使用能够工作的最简单的设计,然后随着现实的不断显现来更改它。
  什么是可能工作的最简单的设计?它是符合以下条件的设计(感谢 Kent Beck 为我们一一列出): 运行所有测试 、不包含重复代码 、明确陈述程序员对所有代码的意图 、包含尽可能最少的类和方法 。
  对简单设计的需求并不是说所有设计都很小,也不表示它们是无足轻重的。它们只不过需要尽可能简单,但是仍能工作。不要包括不使用的额外特性。我们称这样的事物为 YAGNI,表示“您将不需要它(You Aren't Going to Need It)。”不要让 YAGNI 破坏您成功的机会。

6)集合体代码所有权
  小组中的任何人都应该有权对代码进行更改来改进它。每个人都拥有全部代码,这意味着每个人都对它负责。这种技术可以让人们对部分代码进行必要的更改而不用经过代码拥有者个人的瓶颈。每个人都负责这一事实消除了无代码所有权所带来的混乱。
  “每人拥有所有代码”与“没人拥有代码”的说法并不一样。没人拥有代码时,人们可以随处进行破坏而不必负任何责任。而 XP 说,“如果是您破坏的,应该您来弥补。”我们有一些必须在每次集成之前和之后运行的单元测试。如果您破坏了某些事物,您要负责进行修补,无论它位于代码的哪一部分。这需要极端规则。可能这是名称中带有“极端”的另一个原因。
7)持续的集成
  经常进行代码集成可以帮助您避免集成梦魇。XP 团队在一天中集成了代码几次,每次都在所有单元测试对系统运行后执行。
  传统方法工作方式如下:编写大量代码后执行一次大爆炸式的集成,然后花费相当长的时间来改正问题。这种笨拙的形式的确使项目速度减缓。大爆炸式的集成给团队立即带来大量问题,并且这些问题通常都有几百种可能的原因。如果经常进行集成,任何特定集成失败的原因都会非常明显(以前运行过测试,因此错误一定是新事物犯下的)。使用这种方法,当遇到问题时,可能的原因就相当有限。修改起来更容易,花的时间少得多,使团队保持最快速度前进。
8)现场客户   要使功能最理想,XP 小组需要在现场有一位客户来明确素材,并做出重要的企业决策。开发人员是不允许单独做这些事情的。让客户随时在场可以消除开发人员等待决策时出现的瓶颈。
XP 不会假装素材卡是开发人员交付必要代码所需的唯一指示。素材是对以后在客户和开发人员之间填写细节的对话的一项承诺。与将所有要求写在一个静态文档中不同,其思想是进行面对面的交流,减少产生误解的机会。
  我们发现让客户在现场是可能最好的一种情形,但这不是解决问题的唯一方案。底线是客户必须随时在需要回答问题和根据企业价值为团队提供指示时有空。如果客户并非在现场专职陪伴团队的情况下就能做到这些,那很好。如果能和团队待在一起,这会更方便,但只是建议而已。
9)小发行版   发行版应该尽可能地小,同时仍然提供足够的企业价值以证明它们值得。
  只要觉得有意义就可以发布系统。这样就尽可能早为用户提供了价值(请记住,今天的钱比明天的钱来得值钱)。小发行版将为开发人员提供具体的反馈意见,告诉他们哪些符合客户需要,哪些不符合客户需要。然后,小组可以将这些经验教训包括在其下一发行版的规划中。
10)一周 40 小时
Kent Beck 说他希望“...每天早晨都感到有活力有激情,每天晚上都感到疲惫而满足。”一周 40 小时工作可以让您做到这一点。确切的小时数并不重要,重要的是原则。长时间地持续工作会扼杀工作绩效。疲劳的开发人员会犯更多错误,从长期来说,将比按“正常”时间表进行的开发慢得多。
  即使开发人员可以在长时间很好工作,这也不意味着他们应该这样。最终他们会厌倦,会离开他们的工作,或者产生影响他们工作绩效的非工作问题。如果您打乱了人们的生活,将会尝到它所带来的恶果。加班并不是解决项目问题的答案。实际上,它是更大问题的症状。如果您要走向灭亡,就无药可救了。
11)编码标准    拥有编码标准有两个目的:a.防止团队被一些例如事物没有以最大速度发展这种无关紧要的愚蠢争论搞得不知所措;b.它支持其它方法。
  如果没有编码标准,重新划分代码会更加困难,按应当的频度交换对更困难,快速前进也更困难。目标应该是团队中没有人辨认得出是谁写的哪一部分代码。以团队为单位对某一标准达成协议,然后遵守这一标准。目标不是创建一个事无巨细的规则列表,而是提供将确保您的代码可以清晰交流的指导方针。编码标准开始时应该很简单,然后根据团队经验逐步进化。不要预先花费太多时间。创建能够工作的最简单标准,然后逐步发展。

12)系统比喻   体系结构是做什么用的?它提供了系统各种组件以及它们是如何交互的画面 -- 一种映射,可以让开发人员了解新的代码部分适合放在哪里。
XP 中的系统比喻与大多数方法称作的体系结构差不多。比喻为团队提供了一致的画面,他们可以用它来描述现有系统的工作方式、新部件适合的位置,以及它们应该采取的形式。
  重要的是要记住,关键要让每个人理解系统是如何组合在一起的,而不是美丽的比喻。有时您就是无法想到一个好的比喻。想到时就太好了。
〈三〉一起工作的方法
  整体大于各个部分之和。您可以实现单一方法或一小部分方法,比不使用任何方法得到更大收益。但您只能在实现所有方法的情况下获得最大收益,因为它们的力量来自它们之间的交互。
  最初时按照书籍来执行 XP,作为基准。一旦理解了如何进行交互,就拥有了将它们适应于自身环境所需的知识。请记住,“进行 XP”不是目的,而是到达终点的一种手段。目标是快速地开发高级软件。如果您的过程有一些变异,已称不上是在进行 XP,但结果仍能让您战胜所有竞争对手,您已经成功了。
〈四〉为什么 XP 很重要
  坦率地说,XP(或者任何其它灵活方法)根本就不重要。它能够产生的结果才是关键。如果如 XP 这样的灵活方式可以帮助您更快地开发更好的软件而少受痛苦,那么它值得考虑。
  还记得我们在这篇文章开始时提到的那些令人生畏的数字吗?我们相信使用 XP 开发面向对象软件可以有机会将它们变得更好。目前我们的经验已经证实了这一信念。

大型项目的XP(极限编程)

我们在ThoughtWorks这样的大型项目中应用XP方法的时间超过了15个月。这个项目开始于三年前,那时它有大量的需求文档和几个独立的功能小组。从2000年1月起,我们决定应用XP,虽然当时我们已经知道XP并不适用于大型项目。那时,我们需要向客户提交功能演示,并要通过提交一个工作子集而不仅仅是原型来赢得他们的信心(我们在团队中有超过25名开发人员和大约15名分析员)。但我们并未准备好功能,各个组都使用自己的代码框架,一个完整的应用无从谈起。简短地说,就是客户想要看到点什么,但我们还没有真正的功能。应该说开发的第一迭代期是成功的:我们给客户提交了一个真正的应用子集。成功的首要原因是我们让来自Asset, AR 和GUI组从事不同工作的开发者融合在一起。当然,其间也有"成长的烦恼":在分析员和开发者之间发生某些个人冲突,但我们成功跨越了这些障碍。那时一切都很美好:我们以飞快的速度成功地提交了功能演示,但随着时间的流逝和项目的继续,有些事情却不如我们所愿。这就是这篇文章要讲述的内容:我们真正的收获在"蜜月"期后,关于我们如何在一年半中管理一个50人的项目开发并及时完成阶段性的提交(尽管有时并不成功),关于我们经历的痛苦,我们后续的工作,我们最终学到的以及作为改进要用到下一个项目中的经验。
  首先,我们给出我们开始的状况:15个月前我们是如何应用XP的,然后展示我们现在的XP应用情况。也就是说,下面将讨论我们改变技术的原因。这是一个自然选择的过程,它剔除了许多不好的想法,或把它们改造成更有效的方法,使之更适于大型项目。最后我们将总结并给出在大型项目中使用XP的谨慎建议。
大型项目中XP的元素
好吧,让我们来进入正题。我们开发和分析的队伍由大约35名开发人员、15名分析人员和10名QA组成。开发人员依赖分析人员作为项目的客户。尽管有实际的客户,分析人员还是要协同工作以便有选择地做出客户决策。下表演示了[1]所讨论的XP基本元素并简要介绍了这些方面如何应用到项目的各个阶段。我们用这个表来分析我们团队自然选择的实践,以及书本上的XP如何应用到一个超过50人的大型项目中。

 

计划

提交周期

比喻

设计的简单性

测试

重构

1/2000

大跨度的迭代计划会议。开发和分析组的全部成员整天在一起讨论新的故事卡片和预估。大多数开发者签入(sign up)新的功能。

1个月

转变已有的代码基准。已有的代码依然复杂,但新的代码要尽可能简单。这个阶段包括抛弃老的代码和重写那些"今后可能会被用到"的功能代码。

单元测试开始于一些新的代码。推动建立一个大的测试基准。QA做所有的功能测试并有权留弃故事卡片。

对于旧有代码,如有必要就进行重构。

7/2000

基本同1/2000,但确实感觉十分低效。多数与会者没能很好地参与。拖沓的讨论,50人的会议是无法忍受的。

1个月

以尽可能简单的原则继续设计。完成对已有设计的重构。完成代码会审,旨在全体范围中讨论新设计以便整个团队了解代码和设计的发展趋势。

更好的单元测试覆盖更大的范围,尽管没有完全覆盖。代码功能测试帮助覆盖测试范围。

多数开发人员埋头于新功能的开发,很少会去做重构。代码基准在迭代期末向QA组提交卡片时变得糟糕起来。

1/2001

希望能尽快做出每个迭代期间的计划-我们的办法是在正式会议前以小组为单位进行更多的准备工作。

2 周

多数的设计基于已有的设计:保留标准,代码会审逐渐停止,因为新的设计和重构尚未完成。

试图去掉代码功能测试而代之以屏幕搜刮(Screen Scraper),若失败就回到代码功能测试 。加入新的单元测试但仍然没有全部覆盖。QA组开始结合界面测试使用自动功能测试。

重构开始更频繁,因为部分代码开始变得凌乱不堪。需要清理的原因主要是实现简单和迭代期限使得代码在没有重要重构的情况下增长。

6/2001

举行一些讨论卡片或相关卡片组的会议,参加者包括对这些功能感兴趣或有经验的开发者和负责这些卡片的分析人员。

2 周

队伍中的大多数人及整个QA组准备提交1.0版本给客户。代码的基准分离以便加入没有测试的新功能。对单元测试有更多的依赖。

尽管仍有遗漏,测试范围已经基本稳定。QA组不再测试新功能,因为焦点是1.0版本的提交。

在发布版上做的重构很少,而在继续开发的版本上,开发人员会很尽责地进行重构,特别是在年初被迫做了更大更痛苦的重构以后.

 

结对编程

集体所有

持续集成

40小时周

在场客户

代码规范

1/2000

由于我们决定采用XP,整个团队读了[1]。每个人都做了尝试,多数人被吸引。

由于最初阶段的分组是面向功能,因此此时我们并未意识到集体所有 (collective ownership) ,也没有意识到代码的保护。

从第一迭代期开始,进行在线集成,参见[2]

这是一个概念工作时间:因为我们希望客户在场。于是我们会花另外的时间来满足最后期限。

事务分析人员是在场的客户,他们15人一组。真正的客户是不在的,由分析人员同他们沟通。 基本上是JAVA的一般语法。

 

7/2000

结对编程依旧盛行:开发者对新功能进行结对编程,但改错和维护工作由单个人来做。也有一些开发者停止了结对编程。

当开发者越来越多地接触系统不同的部分的时候,代码的所有者逐渐显现出来。成员间通过闲谈,代码互审和短小的站立会议(Stand up meeting)进行很好的沟通。

代码功能测试加入构造过程。

为了通过故事卡片,在每个迭代期的最后工作时间达到50至60小时。

同上

两周一次的代码互审给开发人员一个机会讨论不同子系统的实现方式。我们可以接受某些子系统代码的非正式方式。

1/2001

结对编程更少了,因为这一阶段编码更直接,而有些人在进行重构。

因为效率低,站立会议被弃用,但代码的所有显现得更加清晰。开发人员开始专职负责系统的某部分。

稳定_同上。

以2周为迭代期,开发人员的工作时间更接近于40小时…

同上

代码互审逐渐减少,设计及编码规范趋于饱和。

6/2001

定下了规则:所有新功能要应用结对编程,而改错和维护则由一人完成。

随着专业化分工的继续,不同的开发小组人员拥有不同部分代码的知识,于是我们将使他们在接下来相应模块的设计中起更活跃的作用,但代码仍是集体共有。

稳定同上。

同上

同上

同上

结对编程
  首先我们说说结对编程的体会。多数情况下,我们在某个迭代期间有两个开发人员同时为一个故事卡片(或几个迭代期的相关卡片)工作。在大型项目中开发人员需要投入更多的关注,因为开始新领域的编码的热身时间是不容忽视的。开发人员间良好的沟通和周期性的计划会议让每个人都具有谁在做什么的整体概念。这使得经典教科书中开发者甲找到开发者乙要求共同解决问题的结对编程方式成为可能。
  结对编程当然很好,但并非任何时侯都适用。最通常的情况是,在改错和维护时开发人员并不愿结对,并且这种情况下许多眼睛盯着调试代码也确实没什么好处。再有就是迭代期间的重复工作,这种情况下,问题的解决方案已经确定,不必再结对了。 还有,开发人员有不同的个性:有些人需要间歇性的结对编程,而有些人更加出色,结对编程会妨碍他们才能的发挥而最终成为他们的负担。

单元测试和集成构造
  单元测试和集成构造绝对是必要的,这意味着如果我们没有测试,我们就不能提交任何代码。当应用程序变得越来越大时,没有测试我们就不能加入任何新功能或进行重构。我们现在在新代码进库时会有集成构造和测试。关于这些构造及测试的细节,负责人员会及时放在内部网页上,这样每个开发人员都能知道当前的构造状态,事务分析员和QA能拿到最近构造的信息来测试新的功能。
组内所有和信息共享
  对于这样一个大型的项目,为了防止被分为孤立的部分而使整个系统做出不适当的假设,信息的发布和不同部分的代码轮作非常重要。沟通是必需的(但我们无法强迫一个沉默的人开口)于是我们试图在每两周一次的短会上给每个人发言的机会,以使沉默寡言的人能说出他们的要求。而最终我们取消了这样的会议,因为大多数开发人员认为这种把每件事都蜻蜓点水地提一下的会议只是浪费时间。这也是这个团队的优点之一??我们总能象一个共同体般地工作,如同结对编程一样地合作。 开始,我们采用了轮作的方法,也就是每个人对每件事都作一点,这使得我们在后来快到截止期限的时候都在从事己经了解的事情。但对于一段复杂的代码,要做到这一点,时间投入非常巨大。最好采用折衷的办法:也就是在项目时间紧的时候只作每个人熟悉的工作,而在其它时候,比如改错、研究或正在做一项熟悉的工作时,可以同时做一件不熟悉的事。我们现在的原则是,开发人员在几个迭代周期中连续做一些相关的卡片,同时逐渐地转向系统的其它部分。在同一个迭代期内签入几个不同卡片的做法己经不再用了。
  代码确实会越变越糟。是因为我们的项目有些大吗?还是因为许多做代码的人都是新手(是指这个功能领域中的新手,而非编程新手)?答案可能是二者皆有吧。但有时我们不接触代码,很难开始系统的其它部分,因此定期清理代码是必须的。这样引出我们下一个论题:重构。 重构
  在应用XP的大型项目中,为了消除代码的不一致性,重构是绝对必要的。即便对于那些对项目的应用领域很熟悉的人,也会面对重构的巨大工作量望而却步。对于项目经理来说,必须认识到重构需要另行分配时间。我们做到了这一点,我们留出了时间来重构代码的主要部分。
短迭代周期
  迭代期及其期限是必须的,但长度一直是个问题。过去我们采用长迭代周期(如一个月),而每到月末就会很紧张并伴随着一些不良代码的加入,并且不可避免地在估计上出现问题。我们不得不接受一些未做的卡片(这对开发人员来说非常困难,并且难以满足既定的期限)。
总结
下面把18个月中我们在这个50人项目的经验和教训列出如下:
1) 在每个迭代期开始时进行迭代计划会议。每日客户和开发人员讨论最近的故事卡片并评估它们,在每天的讨论结束后重新分组并演示这些评价和发现,然后让开发人员签入。这可以让整个项目组知道项目的当前情况而不用让每个人都卷入马拉松式的会议。 2) 使提交版本周期尽可能短:我们是两周一次,但在必要的时候也可以使提交跨多个迭代周期。允许在多个迭代周期签入卡片,但要以每个周期为单位进行进度的监控。
3) 进行尽可能多的单元测试,这是不言而喻的。应当有一个自动进行功能测试的软件包以保证测试的覆盖范围。但是QA组是不可替代的(无论开发人员写过多少测试程序),因为我们对系统怎样工作总是存在偏见。
4) 简单的设计能帮助我们连续向客户提供可工作的版本。频繁的设计会议对于加入大量新功能时是很有用的,而午餐是一个召集整个项目组的好时候。这可以避免在系统的不同部分同时存在不兼容的解决方案。
5) 重构是能够做到简单设计的唯一方法。设计的重构与代码的重构同等重要。尽管不进行重构而采用打补丁的解决方法往往是有吸引力的,但是如果一个系统的补丁太多,那就意味着今后它将进行更大的重构。
6) 在加入新功能时,要坚定不移地贯彻结对编程,而在改错和做重复性工作时停止,因为问题已经在结对时解决了。

7) 集体所有和沟通密不可分。团队必须有一种有效的沟通方式。也许不仅仅是非正式的讨论,有时定期的10到15分钟的发布会是很好的方式。
8) 在大型项目中,一些人要扮演客户的角色,为大量开发人员产生足够的工作。这和领域知识密切相关。
9) 编码标准是十分非正式的,这不会损害你的进度。更重要的是一种通过演示的沟通。代码不是文档的全部,开发人员需要看到全貌,这是代码不能提供的。
也有一些规则我们没有实行:
1) 两周一次的站立会议是低效的。可以选择每月一次的迭代团队通气会来替代。
2) 迭代计划会议没有必要让全部人员参与。更好的方式是按更小的组进行研讨,而在每天下班前用30到45分钟对卡片进行讨论。
3) 一个月的迭代期太长,不利于产生高质量的代码。2周一次的迭代周期更容易跟踪并使估计更准确。
4) 上一点在大代码量时并不合适,特别是重构大规模系统时。这时一张卡片会影响到多个迭代期。
5) 比喻(Metaphor)对于大型系统是不适合的。
6) 每周40小时我们来说不成问题,40小时是最少的情况,超时工作并没有给我们带来不利的影响。要重申的是,我们不是100%的实行结对编程。
  这就是XP,或者说我们的XP版本,在我们小组所做的工作。我们经历了按时提交大型复杂应用的过程,也为每个开发人员提供了应对此类项目的宝贵经验。

活用 XP: (一)发挥过程和人的力量

XP作为敏捷方法的一种,拥有很多优秀的实践,用好这些实践,在软件组织中能够起到很好的效果。问题在于,要用好这些实践并不简单,本系列文章的目标就是围绕着 XP 的实践,讨论隐藏在实践内部的敏捷性实质,研究如何灵活的应用 XP 的实践,从而达到改进软件过程的目的。
软件开发虽然有多个环节,但是我们不能只强调某些环节,任何一个环节出问题最终都会影响产品的质量。因此我们在软件开发中应该考虑整个过程,并且重视人这个因素。
质检员的工作:  在以前的工厂作业流程中,产品在生产出来之后,都需要经过质检员的检查。在质检员身边,有两个筐,一个筐上写着合格,一个筐上写着不合格。对于合格的产品,贴上合格证,进入销售的环节,对于不合格的产品,禁止出厂。很久以来,我们一直采用在产品的最终阶段进行质量检验的方式,这种方式用来避免有质量缺陷的产品出厂,但是既没有办法提高产品的质量,也没有办法降低差错率。这种质检方法的基本思想是,产品出现废品是正常的,只要能够找出废品,产品的质量就不受影响。
  那我们看看软件开发的工艺流程。当软件经历了需求、分析、设计、编码之后,质检员同样需要检验软件是否满足质量要求。各种各样的测试人员共同担任了质检员的角色。而质检员的工序也不简单。黑盒测试、白盒测试、集成测试、用户测试、并行测试。和工厂不同的是,出现问题的软件并不能够简单的扔到不合格的产品堆中。另一个不同,软件产品一定会出现质量的问题。既然不能简单的抛弃产品,那么只好把产品退回到生产线上。于是,需求人员、分析人员、设计人员、编码人员开始对软件进行调整,力图使软件能够出厂。这个反复的过程往往要持续上一段时间。幸运的软件可以顺利出厂(交付),否则,可能会遭到项目失败的命运。
  很明显,我们发现这种做法不够聪明。把问题堆积起来,直到最后才来集中解决,这种做法的代价是非常高昂的。软件开发的特性,决定了越是后期的变更,成本越高。那么,我们应该如何调整我们的做法,来降低成本,提高质量呢?
精益原则
  软件开发总是从其它学科中借鉴管理思路。最早的软件工程从土木工程中借鉴经验,但是后来人们发现建筑和软件开发有很大的差异性。故而新的软件开发方式开始兴起,其中就包括了XP方法论。同样的,新的软件开发方式仍然在理论上寻找立足点,这一次众人的焦点落在了现代管理理念上。土木工程管理的一个很大的问题就在于忽视了人的作用,而现代的管理理念将人的作用提到了一个新的高度,这和新兴的软件开发思想是相同的。而对软件开发思路影响最大的,应该算是丰田公司提出的精益生产(Lean Production)的概念。
  二战后的美国,以福特公司为首的汽车制造公司在大肆提倡规模制造(Mass Prodution)的同时,东方的日本,丰田英二等人在考察了美国的制造思路之后,认为美国的制造方式不适合日本,提出了自己的精益制造(Lean Production)的思路,精益制造成就了一代霸主-丰田公司,丰田的制造方式被人称为TPS(Toyota Production System)。丰田公司的丰田英二和大野耐一等人进行了一系列的探索和实验,根据日本的国情,提出了一系列改进生产的方法:及时制生产、全面质量管理、并行工程,逐步创立了独特的多品种、小批量、高质量、低消耗的生产方式。这些方法经过30多年的实践,形成了完整的"丰田生产方式",帮助汽车工业的后来者日本超过了汽车强国美国,产量达到1300万辆,占到世界汽车总量的30%以上。
  回顾这段历史,和软件开发的历史何其相似。大规模制造理论认为,一定程度的浪费,一定程度的废品是正常的,允许的。而在软件开发中,浪费、成本居高不下也同样成为阻止软件开发迈向工程化的一大障碍。像XP这样的敏捷方法从精益制造的思路中吸取了很多的优秀思想,例如,不断改进质量,设计决策应该交给最贴近生产的人员,根据客户的需求来推动生产。虽然我们一直在强调软件开发和制造行业截然不同,但是,处于变革的十字路口的软件开发行业,总是不断的从其它的行业中寻找可借鉴的理论。这种借鉴来的思路就被称为精益编程(Lean Programming)。精益编程的思路包括:
  消除浪费。 任何不能够为最终的产品增加用户认可的价值的东西都是浪费。无用的需求是浪费,无用的设计是浪费,超出了功能范围,不能够被马上利用的代码也是浪费,工件在不同的开发组之间无意义的流转,也是浪费。
强化学习,鼓励改进。软件开发是一个不断发现问题,不断解决问题的过程。而学习能力的强化,能够令软件开发工作不断的获得改进。
  延迟决策。软件开发如果工作在一个不确定的环境中,变化性会对软件开发本身造成伤害。延迟决策,当环境变得逐渐清晰之后,才有充足的理由来进行决策。而对软件设计而言,如何构建一个可支持变化的系统则成为关键的问题。
  尽快交付。自从互联网流行之后,速度成为了商业中的至关重要的因素,从而直接影响了快速软件开发的成熟。软件阶段性交付的周期越快,软件的风险就越容易识别,用户的需求就越清洗,软件的质量就越高。
谁做决策。谁做决策?是高高在上的高级经理,还是贴近代码的编码人员。决策取决于准确的信息,但是掌握这些信息的权威者往往就是做实际工作的编码人员,将编码人员的建议、决定和实践纳入到决策的范畴来,是成功决策的关键。
  精益编程代表了一种思想,很多的Agile方法都从各自的理论基础出发,支持了这种思想。而在我们的讨论中,讨论的重点就是放在XP上。XP方法论中最有价值的是他的思想。我们研究、学习XP,不能够光了解他的实践活动,还需要时刻注意XP的价值观,并仔细的思考,在实践活动的背后,到底隐藏着什么样的思想。就好像我们在阅读设计模式一书的时候,书中给出的是各种各样的关于面向对象的设计方法,但是书中仍然有一条主线贯穿其中,那就是面向对象的编程原则。
过程
  前一段时间,书店中很畅销的书大多数都和6σ相关。6σ是全面质量管理理论的发展。其中一个很重要,和软件开发非常类似的思路是,过程的每一个步骤,都会对产品最后的质量产生影响,要提高质量,降低成本,提升客户的满意度,最关键的是要对过程进行研究和分析,发现对产品影响较大的步骤,并制定改进的措施。
  一家专门提供外卖的公司,常常被客户投诉说送货的时间太慢了。于是他们加强了送货的力量,包括使用更好的工具,雇佣更多的送货人员。但是成本增加了,客户的投诉依然不断。问题出在了哪里?在对整个流程进行了量化评估之后,他们发现,送货的时间对整个的时间影响很小,而更多的时间是花费在了制作外卖的过程中。所以,原先投入对送货流程改进的投资,算是白费了。

  做任何一件事情,都需要经历一个过程。从外卖店接到客户的订货电话开始,一个过程就已经启动了。记录客户的地址、地址特征、菜名,给厨房下单,分配外送人员,将地址信息传递给外送人员,送货,寻找目的地,交付外卖并收款,后续过程忽略。一个似乎平常的生活活动,其背后都包含了复杂的过程。对软件开发而言也是一样的。从客户提出软件的构想,一直到客户真正开始使用软件,其间的过程非常的复杂,充满了各种各样的不可预测的因素。
  送外卖的过程,每一步都会对最终的质量(客户满意度)产生影响。对客户来说,从打电话到收到外卖的时间,外卖的好吃程度,这些都是属于满意度的组成成分。接到电话后,可能客户的口音较重,记录员听错了地址,导致后续过程全部白费,除非客户等的不耐烦,打电话来重新记录一次地址。下单给厨房之后,可能厨房的电风扇会将单子吹到了地上,客户的要求就被忽略了。记录员把客户的地址描述信息写的很潦草,送货人员可能看不懂,这时候他需要打电话回来问,这就担搁了送货时间。送货人员可能对客户所在不熟悉,找到地址花费了很多的时间。好不容易送到了客户手上,客户已经等的不耐烦了,更糟的是,由于时间太长,外卖已经凉了。客户决定,下一次更换一家外卖店。虽然每一个环节出错的概率都不是很大,但是各个环节组合起来之后,出错的概率就大的惊人。在分析了这样一个流程之后,我们的感慨往往是,居然能够送到,真是不容易!软件开发的过程难道不是这样吗?每一个环节都有可能出问题,需求未必就代表了客户的需要,设计不能够很好的代表需求,反而对编码增加了一些不稳定的因素,由于进度较紧,编码的工作也比较马虎。这样的过程,我们能够开发出客户满意的软件,那么只有一个解释,以前客户接触的软件开发人员,比我们还要烂。
  好吧,我们如何改善这一情况呢?对了,对过程进行改进。既然记录员可能会和客户之间出现错配的情况,那我们就要求记录员在听完客户的要求之后,重复一遍。既然,菜单可能会遗失,我们就在厨房中专门设计一个位置,按先进先出的顺序排列这些菜单,而且保证菜单不会被遗失。既然送货员可能会看不懂记录员的字,那么就让送货员和记录员花费一些时间沟通,要么送货员能够习惯记录员的字,要么记录员写出送货员能够理解的字。既然送货员可能未必认识路,那么就对送货员划片,有专门送A区的,有专门送B区的,每个人熟悉的范围减小了,熟悉的程度自然就上升了。好吧,有了这样的思想,我们也准备对软件过程进行改进了。不过,并不是现在,本文的剩余部分将会围绕着这一点来进行。
过程中的人
  除了过程的重要性,我们还需要考虑人的因素,过程是要依靠人去推动的,没有人,过程就没有任何意义。对软件开发更是如此,开发过程的每一个环节都需要人的参与。从来没有一个方法论象XP这样充分的强调人的作用。因此,在XP的全过程中,人的因素是始终处于首位的。而XP的实践也是根据人的优点和弱点进行精心的设计。我们这里做一些简单的讨论:
  计划游戏:我们常常挂在嘴边的一句话是计划赶不上变化。计划,往往都是很多软件组织的一块心病。所有人都知道计划的重要性,可是计划又是最难做的。没有计划,软件过程无从遵循;有了计划,软件过程又常常偏离计划。在变化越来越频繁的现在,计划更是难上加难。对待捉摸不定的计划,XP的态度是:与其在一开始就费时耗力地制定一堆不切实际的计划,倒不如花费少量的精力做一个简单的计划,然后在随后的软件过程中不断的进行调整。
  这就好像我们骑自行车,设定一个500米外的目标,然后我们把车把固定住,选取好起点,并预先制定好角度和标准路线,然后骑着车子,严格的按照原定路线前进。车子能到终点吗?可能性不大,设定好的标准路线上可能会有障碍物,这是你原先没有想到的;由于无法调节方向来保持平衡,车子可能会摔倒。
  车子,应该这样骑。看看远处的目标,估算距离和时间,得出一个粗糙的速度,然后我们就上路了。在前进的过程中,我们不断的目测目标,察看时间,并调整速度和方向。目标越来越接近,我们的调整越来越熟练,最后,我们成功的抵达的目标点。
  传统的计划方法和第一种骑车方法一样不切实际,花费大量的时间思考几个月后发生的事情是很难的。只有根据变化不断的调整,才是正确的态度。 注意,不把时间花费在计划上,并不等于不重视计划。计划仍然是软件开发的核心。你必须有一个当前的迭代计划,当前的周计划,甚至当前的计划。只有保证每一个小计划的严谨性,才能够保证整个项目计划的成功。

XP对计划的态度是:你不需要把计划做的多么精密,但是你必须去做计划。计划赶不上变化,这句话说的一点都没错,我们不需要逃避变化,花大力气进行精确的计划既浪费,又没有意义。但是这并不是说不做计划。凡事预则立,我们需要简单明了的计划,然后在软件开发的过程中,不断的修正并完善计划。 学习变化:XP最适合那些需求容易发生变化的过程,在现实中,我们发现这种情况实在是太多了。可能软件的目标市场发生了变化,要求软件同步变化;可能客户对需求没有充分的了解,导致需求的变化;可能客户的组织或业务流程发生了改变,要求软件变化。种种的可能性表示,在一个一成不变的环境下开发软件已经称为一种奢望。在这样一个残酷的环境中,我们不得不去学习变化。
  变化包括两个方面:软件过程如何适应变化,以及软件设计如何适应变化。传统的软件过程往往要求上游的软件阶段确定之后,才能够进行下一个软件阶段。但是变化的需要要求软件过程在多个软件阶段之间切换。 由于变化的残酷性,XP建议开发人员必须建立变化的意识。你必须去改变你的心态,接受变化,变化是合理的,一成不变的东西压根就不存在。
  这里插一句题外话。强烈建议在XP的项目中使用面向对象技术。虽然面向对象并没有对软件过程或是软件管理提出任何的要求。但是,一个使用面向对象的团队(注意,是使用面向对象技术,而不是使用面向对象语言,这么说是因为有着大量的开发人员使用面向对象的编程语言编码面向过程式的代码),其管理过程也会随之变化。用户参与、迭代开发、重用、使用框架。这些都是在使用了面向对象技术之后自然而然出现的变化。使用面向对象技术,能够和XP方法进行更加紧密的衔接。
  除了上面讨论的两个简单的思路,本文的其它部分都会针对XP中过程和人两方面的因素进行讨论。

活用 XP: (二)考核和评估之别
螺旋、迭代、增量,不同的名词代表了同样的含义-分阶段开发软件。众多的方法学都采用了这种思路设计软件过程。但是在实践中,更多时候,分阶段开发软件带来的是痛苦。看来,我们常常被书中优美的叙述所迷惑,却没有真正想过实施中的难题。那么,如何管理分阶段的软件开发呢?如何应对现实中的难题呢?
考核和评估之别
  在绩效管理中,有两个名词:考核和评估,分别表示了绩效考核和绩效评估两种绩效管理方式。这两者有什么区别呢?
  我们说考核是一种制度,而评估是一个过程。怎么理解呢?很多的公司都有绩效考核的制度,这个制度一般是在年底的时候,对员工今年的工作做一个评定。考核是一个点。但是评估不一样,评估是针对某一段时间中员工工作中的不足之处,需要改进之处进行评价。不论是考核还是评估,它们两者虽然都是为了达到评价并改进员工行为的目的而设计的,但是做法是不同的。考核针对过去的事情进行评定,容易实现,但是效果不佳,因为时间一长,大家可能忘记了以前的事情,而要公平的对过去一年的表现做一个评定也不是一件容易的事,评估则不同,评估是不断进行的,针对刚刚发生的事情做出评价,并找到改进方法。就好像我们在第一章中举的外卖店的例子,不断地对过程进行分析和改进,这就是一种评估。评估的效果不错,但难以实现。
软件开发中的考核和评估
  这一思路在软件过程中,直接表现为里程碑和迭代的思路。我们可以想想,里程碑是不是一种制度。在需求结束的时候,我们需要需求规约文档,风险列表等等一系列的文档,在设计结束的时候,我们也需要另一些文档。这种处理方式就是考核的思路。但是很多时候,这种考核起到的作用是有局限性的:
  工件的设计原本是为了辅助生成最终的代码,但是往往会演变成为了通过里程碑而设计;
  里程碑的设计不能够完全捕获所有的问题,部分的风险被隐藏了;
  难以对工作量和工作价值进行评估;
  里程碑揭露问题的时间要远远落后于问题出现的时间;
  这里对里程碑的方式做一些分析。我们对问题的理解往往是逐步深入的。在项目一开始的时候,业务和技术上都存在问题,存在不确定性和风险,这时候往往是最需要评估和验证的。但是里程碑方式往往要求必须深入的分析需求,很多的问题并没有得以解决,而是被悄悄的有意或无意的掩盖了。需求毕竟不是软件,它是一个不同人具有不同理解的模型,这时候,项目中各个角色对它的理解都不相同,但是这并不影响他们做出一致的决定-通过需求里程碑。问题到了设计阶段依然存在,这时候需求阶段隐藏的一些问题开始出现,导致我们不得不补充一些工作量。但是所有的问题也没有得到解决,依然存在未知的风险。那么风险到了什么时候才会暴露出来呢?最乐观的情况是在编码时期发现,最悲观的情况是在交付期发现。我们把这种过程称为固化考核过程。 问题在哪里?除了软件本身,模型也好、文档也罢,都不能够代替最后的代码。在精益原则中,我们说,必须消除浪费。当我们在开发工件的时候,我们的浪费行为已经或多或少的出现了。
  与固化考核过程相对的,我们认为存在另一种动态评估过程。里程碑或是检查点并不是不重要。但是我们需要转换思路,来将里程碑的实践做的更好一些。我们上面提到说里程碑方式最大问题就在于一定要等到问题都积累起来了才解决问题,而这个时候往往已经错过了解决问题的最佳时机。而动态评估过程的含义就是在过程进行中不断的发现并解决问题,而不是等到问题累积到一定程度才批量解决。过程随着环境的变化不断的调整,以适应变化性和不确定性的需要。而里程碑实践重在提供一个复审的机会,能够从一个较高的层次上来评价软件。 这种过程就是分阶段开发软件的思路,我们也可以称呼它为迭代、螺旋、增量,都没有关系。关键在于,我们需要不断的发现导致客户不满意的问题,发现改进接电话的方法,发现改进做菜的方法,发现更快送货的方法。
实现策略
  动态评估过程有一些基本的实现思路,第一个基本思路是尽可能早的发现所有的问题,如何发现呢?进行一次探险式的过程。这个过程周期不能够太长,太长的周期容易失控,而且项目初期人员未必能够全部到位;但这个周期也不能够太短,太短的周期无法发现足够数量的风险,无法为后续的过程提供丰富的数据。
clip_image004

  有时候,我们运用原型法来实现这个Mini过程。原型法包括了需求原型和技术原型,分别用于解决业务风险和技术风险。一个典型的需求原型是建立一个界面原型,来帮助客户理解未来的软件,避免抽象的思考。我看过很多界面原型的做法,有使用HTML的,有使用画图软件的,有使用规范的XML的。但是不管如何,界面原型能够帮助用户直观的理解需求。技术原型的主要目标是解决技术风险,任何一个项目都可能存在这样或那样的技术风险。对待风险的基本态度是尽早的评估风险并制定解决方案,而不是束之高阁。技术风险的解决方案视具体情况而定,但是,值得注意的是,一个项目中,技术风险不能够过多。如果确实存在这种情况,想办法找到有经验的导师或培训师,这要比自己摸索节省许多的成本。
XP对探险式过程的评估主要包括两个方面,spike solution和迭代。spike solution其实就是我们在上面提到了的技术原型。它的目的是让不明确的评估成为明确的评估(参见XP的过程图中的Spike)。只有评估准确了,计划才能够准确。因此它是计划和迭代的输入项。
  至于迭代,它是XP中的重要概念。迭代选取了用户需要的功能(称为用户故事),并进行设计、开发、测试,迭代不断重复,并解决各种各样的问题。在通过用户的测试和认可之后,最终产生了一个可以运行的版本。这个版本的产生,标志着一组迭代周期的完成。第一个小版本正是我们所强调的探险式的过程。它的成功和教训,足以让你了解项目的各种知识,包括客户的复杂组织关系,投资方的准确意图,找出所有的涉众,发现用例,令团队成员和客户达成初步的共识,验证技术方案,建立一个初步的软件架构,或是针对现有的架构进行初步的映射,程序员需要额外的培训,测试力量似乎不足够,部署环境的风险需要提前解决。只有你按照完整的生命周期真正的去做这项工作,这些问题才会在一开始都暴露出来,否则,其中的很多问题会在后续的阶段中给你制造大麻烦。 第二个基本思路是增量开发。增量和迭代有什么区别呢?Alistair Cockburn在Surviving Object-Oriented Projects一书中将增量描述为修正或改进开发过程,形象的说法是逐步的完成软件开发。注意到,XP的过程图中的小版本正是一个增量。XP认为,一个增量应该是可以发布的。做到这一点固然很好,但是并不是所有的项目都能够达成这一目标。例如,第一次的增量目标可能主要是定义一个架构,这个架构并不包含用户需要的功能,但是它是项目开发的基础。架构中可能包括业务实体基础结构、数据操纵基础架构等一系列的框架。但是对于XP来说,在用户无法发现价值的框架上花费大量的时间是不值得的,XP提倡的做法是根据需求的发展来逐步完善架构,而不是在项目一开始就花费精力开发架构。很难评价哪一种说法正确,我比较倾向于前期花费时间进行架构设计,但是实践中确实发生过设计过于复杂导致高昂成本的情况。在花费了大量的时间开发了一个属性处理框架之后,我发现其实简单属性就能够处理大部分的情况,毫无疑问,前期的设计投入打了漂。因此,重要的是权衡前期的投入时间。理想的情况是,你已经拥有了一个可重用的框架(或是架构),这样,你可以将项目的需求映射到框架上,而不是在项目一开始的时候花时间来开发框架。如果你没有框架,在项目一开始的时候,花费一定的时间来开发架构,但是这个时间不宜过长,你完全可以在后续的增量中对架构进行改进,所以不用急于一时。而且,单纯的框架(架构)开发是没有办法进行用户接受测试的,你的测试不得不推迟到第二次增量。这个理由也促使我们尽可能的缩短框架设计的周期。
  而迭代则是改进或修正软件质量。这也是第三个基本思路。我们注意看XP过程图中的迭代,多次的迭代才构成一次的增量(小版本),每一次的迭代都是对上一次迭代的改进,其中可能是修正了设计错误,或是需求缺陷。值得注意的是,迭代中可能会出现新的需求变更(新需求或需求改变),并令项目人员对项目的进展速度更加的了解(Project Velocity),这些将会反过来影响计划的修正。这体现了我们在上一章所讲述的XP对待计划的态度。
  并没有法律规定迭代需要和增量一起使用,但很明显,结合这两种方式是一种有效的做法。增量的目标是让项目得以向前推进(这就像是修路的时候,路的长度变长了),而迭代的目标是令软件的质量更优(就像是在一段路上架设路基、铺上水泥,建设路面设施)。这让我们想起了什么,不错,重构的两顶帽子。一顶帽子是为软件增加新功能,一顶帽子是改进软件的质量。非常的相似,只不过一个是过程级别的,一个是程序级别的。这里有一个基本的假设,不要同时增加功能和改进质量。团队也好,个人也好,一次只完成一个目标效率是最高的。
思考 :和传统的先定义问题,然后再解决问题的做法不同,XP偏重于逐步的精化问题。软件开发中的问题定义和数学中不同,它往往是模糊的,动态的,需要在解决问题的过程中不断的调整解题的思路。对XP来说,这种解题思路,体现了其反馈的价值观-尽快获得客户对软件的反馈。

活用 XP: (三)实践迭代
在了解了分阶段开发软件的基本思路之后,紧接着就需要考虑实施的问题。分阶段开发最难的,并不是在过程的控制上,而是在软件设计能力上。
应用迭代的问题
  有一则故事说的是一个人肚子疼,去看医生,医生给他开了眼药,理由是眼神不好,吃错了东西,所以才会肚子疼。软件开发中出现的问题往往不是单纯的问题,头疼医头,脚疼医脚的做法未必适合于软件开发。
  应用迭代并不是一件简单的事情,懂得了迭代和增量的概念,并不等于你能够用好它们。为什么这么说呢?很多的软件组织尝试着运用迭代开发,但是结果却不尽人意,于是将问题怪罪在迭代的方法不切实际上。软件工程中有句著名的话?quot;没有银弹"。迭代和增量也不是什么银弹。要想做好迭代,缺乏优秀的软件设计思想和高明的软件设计师的支持是不行的。在XP中,非常强调各项实践的互为补充。在我看来,迭代能够顺利实行的思路需要重构、测试优先、持续集成等的直接支持。而这些实践,体现了软件设计和软件过程中的关系。
  迭代实践出现问题往往是在项目的中期。这个时候,软件的主体已经形成,代码的增长速度也处于一个快速增长的情况。这种状态下的软件开发对变化的需求是最没有抵抗力的,尤其是那些设计本身存在问题的软件。软件开发到这个阶段,代码往往比较混乱,缺乏一条主线或是基础的架构。这时候,需求的变化,或是新增的需求导致的成本直线上升,项目进度立刻变得难以预期,开发人员的士气受到影响。
迭代之外的解决方法
  在这个时候,软件组织要做的,并不是在迭代这个问题上深究下去,而是应当从软件设计入手,找到一种能够适应变化的软件设计思路或方法。例如,你是否应该考虑在面向对象领域做一些研究呢?面向对象的思路很注重将变化的内容和不变的内容相区分,以便支持未来的变化和应对不确定性。然后你再来考虑相应的成本。
做好迭代有几个值得注意的地方:
代码设计优化
  软件开发的能力并不体现为代码量的多少,而是体现为代码实现的功能,代码的可扩展性、可理解性上。所以对代码进行不断的改进,对设计进行不断的改进(具体的次数根据需要而定),使软件的结构比较稳定,并能够支持变化。这是迭代的一个前提。否则,每一次的迭代都花费大量的精力来对原先的设计进行修改,对代码进行优化,这样的迭代效率是不高的,也可以视为一种浪费。坚持不断改进软件质量的做法其实是将软件的集中维护、改进的成本分摊到整个过程中,这种思路,和全面质量管理的思路是非常类似的。XP中的重构实践有一个修饰词,称为无情。这充分表现了XP的异类,但是应该承认,只有设计和代码的质量上去了,才能够为后续的迭代过程打下一个基础,更何况,XP所处的往往是一个不确定的、变化多端的环境。正是因为这种环境对软件开发有着很大的影响,因此代码质量也被高度的重视。不同的行业,不同的项目,需要根据自己的特征进行调整,但是,只有保证代码的优美性,才能够顺利地达成迭代的目标。
    在一个软件的设计中,对界面结构有着很强的要求,而Eclipse的设计思路正当其时。因此,我兴奋的将Eclipse的设计思路注入到界面设计上来,在花费了大量的时间进行设计和实现之后,发现并不能很好的满足需要。更为糟糕的是,由于设计的复杂性,导致调试和变更的难度都加大,而团队的其它成员,也表示难以理解这种思路。最后的这个设计废弃了,但是损失已经是造成了,复杂的设计和实现,足足花费了一个星期的开发时间。
重构和审查
  除了第一次的迭代,后续的迭代过程都是建立在前一次迭代的基础上。因此,每一次迭代中积累下来的问题最终都会反应在后续的迭代过程中。要想保证迭代顺利的进行,对代码进行重构和审查是少不了的工作。其中最重要的工作莫过于消除重复代码,重复代码是造成代码杂乱的罪魁祸首。消除重复代码的工作可不仅仅只是找出公函这么简单,其间涉及到重构、面向对象设计、设计模式、框架等众多的知识。这些知识的介绍并不是本文的重点,但是我们必须知道,只有严格的控制好代码的质量,软件的质量和软件过程的质量才有保证。
推迟设计决策
  精益编程告诉我们,尽可能推迟决策。在一个变化的环境中,早期的决策往往缺乏足够的事实支持和实践证明。即便是再高明的软件设计师,难免会犯错误,这是非常正常的,那么,既然目前的决定是有着很大风险的,那为什么我们还要急于做出决定呢?在看待设计这个问题上,一种比较好的做法是,尽量避免高难度、高浪费的设计,以满足现有的需要作为实现的目标。未来的需求等到确定的时候再进行调整。
  推迟决策其实是软件设计的一大能力,为什么我们会推荐使用面向对象技术呢?因为面向对象技术具有很强的推迟决策的能力,先将目前确定的问题纳入面向对象设计,并为未来的不确定性留下扩展。推迟决策并不是一个简单的问题,它需要很强的面向对象的设计思维能力。
  设计模式中有很多这方面的例子,其中的装饰模式具有很强的代表性。
clip_image006

  在设计刚开始的时候,没有人知道ConcreteComponent最后的发展会是什么样。很明显,这是一个处于不确定环境中的设计,我们唯一能够确定的,只有Component这个类体系一定会拥有Operate这个方法,所以,我们设计了一个接口Component来约束类体系,要求所有的子类都拥有Operate方法。另一个目的是为客户端调用提供了统一的接口,这样,客户端对服务端信息的了解到了最小的程度,只需要知道Operate这个方法,并选择适当的类就可以了。还可以对这个模型做进一步的改进,令耦合程度进一步降低。
  在统一了接口之后,我们就可以根据需要来实现现有的功能,我们实现了一个ConcreteComponent类,它实现了Component接口,并实现了核心的功能。如果在未来,需求的变化,要求我们增加额外的行为,我们就使用ConcreteDecorator类来为ConcreteComponent添加新的功能:
public class ConcreteDecorator implement Component
{
private Component component;
public void Operate()
{
//额外的行为
component.Operate;
}
}

  先找出共通点,然后实现共通点,并把不确定的信息设计为扩展,这就是推迟决策的设计思路。但是,应该指出的是,上面这个例子的设计,仍然有很多的限制,例如,增加的需求(也就是某个ConcreteDecorator)中可能拥有新的接口,例如需要一个AnotherOperate方法,这时候,原先的扩展性设计就又变得难以满足需要了。在软件设计中,针对接口设计的灵活性和扩展性虽然比以往的设计增强的许多,但它并不是万能的,而且取决于设计师对需求的理解能力和设计水平。此外,推迟设计决策要求我们学习抽象的思维,识别和区分软件中变化和不变的部分。
注重接口,而不是注重实现
Martin Fowler把软件设计分为三个层面:概念(conceptual)层面、规约(Specification)层面、实现(Implementation)层面。软件的设计应该尽可能地站在概念、规约层面上进行,而不是过分关注实现层面。之所以有时候我们发现在迭代的过程中,软件难以承受这种变化,那么,很大的可能是规约层面和实现层面出了问题。我们在前面一节讨论重构和审查的时候说,消除重复代码
  我们可以把规约层面想象为软件的接口或是抽象类,或是具体类的公有方法,而把实现层面想象为实现类、实现细节。那么,我们的原则应该尽可能设计稳定的规约层面,并为客户(可能是真正的客户,大部分情况下是使用你的代码的客户程序员)提供一个优秀的、简单的界面(接口)。社会发展到现在的水平,任何一个人都不会花费过多的时间来研究你的代码,如果你的代码不能够为他人提供便利性,那么最后被淘汰的一定就是你的代码。Java语言的成功,很大程度上就在于他在保证其强大功能的同时,还提供了一个简单、易用、清晰的规约界面。
  在软件设计中,重视规约层面的设计是很普遍的。为什么我们提倡三层架构的软件设计?最重要的是因为他为软件结构合理性贡献巨大,远远超过了他的其它价值。在现代的软件设计中,数据库、界面、业务建模其实是三种差异较大的技术,这就导致了三者的变化度是不同的。根据区分不同变化度的原则,我们知道,必须对三种技术进行区分。而这正是三层架构的主要思路。从这个思路扩展出去,我们还可以根据变化度的需要,将三层架构演变为四层架构、甚至多层架构。而多个层次之间,正是通过优秀的规约界面来达到最松散的耦合的。
  在精益编程中,为了避免浪费,要求每位程序员提高代码的规约层面的稳定性是非常有必要的。一个系统中,设计优良的规约界面能够拥有比较好的抗变化能力,能够较好的适应迭代过程。
回归
  版本2的软件出现了版本1中不存在的行为,称为回归。回归是软件开发中的主要问题。在对现有功能修改的同时影响原有的行为,这是造成bug的主要原因。在迭代的过程中,必须避免回归行为的出现。而避免回归问题的主要解决方法是构建自动化的测试,实现回归测试。
  成功构建回归测试的关键仍然在于是否能够设计出优秀的规约界面,并针对规约界面进行测试。这样,不但设计具有抗变化性,测试同样具有抗变化性。而唯一可能改变的就只有实现了。在回归测试的帮助下,代码的变化是不足为惧的。我们把有关测试的详细讨论放在测试一节中。
组织规则
  在后续的章节中,我们会详细的讨论XP中的一项非常有特点的组织规则-结对编程。这里我们需要知道,不同的团队有着不同的组织,其迭代过程也需要应用不同的组织规则。例如,组织的规模,小规模的组织可以应用更快的迭代周期,如一周,在一个迭代周期中,团队可以集中力量来开发一个需求,强调重构和测试,避免过多的前期设计。对于大的组织来说,可以考虑迭代周期更长一些,更注重前期设计,并将开发人员和测试人员的迭代周期交错开来。团队的组织构成也是影响迭代过程的主要原因。团队是否都是由相同水平的人构成,每个人的专长是否能够互补,团队是否存在沟通问题。

活用 XP: (四)需求和故事

如何分析需求,如何记录需求,如何将需求映射为设计,这些永远是需求分析中最为重要的问题。XP提倡以一种简单实用的态度来对待需求,而在软件开发的历史中,需求分析从来都是最需要严谨对待的工作流程。究竟谁是对的?
故事
  每个人都喜欢听故事,这也许是从小就养成的习惯。如果能够把需求分析工作变成听故事的过程,那该有多好。需求分析人员写出一个个优美的故事,开发人员边看故事,边实现故事。也许这就是XP的设计思路所在。用户故事,XP把需求变成了一个个故事,摒弃了枯燥无味的需求稳定。文档的作用是传递信息,如果失去这个意义,再优秀的文档也没有任何用处。但是,完整细致、厚达数十页的需求文档是否真的能够达到沟通的目标呢?对于大多数而言,恐怕看到文档的厚度就已经心生惧意了吧。好吧,我们通过很多的辅助手段,可以强制要求开发人员都投入大量的精力来研究、学习复杂的需求文档。但是这厚厚的需求文档真的能够完整的记录所有的需求吗?更糟糕的是,需求是会发生变化的,到时候如何维护这份需求文档呢?回想精益原则,我们可以判定,这种处理需求的方式一定会产生大量的浪费。将需求做的尽善尽美需要成本,项目组的人员熟悉需求需要成本,维护文档需求成本,解决不一致的问题也需要成本。那么,我们可以针对这几点做一个分析:
  需求的文档是否要尽善尽美?需求文档的最大目标是将信息从业务人员传递给开发人员(当然也会存在其它的目的,例如作为合同的组成部分)。那么,文档是否完美和能否实现沟通效果并没有直接的关系。
  开发人员怎么才能够快速理解需求?文档的制作融入了制作者的思想,因此他人理解总是需要一定的时间的。解决问题的思路有两个:一是提供标准通用的做法;二是简化文档,简单的东西总是要容易理解,但简单的东西并不等同于制作容易。
  维护文档需要成本。不管如何,维护成本始终是无法避免的,关键在于,能否降低这部分的成本呢?维护成本和文档数量、复杂度成正比,因此文档的数量要尽可能的少、复杂度要降低。此外,减少维护的次数也是关键的因素之一,在讨论精益原则的时候我们说尽可能推迟决策就是这个意思。
  针对以上的几点,XP提出了自己的实现思路-用户故事。用户故事简单,每个人都会写,每个人也都能理解,改变起来也很容易。但用户故事只是对系统功能的一个简单的描述,他并不能提供所有的需求内容,因此,在XP中,用户故事的实践需要现场客户的支持。用户故事之所以简单,是因为它只是开发人员和客户之间的一种契约,更详细的信息需要通过现场客户来获得支持。 从XP的观点来看,用户故事有这么几点作用:
  客户自己描述、编写需求。对于任何一个需求来说,最理想的状态都是开发人员教授客户编写需求。最差的情况是开发人员代替客户编写需求。毫无疑问的,XP要求的就是最优秀的做法。客户要能够自己开发需求,前提条件是编写需求的技巧应该足够简单,能够很容易掌握,或是经过培训很容易掌握。用户故事就是这样一种简单的机制。
  用户的观点。优秀的需求应该是站在用户的角度来思考问题,是用户能够利用系统完成什么,而不是系统自己完成。用户故事很好的达成了这一原则。因为用户故事是用户站在自己立场上编写,表现了用户对系统的期望和看法。
  重视全局,而不是细节。需求有精度上的差别,软件开发初期最关键的,是建立一个高阶的需求概况,而不是立刻深入细节。对于XP来说,最主要的细节需求获取的方法是经过现场客户。现场客户随时提供对需求细节的指导。因此,用户故事的重点在于,尽可能全面的发现需求,以及,维持一个简单的需求列表。
  评估的依据。用户编写的需求为软件的估算提供了依据。虽然这个依据是比较粗的,但随着项目的发展,开发速度的估算会越来越精确。在需求初期就进行适当的估算,其目的是让用户能够有一个比较直观的成本概念。这为用户制定需求实现的先后次序提供了指导。
  用户自己的统筹安排。制定用户故事就像是上商场购物,虽然每件物品都是有用的,但是最后购买的次序和数量则要取决于钱包的厚度。在每一个用户故事具有了成本(即上一条中的估算)之后,用户就能够权衡实际成本和需要,并排定需求的座次。
  迭代计划的输入。用户对用户故事的选择直接影响到迭代计划的制定,在第一个版本中,用户希望能够实现哪一些的需求(通过选择用户故事),经过估算,这些需求是不是能够在这个版本中实现,计划需要多长的时间。这些都是需求对迭代计划的影响。
故事的弊端
  在收到国外汇款时,业务人员需要记录汇款的相关信息,如果汇款指定的收款人帐户为本行帐户,进行入帐处理,如果收款人帐户属于同城同业(本地的其他银行),则通过同城同业转汇给收款人(后续如何处理?),如果收款人帐户属于异地同业(异地的其他银行),则通过银行的帐户行将汇款转汇至异地,并支付帐户行转汇的费用(后续如何处理?)。   以上是一个银行的国际结算业务中款业务的例子。简短的叙述和非正式的形式体现了XP强调的简单原则。故事帮助开发人员和用户理顺流程的关系。在上述例子中,我们看到开发双方对流程仍然存在一定的疑虑(即括号中有问号的部分),但是这并不影响到用户故事的创作,因为这个版本的用户故事还会变化多次。但从这个简单的例子上来看,我们发现故事的形式仍然存在着一些不足: 故事的形式更容易被人接受,但是也有不规则的缺点。任意描述需求虽然节约了培训的成本,但是却造成了不一致性。不同的人对故事有着不同的理解,对需求也就有了不同的理解。需求故事虽然看起来很简单,但是要讲好一个需求故事绝对不是一件容易的事情。需求规约过于形式化和正式化,导致了需求规约难以使用,但是完全不要形式也不是一个好的做法。在形式和可用性之间保持平衡,是讲好需求故事的关键。 需求故事虽然容易阅读,但是却很难写得好。如何控制需求的描写精度,如何分解需求,如何组织,如何确定边界。但是XP并不关心这个问题,只要能够起到沟通的效果,怎么做都行。这种态度是否正确我们暂不去评价。但在实践中,由于缺乏系统的指导,一个新手往往需要花费很长的时间才能够学会故事的写法。
  对于XP来说,需求的开发只有先后次序之分。而先后次序的制定由客户来负责。但是在实践中,识别出先后次序并不仅仅是客户的责任,开发人员同样需要提供需求优先级和风险的建议。这里有几点需求优先级的建议:
  需求中包含了主要的设计,或是包含了大量的业务实体和典型的业务规则。对于这样具有系统代表性的需求,应该赋予较高的优先级。
  需求中存在重大的技术风险,例如需求中包括了OCR开发包,而开发团队原先并没有相关的经验。
  需求中包含了重要的业务流程,代表了本软件的主要设计思路。
  需求具有代表性,并且难以估算,急需对需求进行试验性的评估,以获得较为精确的速度值。
  需求的时间紧迫。
采用用例技术
  用例技术保持了需求的简单原则,用例和形式和用户故事非常的相似,但是用例具有自己的格式,虽然这个格式也是可以任意定义的。用例的重点是表示系统的行为。我们看看上面的例子如何用用例来表示: 主要角色:业务人员
层次:业务流程级别
  前置条件:收到汇款
  基本流程:
1 业务人员选择汇入汇款业务。  
2 业务人员输入必要的汇款相关信息。
3 业务人员将汇款转入收款人帐户。
3.1 如果收款人为本银行帐户,直接入帐。
3.2 如果收款人为同城同业(本地的其他银行),则通过同城同业转汇给收款人(后续如何处理?)
3.3 如果收款人帐户属于异地同业(异地的其他银行),则通过银行的帐户行将汇款转汇至异地,并支付帐户行转汇的费用(后续如何处理?)。
    可以看到,用例表示的内容和用户故事并没有太大的差别,但用例比较强调格式。虽然不同的团队有不同的格式,但是在同一个团队中,尽可能使用相同和相似的格式(不同的用例可能需要不同的用例格式)基本流程中的每一个步骤都代表了业务人员和系统一次交互,流程非常的简单,但是已经覆盖了一个成功的流程。我们看到,流程的每一步都高度抽象的原因是该用例的层次是业务流程级别的。(业务流程级别也仅仅是一种约定,并不是标准)。利用层次的概念对用例进行精度的划分。在上面的例子中,低精度的用例主要的目标是把握系统的全貌。在RUP中,这种用例也被称为业务用例(Business Use Case)。在原先的用户故事中,对分支情况描述比较含糊,但采用了用例的这种描述形式,分支情况就一目了然了,和前面一样,分支情况的表述也有很多种的形式。
  用例技术从提出到现在,已经有了大量的经验积累。在XP项目中采用用例技术并不是什么新鲜事。但在XP中应用用例也必须遵循XP的原则,以及精益编程的思路。所幸的是,这些思路是非常自然的,使用用例技术是完全可以实现的。本文并不打算详细的描述用例技术,如果要深入了解用例技术,有几本书是非常值得一看的(见附录)。
  先把握系统的全貌:在做需求的时候,常常出现的一种情况是需求分析人员花费了很多的心思来精华、完善某个用例。对XP来说,这种做法并不推荐,而根据精益原则,这种行为存在浪费的可能性。我们对软件、对项目的认识是不断深入的。因此,我们在项目一开始就深入到需求、故事、或用例的细节,分析人员的能力可能很强,能够正确的捕捉到用户的实际需要。但是一个星期之后我们对需求的认识就有可能发生变化,也许是原先对用例范围的界定出现了问题,也许从另一个角度分析用例效果会更好,也许原先处理用例的思路不正确。不管如何,需求变化的可能性是非常大。用例越详细,发生变化的可能性就越大。这时候,原先花在精化用例上的时间就被浪费了。
  因此,不要在一开始就精化需求,一开始的工作重点应该是放在尽可能全面的收集用例,了解整体的业务流程,分析主体业务流程等工作上。在获得了系统的全貌之后,你会发现你原先对系统的认识是不充分的,用例需要根据新的思路进行重新排列,用例的优先级需要调整,在UML图中,往往有一张系统的用例概览图,这张图所表示的就是系统行为的一个概述。
  寻找优先级高的用例进行精化:我们在上文提到了需求优先级的判断,用例的优先级判断和需求的优先级判断相似。在讨论迭代的时候我们说过,前几次迭代的主要目的是要识别出项目风险。因此,寻找有代表性、优先级高的用例进行精化,能够帮助开发人员更快的理解领域知识,构建起初步的领域模型。 继续上面国际结算的例子,在完成总的用例图之后,我们发现,银行的业务非常的复杂,如果缺少领域专家,要在短时间内领会领域逻辑是非常困难的,同时,我们发现,汇款的业务在日常业务中所占的百分比是非常高的,而汇款业务涉及到了大多数的领域知识,而业务流程却相对简单。因此,我们决定,先把汇款的用例作为一个突破口,在完成了这个用例之后,我们的开发人员就会对业务领域有着比较深入的认识,也就能够进行更复杂的工作了:
主要角色:业务人员
  层次:业务流程级别
  前置条件:收到汇款
  基本流程:
1 业务人员选择汇入汇款业务。
2 业务人员输入必要的汇款相关信息。
3 业务人员将汇款转入收款人帐户。  
3.1 如果收款人为本银行帐户,直接入帐。
3.2 如果收款人为同城同业(本地的其他银行),则通过同城同业转汇给收款人(后续如何处理?)
3.3 如果收款人帐户属于异地同业(异地的其他银行),则通过银行的帐户行将汇款转汇至异地,并支付帐户行转汇的费用(后续如何处理?)。
  备选流程
2.A在任何时候,业务人员都可以应客户的要求对向汇款银行进行查询。
2.A1在收到汇款银行的查询答复之后,记录答复信息。
2.B在任何时候,业务人员收到汇款银行要求退回汇款的授权。
2.B1如果汇款未被提走,根据要求将汇款退回汇款银行。
2.B2如果汇款已被提走,通知汇款银行无法处理,用例结束。
  注意到,在这个例子中我们对用例优先级的判定条件和上文的稍有不同,我们选择有代表性,但又相对简单的用例作为高优先级的用例。这样做是因为对业务领域比较陌生,一开始实现复杂的需求有很大的难度。所以,虽然我们提供了一些制定用例优先级的思路,但是实践的时候仍需要根据实际情况权衡。 迭代精化:用例的编写过程是一个对业务领域不断熟悉的过程。随着调研的深入,不断有新的问题显露出来,需要补充或修改原先的用例。这里有两种情况,一种是在同一个增量内,在对用例B精化的时候,发现用例A中忽略了一种情况,这时候我们就需要补充用例A。例如,我们在精化其它用例的时候,发现汇款用例中忽略了报表的需求,这样我们的工作又必须回到汇款用例上。这样的情况是非常普遍的,这就要求我们不要过分的修饰用例,不要把精力花在用例格式上,这样只会造成浪费。
  第二种情况是在不同的增量中,这时候用例往往会加入新的需求、新的情境。我们如何去控制不同增量期间的迭代呢?一般来说,有两种方法,一种是对原有的用例进行增补,增补的部分用不同的颜色或标记。另一种方法是为用例建立版本,不同版本的用例对应于不同的增量周期。这样,对应对N个增量周期就有了n个不同版本的用例(n≤N)。不管是哪一种情况,都要求我们采用迭代的思路来处理用例。
  形式不是最重要的:在团队中强制要求统一的用例书写格式是有意义的,但有的时候,这个意义并没有想象中的那么大。可以约定条件的编写形式、也可以约定层次的划分。但是过分的强制形式就没有什么意义了。

活用 XP: (五)测试管理

无论从那一点上来看,要保证软件的质量,测试工作是少不了的。而测试往往又是经常被忽略的。对于敏捷方法,精益编程而言,如何保证测试的有效性?如何减小测试的成本?是测试中首要考虑的两个问题。
测试过程
  要做好测试可不是一件容易的事情。测试工作和软件开发密切相关,却又自成体系。测试并不是一个单独的阶段或活动,测试本身就是一个过程,具有自己的生命周期,从测试计划开始,到测试用例的制定,测试的结构设计,测试代码的编写。测试的生命周期和软件开发生命周期拧在一起,相互影响。当然,我们还是那句老话,罗马不是一天建成的。对我们来说,还是从简单的开始。
  在我们谈及精益编程理论的时候,曾经讨论过全面质量管理的概念:生产过程的每一个环节都需要为质量负责,而不是把质量问题留给最后的质检员。这对于软件开发有着很好的借鉴。软件开发中最头疼的就是质量问题,因为人的行为过于不确定了。在经过漫长的软件开发周期之后,软件渐渐成型,但是缺陷也慢慢增多,试图在最后的关头解决长期积累的问题并不是一个好的做法。软件开发到了这种时候,发现和修改缺陷需要付出很大的代价。
  我们说,最后关头的测试并不是不重要,但是,软件质量问题应该在整个软件过程中予以重视。
测试的最小单位
  测试问题的很重要的思路在于测试的管理上,如何管理一个项目中所有的测试,以及它们相关的文档,相关的代码,如何定义测试人员的职责,如何协调测试人员和开发人员之间的关系?
XP的测试优先和自动化测试实践是一个非常优秀的实践,我们也曾不止一次的提到该实践,但是对XP强调的单元测试,很多人都有一些误解:
XP中提供的例子过于简单,无法和生产环境相结合。XP中的单元测试只是为测试提供了一个具体的操作思路,但是它并不能取代其它的测试原理。如何进行测试,如何组织测试,如何管理测试,这些都要由不同的软件组织自己来进行定义。
  测试代码本身不能够适应变化。黑盒测试的理想状况是外部行为不因为内部行为的改变而改变。当需求或是设计发生变化的时候,一段代码的内部行为需要改变,但是外部行为却不需要变化,这样,针对外部接口进行的单元测试同样不需要改变,但是这个规则一旦被违反,我们就需要付出同时改变测试代码的双重代价了。因此,测试代码的设计本身就是很讲究的。
  单元测试(有时候也称为类测试)是代码级别的测试,是测试的最小单位。XP非常看重这个最小单位。我们观察测试优先框架XUnit,发现它使用组合模式将大量的最小单位的单元测试组织起来,形成完整的测试网。所以,XP的思路非常的简单:最小单位的测试能够做好,全系统的测试就能够做好。这个思路未必就正确,但是注重最小单位的测试的思路是绝对正确的。每个部件都正确,最后的软件未必正确,但任何一个部件不正确,最后的软件一定是不正确的。
测试优先
  测试优先和单元测试在XP中属于同一个实践,但是它们仍然是由区别的。测试优先强调行为,在写代码之前写测试,单元测试主要指的是测试的范围或级别。我们说,测试优先实践真正关心的,并不是测试是否要先于代码,关键在于你是否能够编写出适合于测试的代码,是否能够从测试的角度来考虑设计,考虑代码。 从另外的一个角度上说,坚持测试优先的实践,可以让你从一个外部接口和客户端的角度来考虑问题,这样可以保证软件系统各个模块之间能够较好的连接在一起,而开发人员的思考方式,也会逐步地从单纯的考虑实现,转移到对软件结构的思考上来。这才是测试优先的真正思路。而坚持先写测试,只不过是帮助你转变思维习惯的一种措施而已。对于一些优秀的程序员来说,只要能达成目的,是否测试优先,倒并不是最关键的了。
  其实做测试是一件很难的事情,因为很多时候,我们不能够完全的模拟出测试环境,或者是完全模拟出测试环境的代价太高。软件开发总是在一个固定的时间和成本的前提下进行,因此我们必须尽可能用小的成本来达成我们的关键目标。很多关于测试的书中都提到诸如磁盘出错之类的错误是很难进行测试的,但实际上,还有很多很多的内容是难以进行测试的。例如,一个业务逻辑,它使用到了14个业务实体和其它的一些配合的类,如何测试它?使用Mock Object方法,建立测试Fixture的代价将会很高,此外,如果实体类是可以控制的(例如,该实体类可以使用程序来初始化数据,而不是从数据库中获取数据),这个测试的成本还可以接受,如果不是(例如,第三方提供的技术),这个成本将会更高。类似的情况还有很多,但是为什么会出现这些问题呢?其中一个很大的原因就是我们并没有真正的把测试作为软件开发的一个重要的组成部分, 坚持测试优先的思考方式,可以大幅度的降低测试成本。现代的软件开发往往都依赖于特定的中间件或是开发平台,如果这些第三方产品没有提供一个强大的测试机制的话,要对最终的产品进行全面的测试往往是很难的。例如,在J2EE提供的Jsp/Serverlet环境,模拟Http的输入和输出是一件很难的事情。如果在软件设计阶段不考虑测试,那么最后的测试将会是寸步难行的。但是实际上,如果在软件设计时考虑到测试的困难程度,并将业务代码和环境控制代码区分开发,使之彼此之间没有过大的耦合。这样,测试工作就可以针对独立的业务代码进行,而这个成本就会低很多。
public class UserLog
{
public Service()
{
//难以进行测试的代码
//需要测试的业务代码
}
}

  注意到,在上面的示例类中,提供服务的代码分为两个部分,一部分是框架提供的、难以进行测试模拟的代码,这类的代码有很多,例如对HttpRequest的处理,模拟http的数据是比较复杂的。这就增大了测试的难度。而这部分的处理往往是平台提供的功能,不需要进行测试。第二部分是关键的业务代码,是测试的核心。那么,一方面构建测试环境难度较大,另一方面又需要对业务代码进行测试。因此我们自然就想到将待测的业务代码分离出来:
public class UserLog
{
public static void Write(String name)
{
//写入用户信息;
}
}

public class UserLogAdapter
{
public Service()
{
//难以进行测试的代码
UserLog.Write(Name);
}
}

  这样,测试就可以针对UserLog进行,由于不需要复杂的测试环境,对UserLog进行测试的成本是很低的。在J2EE核心模式一书中,提到了一种向业务层隐藏特定表示层细节的重构思路:

clip_image008clip_image010clip_image012
  虽然,这种重构方法的出发思路是避免界面层次的细节暴露给业务层,但是从另一个角度来说,也提高了业务层组件的可测试性。毕竟,构建一个用户信息,要比构建一个HttpServeltRequest要容易的多。
  因此,最合理的引入测试的阶段是在需求阶段。需求阶段的测试工作的重点是如何定义测试计划,如何定义接受测试并获得客户的认可,在需求阶段结束的时候,必须保证所有的需求都是可测试的,都拥有测试用例,需求阶段另一个重要的测试任务是准备构建测试沙盒,建立一个测试环境,以及这个软件项目所需要的测试数据;在设计阶段,测试工作的重点则在于如何定义各个模块的详细测试内容,最好的方式是实现测试代码,并构建测试框架,对于一些比较复杂的项目,甚至还需要编写一些测试工具。实践中我们发现,在XUnit的基础上扩展出一个测试框架是一种简单但又实用的方法。XUnit的重点是对自动化测试提供了一个通用的框架,捕获异常,记录错误和失败,并利用组合模式对Test Case和Test Suite进行管理。实际上,还有很多工作是可以在XUnit框架上继续开展的,例如,软件开发中是不是存在较为通用的测试用例?这样,你就可以定义一些抽象的测试用例,并以此作为测试框架的基础。再比如,我们希望每天晚上在进行日集成的时候,测试结果能够通过短信直接发送到负责人的手机上,那么我们可以在框架中嵌入这部分的功能。这些都属于对测试框架的积累。对一个软件组织来说,很有必要花费时间对测试框架进行积累。这可以简化测试的工作量,并提升软件的质量。
测试过程
  我们一开始说,测试有其自己的过程,虽然XP并没有花费太多的笔墨来描述自己的测试过程,但经过细心的观察,我们可以发现,在XP中同样存在着一个测试过程:
clip_image004[1]

  这个过程是从用户故事(或者是我们在上一章中推荐的用例)开始的,用户故事不但为版本计划提供了需求,而且为接受测试提供了测试场景。而对于客户参与的接受测试来说,它为每一次的迭代提供了反馈,包括bug的反馈和下次迭代信息的反馈。只有客户认可了接受测试,软件才能够发布小版本。这是XP过程最高层次的测试过程。
  在上文中,我们提到引入测试最好的时机是在需求分析阶段。因为测试生命周期的起源活动-测试计划和测试用例都需要需求的支持。我们再参考RUP的过程:
clip_image013

  我们看到,RUP建议在先启阶段就开始测试活动。在开发过程的前期就进行测试活动,其目的是为了提高软件的可测试性。软件设计如果没能够考虑软件的可测试性,那么测试的成本就会升高,软件质量随之下降。有时候,单元测试或是组件测试是很难进行的。因此,我们需要专门针对类或组件的可测试性进行测试。例如,对于一个实现企业流程的组件,之间涉及到大量的状态、事件、分支选择等等因素。对这样的组件进行组件测试的代价是非常高的。如果能够在组件设计的时候,能够考虑到测试性,例如,将组件拆分为粒度更小的子组件,或是在组件中内嵌供测试使用的方法,能够直接操纵组件的状态。在设计时充分考虑可测试性,是降低测试成本的关键。而设计测试的源泉,正是先启阶段中对需求的分析。对流程组件测试的依据,正是源于项目涉众对流程的需求。
测试的一些实践问题
  严格按照先维护测试,再维护代码的顺序要实现变更。在实践中,测试优先常常发生的一个问题是,设计变更影响到测试代码的时候,开发人员往往会绕过测试代码,直接修改代码。
  在刚刚接触测试优先思路的时候,我严格按照先写测试的做法编写代码,但是当代码需要修改时,有时候只是一些非常小的修改,这时候我仍然保持原有的习惯,直接对代码进行了修改,在完成代码的修改之后,我突然意识到测试代码需要修改,于是我又修改了代码,由于只是一个小修改,我认为没有必要再运行测试了。这件事情很快被我遗忘了,但隐患就此埋下。到了两天后的集成测试时,测试程序捕捉到了这段代码的错误,经过调试,发现当时认为简单的修改忽略了一种极端的情况。定位错误,调试代码,并通过测试的时间远远超过了当初贪图省事节省的时间。所幸的是,代码在下一个检查点(集成测试)被发现出来。 完善测试网。在我学习并实践测试优先的时候,我所处的团队正处于项目的中期,已经有大量的没有实现测试的代码被创建出来,当时我采取的思路是,新编写的代码必须遵循新的测试方法,旧有的代码保持现状。这样做可以节省一定的成本,但是很快我们发现,投入力量把现有的代码加上测试是绝对值得的。加上测试的代码能够迅速回应变化,仅仅这一点,就值得我们重建测试网。此外,由于需要构建测试,我们还发现了原有代码中一些接口定义不合理或是不规范的地方。 而在另一些一开始就采用测试优先思路的项目中,往往遇到的问题是,随着项目的进展,后期的测试代码越来越优秀。这时候,我们需不需要对原有的测试代码进行改进呢?答案是肯定的,你一定会从中获益的,对于自动化测试来说,修改测试代码并重新运行测试的代价并没有你想象中的那么大。
  完美的测试是不存在的,但是测试可以越来越完美。我们在文章一开始就提到了全面质量管理(TQM)的思路,TQM认为,产品生产的每个过程都会对最后的产品质量产生影响,每个人都需要对质量负责。对于软件开发也是一样,开发过程的任何步骤都会对软件质量产生影响,要提高软件质量,并不是加强测试力量就能够做到的,需要在整个过程中保证软件的质量。构建测试并不断改进测试的行为贯穿于整个开发过程,为质量提供了基础的保证。
自动化测试
  自动化测试是XP测试活动的另一个优秀思路。在我们讨论迭代的时候,曾经简单讨论过回归和自动化测试。只有测试实现了自动化,回归测试才能实现,重构才能够贯彻,而迭代也才能够进行。所以XP一直强调它的实践就像是拼图,只有全部实现才能够完全展现其魅力。单单从这个角度,我们就能够体会到这句话的含义了。
  对于一个自动化测试系统而言,有几个部分是特别重要的:
  数据准备:对于一个简单的TestCase而言,数据准备的工作在Setup中就完成了处理(参见JUnit),但是现实开发过程中的测试数据通常比较复杂,因此有必要准备单独的数据提供类。对于一个完整的企业应用系统而言,往往包含数千的测试用例,而相应的测试数据量也极为庞大,这时候,我们还需要有专门的机制来生成和管理测试数据。
  测试数据和特定的项目有关,因此不存在一个标准的建立测试数据的规范。所以我们在XUnit框架中看到,框架仅仅只是把建立数据这个活动给抽象出来,并未做额外的处理。但对于自动化测试而言,为各个单元测试建立独立的测试数据是很有必要的。测试数据的独立性是测试用例独立性的前提。测试数据大部分采用脚本的形式建立,包括输入数据和输出数据两个部分。例如,对于一个业务实体,就可以使用一个脚本来对它的属性赋值。脚本文件的形式有很多,例如配置文件、数据库数据脚本等。
  验证:验证是将待测试的方法返回的结果值和预定的结果值进行比较,以判断该方法是否成功执行。结果值总是和输入值相匹配,因此,我们经常将结果值和输入值放在同样的脚本中处理。比较通用的验证方式是采用断言机制,此外,还包括错误记录、浏览测试结果,产生测试报告等功能。
  桩:桩(Stub)是自动化测试中常用的一种技巧。在OO设计中,类和类之间往往都有关系,我们如何对一个依赖于其它类的类进行单独的测试呢?很多的软件设计中都存在难以模拟错误的现象。例如对磁盘出错、网络协议出错的情况就难以模拟。测试桩的思路就是为了解决这些问题,一个桩并不是真正的对象,但是能够提供待测对象感兴趣的数据或状态,这样,待测试对象就能够顺利的使用依赖对象,或是模拟事件。

活用 XP: (六)强化沟通

结对编程是本系列文章讨论的最后一个主题,也是备受争议的一个主题。为什么一个人的工作要两个人来完成,这对于老板来说简直就是犯罪。和前面的主题类似的,我们要学习和应用一项实践,关键的还是要把握其实质。
沟通为王
  沟通问题是一个项目成功最重要的因素之一。一个项目可能并没有什么正式的软件过程,但是只要团队成员能够进行有效的沟通,项目成功的可能性就很大,但是如果项目中缺乏有效的沟通渠道,再优秀,再严谨的软件过程也没有用。优秀的软件方法学,总是会在沟通渠道的建立,推动有效沟通上花费大量的精力。我们分析RUP、XP等方法学,都会看到很多这样的实践。沟通对一个项目而言是重要的,对一个软件组织而言就更重要了。从长期来看,内部能够进行有效沟通的组织能够得到很好的发展,但是反过来,内部沟通不畅的组织将会出现很多的问题。
  在软件开发过程存在的一个很大的问题就是沟通不畅的问题。事实上,这个问题并不仅仅在一个开发过程中存在,在整个软件组织内都将长期的存在,并成为阻碍软件组织发展的一大障碍。这样的说法可能过于理论化,但是我们只要想想,如果现在的项目中,一个主力程序员离开的话,是否会给项目,甚至组织带来重大的影响,就能够理解这段话的含义了。造成这种现象的主要问题是程序是分散在各个程序员手中的。各个代码块就像是程序员们的私有财产一样,神圣不可侵犯。
  更为糟糕的是,任何一个程序员都不愿意阅读他人的代码,比起理解别人的代码,程序员们宁可自己重新编写代码,这导致另一个严重的问题――软件组织中大部分的工作都是重复的,以至于程序员天天忙于开发代码,却难以把精力放在更有价值的地方(关于什么是更有价值的地方,我们在下文会详细的描述)。
  在一些项目中,我们经常看到这样一种开发环境:每个程序员都拥有个人的隔离空间,彼此之间不进行交流,甚至有时候他们整天不说一句话。在和项目中的一位主力程序员进行沟通之后,我们发现了他们的真实想法:
  项目非常紧张,团队成员之间的关系非常的微妙,主力程序员必须要保持自己的主力地位,对他们来说,必须努力写出优秀的代码,同时,你还需要承担项目进度的压力,并提防着其它的程序员。将程序掌握在手中是自己安全感的来源。压力如此之大,他们不得不每天工作12个小时以上。程序开发就如同噩梦一样。
  虽然未必所有的团队都如此不良的开发人文环境,但是或多或少都存在一些不好的环境因素。可以肯定的说,没有多少人愿意在这样一个开发环境中工作。这些环境因素都影响了沟通问题的形成。
XP的四大价值观中的一项就是沟通。XP中的沟通范围很广,有开发人员和客户之间的沟通(我们在需求和故事一章中也提到了沟通问题),有程序员和设计师之间的沟通,有程序员和测试人员之间的沟通。但是本文的重点集中在开发团队内部,即,如何改进开发团队内部的沟通质量。 }
改进沟通的实践-结对编程
XP方法论非常强调营造一种轻松的开发氛围,重视人的价值胜于重视过程。沟通是XP的一大价值观。XP中大量的实践是围绕沟通这个价值观设计的。例如,用户故事,现场客户,代码集体所有权等等,但是我们这里要强调的,是结对编程这一实践。本文中不对结对编程做介绍,这方面的资料有很多,没有必要在这里浪费笔墨。本文要讨论的,是我们如何在项目的角度上考虑结对编程。
  结对编程是一种非常有效的改善沟通的方法。一对编程人员是协作过程中最基本的沟通单元。在经典的XP方法中,结对编程指的是两个程序员在同一时间、同一机器前,主动的共同的解决统一问题。也许经理们听到这句话的第一个反应就是:"这不可能,我花了两倍的钱,却只做一个人的事情!"事实上,结对编程运用得当的话,是能够提高工作效率的,不但体现在进度上,还体现在代码质量、以及项目风险上。

个人编程
  个人编程往往会遇到各种各样的问题。在软件开发中,编写代码往往只占构建过程中很小一部分的时间,很多的时间花在调试代码、改进代码结构,以及针对需求或是设计的变更修改代码。想必很多人都有这样的经历,在一些关键的技术问题上卡壳,而单人进行研究不但费时费力,而且很容易导致士气的低落。
  在另一些时候,程序员往往需要在不同的设计选择之间进行权衡,而一个人做出技术决策往往造成内心的不安,这时候就希望能够有另一个同伴支持你做出决定。
  说代码是最严谨的工件是一点错也没有,任何一个微小的错误,例如缺少分号,都会造成程序运行的错误。虽然编译器能够检查大部分的错误,可是仍然会有一些深藏其中的,时不时出来捣乱的小错误。一个人的眼睛往往容易错过一些错误,但是两个人同时进行编码,这种出错的概率将会大幅度的下降。
  为了修正代码缺陷而进行的调试工作往往会占用大量的人月,如果代码缺陷到了测试团队的手中才被发现,修改缺陷的代价会很高,而如果代码缺陷一直持续到客户手中才被发现,这个代价更是惊人。而通过对开发人员配对,可以减少缺陷的数量。根据一些数据显示,结对编程可以让缺陷的数量减少15%。相对于在软件过程后期改正缺陷所付出的高昂代价,采用结对编程还是值得的。
  以上讨论的是个人编程中遇到的一些问题,这些是很小的问题,但是都会对开发人员的情绪、进度产生影响。而在一个团队环境中,这些问题还会扩大,升级为团队问题。
团队编程
  虽然软件组织规定了软件编码规范,但是编码规范不可能约定的过细,过细的编码规范不具备可操作性。因此不同人写出的代码仍然相差很大,优秀的代码和拙劣的代码同时存在,每个人都熟悉各自的代码,但却不愿意碰别人的代码。各种各样风格的代码逐渐产生的代码的混乱。这会产生很多问题。首先,软件组织内部复用的目标难以实现,如果人人都不愿意看别人的代码,你又如何建立一个内部复用的框架呢?现存的代码无法进行控制,旧项目的维护成本不断上升,团队积累也成为一句空话,
  其次,代码复审的难度加大。代码复审是非常重要的工作,但是代码的混乱将会加大代码复审的难度,因为复审小组的成员不得不花费时间来了解代码的风格,并做出指导。更糟糕的是,代码复审小组的成员往往都是软件组织中的重要成员,他们的时间都代表了高昂的成本。也许没有人仔细计算过这样的成本,但是这些成本累积起来,也会是一个令人吃惊的数字。
  再次,项目风险和组织风险都随之增大。这种在以项目开发为结算单位的软件开发组织中尤为明显,因项目开发人员离开而导致项目源代码难以维护的情况非常的普遍。对于已经完工的项目而言,这使得项目维护成本上升,对于尚未完工的项目而言,这会打乱现有的项目进度,导致项目进度的延后。
  最后,也是致命的一个问题,内部沟通难以有效的进行。软件开发不是一个单独的活动。优秀的程序员组成的团队未必就是一个优秀的团队。究其原因,大部分都是因为沟通不善造成的原因。组织内部的知识很难形成流动,开发人员之间难以共享知识,而新成员也无法从经验丰富的老员工那里学习。
  沟通不畅最终会积累形成组织软件设计平均水平无法提高的问题。软件设计属于脑力劳动,但是个人的知识覆盖程度和思考能力都有限,个人的设计往往都是有缺陷的,而雇佣大师级的开发人员的成本是相当高昂的,并不是所有的软件组织都能够像IBM或是微软那样雇佣大量的优秀人才。因此面对有限的人力资源(数量和质量两方面),关键的问题就在于如何让有限的资源发挥最大的作用。
软件工艺
  在参与一家软件组织的代码复审之后,我加入了这一小节的内容。既然是工艺,当然是一些很细微的环节,例如浏览集合的写法、类和方法的命令、注释的规则等等。这些都属于程序员自身修养的部分,但是很多组织恰恰是在这个环节上存在问题。编码的随意性导致了代码可理解性的下降,为团队共享代码设置了障碍,没有人会主动的去看别人的代码。在前面我们说代码的混乱会导致复审的困难,而代码混乱同时产生的另一个影响,就是软件组织的平均软件工艺水平无法提高。虽然每个程序员都希望能够编写优美的代码,但编写优美代码需要一定的毅力和时间,尤其是在项目时间压力大的时候,代码的优美性常常是被忽略的。但是,强制要求代码优美性并不容易实现,需要监督的成本,效果也难以令人满意。
  结对编程可以从组织结构上缓解这个问题。程序员大多是骄傲的,如果有一个同伴在身边,那程序员可拉不下脸来编写难看的代码。这是很有意思的现象,但是挺有效的。程序员通过这种方式,可以相互促进,提高编程工艺水平。虽然软件工艺解决的都是一些微小的问题,但是正是这些问题,最终影响到了软件的质量。从代码管理的角度上来说,管理的基本任务都是这些"小问题"。
过程保证
  结对编程可以在有效的解决这些问题的同时保证成本最小,这是结对编程之所以成为结对编程而不是三人编程的原因。在硬件设备的运行过程中,单点故障的最好解决方法是双机备份。这一思想运用到团队和过程上就形成了结对编程的基础。我们见过一个软件组织实施结对编程的初衷是为了保证产品的安全性,在产品的各个重要部件上都至少配备了两位负责人。一开始他们没有意识到他们朝着结对编程迈出了第一步,后来他们发现这种方法非常的有效,并针对这种方法进行扩展,形成了完整的结对编程体系。
  在传统的软件开发中,一般都会在软件过程中建立几个检查点(Check Point),在这个点上,软件的各个部分都需要进行检查,设计是否符合规范,是否满足需求,程序中是否存在缺陷。但是在每个Check Point上花费的时间往往是非常可怕的。每个CP上花费的工作包括:
熟悉他人的设计思路和代码风格
  将不同的系统整合起来
  对缺陷进行改进
  而结对编程的实践实际上就是将这部分的成本分摊到每一个人天中去。通过两两互配,让组织中所有的人都能能够熟悉软件的各个部分。这个成本在刚开始时确实会比较高,但是随着对结对编程理解的深入,这个成本会慢慢的降低。根据资料显示,结对编程并不是像大多数人想象的那样,会增加100%成本,这个数字取决于具体的实现形式,但绝对不会到100%。

活用 XP: (七)实战结对

结对编程的根本思路是改善开发团队内部的沟通质量。在实际情况中,不同的开发团队面临着不同的沟通问题。那么,该如何找到一个共通的指导思路呢,又该如何根据实际的情况进行调整呢?
成本权衡和策略选择
  从上一篇文章的讨论中,我们可以了解到,由于现实的因素,做到理想化的结对编程往往会有很大的阻力。这个时候,我们可以根据实际的情况进行调整,选用不同的方式。但我们如何评估这些方式的成本呢?设计结对,测试结对,复审结对等等的变通方式都存在一个问题:就是表面上看起来它们似乎既达到了结对的效果,又节省了成本。但是实际上,这个成本并没有节省,而是转移了。
  在项目中,为了令结对编程的思路更容易令人接受,我们采用了变通的做法,在设计和复审的时候结对,编码则由单个开发人员负责。A和B针对某项需求进行了2个小时的设计讨论,然后由B负责编码。但是B在编码的时候发现原先的设计存在考虑不周的情况,他决定对设计进行一些修改,这时候,他想通知A,但是此时A不在,于是B根据自己的思路调整了设计,并完成了实现。而在复审的时候,B不得不花上一段时间来向A说明设计变更的原因和细节。
  注意到,这个过程中,A和B不进行结对编码而节省的时间其实是转移到复审上来了。当然,复审上花费的时间可能要比编码的时间短得多。但是我们还必须看到,如果B变更后的设计也存在缺陷,A和B仍然需要花费一定的时间来改进设计和实现,这种情况也是有很大的可能性发生的。
  对结对编程的成本进行讨论并不是要下一个定论。对于不同的组织而言,这个成本是不确定的。对于某一些组织而言,理想的结对编程也许非常的合适,但对于另外一些组织来说就未必。重点在于,必须找到一种方法,使得团队之间的沟通能力得以增强。
  不同团队进行结对方式设计的时候的标准只有一种,就是如何改进沟通质量。不同的团队有着不同的沟通问题。找到这个沟通问题,才能够对症下药。有这样一个软件组织,他贯彻结对编程的思路很简单,就是为了减小人员流动对业务的影响。经过研究,我们发现这个组织有这么一些特点:产品经历过数代的演化,结构复杂;开发人员仅对自己负责的模块比较了解,全面掌握系统的人极少;产品拥有固定的客户群,客户时常有修改的需求;任何一个开发人员的流失都意味着他负责的模块在一段时间内无人接手;相对于模块无人负责的尴尬境地,增加一个开发人员的成本是可以接受的。在这样的一种情况下,该组织要求任何一个模块都必须有两个开发人员负责。事实上,采用了这一方法之后,人员并没有翻倍,因为维护老产品和开发新产品的工作是并行的,而且,同一个开发人员不仅仅只负责一个模块。虽然人员增多了,但是客户的满意度提高了,而开发力量也同时得到了增强,这个结果还是令人满意的。 可以看到,在这个例子中,结对的方式并不是XP中所描述的结对编程,它只是一种组织形式,但是在解决沟通问题上,两者的思路是相类似的。同样的,我们如果希望在自己的组织中应用结对,那么分析自己组织的沟通瓶颈的工作是少不了的。
设计结对
  设计结对的含义是某一模块的设计由双人完成,这里的设计并不是大规模的软件设计(对于大规模的前期设计而言,我们更倾向于让团队设计,请参看敏捷架构设计一文),而是在某个特性在编码之前的设计,这种设计的特点是持续的时间很短(只有几个小时或是几十分钟),但是对于整个代码的质量而言非常的重要,因为我们需要保证设计符合架构的原则,以及设计的灵活性,一致性等等,还需要保证设计的性能和速度。而某个特性在设计完成并进入编码之后,这部分特性就已经确定下来了。因此这种小规模的设计往往是软件开发中比较重要的细微点。在设计上配置双人,能够有效地提高代码质量。这种结对的思路是把成本花在关键的部件上,但是小规模设计结对的具体表现往往是两个人对某个问题的某种看法,他并不能以代码或是模型的形式来体现,对非编码者一方的约束比较小,而代码实现很可能和设计有所出入,这样,非实现者也难以获得这方面的知识。这种方式如果单独使用,容易演变成一种形式,效果并不是很好。因此,我们需要其它结对方式的配合。
测试结对
  这里的测试结对专指单元测试结对。结对的基本思路是A和B就类轮廓(类结构和公有方法)达成一致后,A编写测试代码,B编写代码来满足测试。如果B对设计的理解有误,那么代码一定通不过测试,如果A对设计的理解有误,B必须通知A重新编写测试。如果对XP的单元测试的改变非常熟悉的话,采用这种方式会有不错的效果。首先,测试代码本身就是小规模设计,而且它以一种规范的编码形式反映出来。只要测试代码足够优秀,它可以捕捉很多的设计缺陷。其次,这种方式是测试有限的一种变体,但是其效果要优于单个人的测试优先。因为一个人思考测试和设计难免有考虑不周的地方,但是如果两个人来考虑的话,测试往往能够发现出更多的问题或缺陷。刚开始使用这种方式的时候,可能会有些不习惯,但是熟悉之后就会比较顺利。
复审结对
  设计结对和测试结对都是在编码活动开始之前进行结对活动。但是复审结对则是在编码活动完成后进行的。A在B完成代码之后,需要对代码进行复审,复审的内容包括,代码是否体现了设计的意图,代码中是否存在缺陷,代码是否满足需求,代码是否符合一致性原则。一般这种复审都属于同级复审。当然根据我们下文中讨论的组织风格,也可以让有经验的程序员对没有经验的程序员进行指导。复审结对对软件过程的最大意义就在于它形成了一个持续复审的体制,它保留了复审制度的优点,而且可以克服复审制度中的缺点。例如花费时间长,遭至开发人员的反感,不能够进行彻底的复审等等。
  这三种方式的结对可以单独实行,也可以配合实行。这三种方式虽然都不是完整的结对编程实践,但是尽可能的获得了结对编程好处,而成本是相对低廉的。刚开始实施结对编程或是没有足够的资源采用结对编程的,可以采用以上的变通方式。
结对编程的组织风格
  结对编程并不是抓阄。成员的组织是需要一定的技巧的。基本的操作思路是,先找出沟通的关键性问题,然后针对问题入手,组织人员。举一个例子来说,对于某个项目而言,参与的开发人员经验较少,开发人员对组织的开发模式不熟悉,对开发的目标领域也同样不熟悉。主要的工作任务都压到了经验丰富的高级程序员身上。为了解决这个问题,在项目的头几次的迭代中,强制实行了配对制度,配对的基本思路是老手带新手,配对的实现是老手编写单元测试,要求新手实现,并共同进行代码复审(即采用测试结对和复审结对两种方式)。在完成一个小模块之后,老手就需要更换他的搭档,以保证在前两个迭代完成的时候,新手能够较为独立的进行开发工作。一开始的进度非常的不理想。老手也有着不同程度的怨言,认为这是在耽误时间。但高层管理人员听取了项目负责人的汇报之后,表示支持这种做法。在所有的新手都和老手搭配过后,情况有了很大的变化。系统的开发速度明显加快,团队内已经形成了密切沟通的氛围,老手们能够腾出手进行更复杂的设计和质量控制,更令人惊喜的是,已经有两名新手快要接近老手的水平了,这意味着,下一轮的结对编程过程中,他们将扮演当老手的角色。 这个例子告诉我们:
  针对重要的沟通问题实行结对编程
  一次解决一个问题
  结对编程的形式是针对沟通问题和环境特点而设计的
  优秀的实践必须坚持才能够有效果
  这是一个非常典型的组织风格,可以适用于很多的软件项目。它充分体现了沟通的重要性。更多的组织风格还包括:
  培训性项目。有时候这种项目也被称为摸索性项目,项目的最大目标就是为了研究和试验某些技术,以便在软件组织内部推广该技术。在这种项目中,知识的探索和研究往往要比成本更重要,因此可以采用结对编程的形式。
质量控制。软件开发的过程中,往往会有设计的核心部分。这部分的设计要么关系到软件的整体结构,要么就是代表了客户最为关心的需求。这部分的软件设计的好坏,将会直接影响客户对软件的看法。因此这部分的设计是值得投入双倍的开发力量的。而在很多的项目中,我们发现很多开发人员并没有意识到这一点,对开发人员来说,可能只是一些微不足道的错误,但这些错误有时候就会让客户留下非常不好的影响。因此,识别软件中的重要部分并投入更多的开发力量,往往能够令最终的软件质量有很大的提升。
  轮岗制度。研究表明,即便是知识管理做的再好的组织,仍然是有大量的知识是保存在人脑中的。而为了不形成一个个的信息孤岛,最好的信息流转的方式就是沟通。轮岗制度,或者说是Cross Training,正是为了解决这一问题而设计的。轮岗制度解决的另一个问题是,保证你的团队中既没有忙的团团转的人,也没有闲的发慌的人。这是管理着重要解决的问题,而轮岗制度可以很好的解决它。

活用 XP: (八)杂说

XP 还拥有其它优秀的实践,本文讨论了 XP 的另外三个实践,并研究如何在项目中灵活的使用它们。
代码集体所有权
XP提倡代码归属集体所有,这样做的理由是每个人都可以修改代码,而不是等待别人来修改代码。这种做法可以有效避免形成代码之间的鸿沟。但集体代码所有权也它的问题。
  我们尝试了由多人共享代码的做法,其目的是为了加强交流,避免出现一段代码只有一个人了解的情况。这种方法一开始工作的很好,但很快我们发现出现了很多的问题,类的定义变得不清晰了,某些类变得臃肿,我们闻到了"Large Class"的味道。更为糟糕的是,这些类的清理和重构相当的困难,因为这些类的客户太多了。正是因为这些类被广泛的使用,因此大家都对其进行修改和扩充,导致了代码的混乱。于是,我们加大了重构的力度,对这些类不断的进行审查和重构,但是新的问题又出现了,很难找到一个平衡点,既能够保持团队的敏捷性,又保证类的高度可用性。更糟糕的是,不同的人对这些类有着不同的了解和期望,导致了这些类的设计风格有些怪异。
  在本文中,我们不只一次的强调过,XP中所有的实践是配合使用的。项目中采用集体代码所有权不是不行,但有前提。在上面的例子中,我们至少犯了几个错误:
  没有考虑到团队成员之间配合的默契程度。只有团队成员之间知识程度相近,或是具有相近的思维观,例如都熟悉面向对象,都熟悉设计模式。这样他们才能够共同保证代码的质量。在项目中,我们尝试了一种新的做法,将集体所有权的范围缩小到组,这个组由三到四个资深的开发人员组成,称为核心组,负责构建基础的框架。这个组之间采用高效的共享代码机制。其他的开发人员利用核心组的成果进行工作,他们并不修改核心代码,但可以提供修改意见。
忽视了代码质量。不同人修改代码要比单人修改代码更容易导致代码质量的下降。必须考虑这个成本,并找出恢复代码质量的办法。审查是非常有效的手段,FDD(Feature Driven Development 特征驱动开发)方法就非常强调审查在项目中的作用。只要成本能够接受,再多的审查都是不过分的。
  代码标准化。这里说的代码标准化不仅仅指代码规范。还包括代码是不是具有可读性,代码的结构是否足够的清晰。这些都可能导致团队集体拥有代码时形成混乱。
  此外,还应该注意到集体代码要求能够频繁的集成代码。代码必须要快速的同步和集成,共享代码往往意味着同一个包,同一个类都有可能被同时修改。这样大大增加了引入bug的可能。尽快的同步代码时非常有必要的。
  如果一个软件组织不能够解决这些问题,冒然采用集体代码所有权的实践是比较危险的。相反,可以考虑采用个人代码所有权,或是微团队代码所有权。前者说的是个人对个人的代码负责,后者说的是两到四个人对某部分代码负责。
  不论是个人代码所有权还是微团队代码所有权,其立足的根本是有明确的开发人员对代码负责,他保证代码的统一设计思路和风格,负责代码的客户端接口,负责维护和改进代码,负责代码的相关文档,负责解释代码的运行机理。个人代码所有权是最清晰的做法,但其坏处和集体所有权正好相反。某个人的代码可能造成进度的瓶颈,任何一个人离开团队都会造成损失。
  个人代码所有权很容易理解,但微团队代码所有权就需要特别做解释了。他的组织思路非常类似于我们在结对编程中提倡的组织风格:
clip_image014

  不同的人负责不同的代码,人员之间形成交叉。这样的组织比较灵活,和结对编程有着异曲同工之妙。
持续集成
  在Martin Fowler的持续集成(在Agilechina网站上可以找到该文的中文译本)一文中,对持续继承有着这样的描述:
  在软件开发的领域里有各种各样的"最佳实践",它们经常被人们谈起,但是似乎很少有真正得到实现的。这些实践最基本、最有价值的就是:都有一个完全自动化的创建、测试过程,让开发团队可以每天多次创建他们的软件?quot;日创建"也是人们经常讨论的一个观点,McConnell在他的《快速软件开发》中将日创建作为一个最佳实践来推荐,同时日创建也是微软很出名的一项开发方法。但是,我们更支持XP社群的观点:日创建只是最低要求。一个完全自动化的过程让你可以每天完成多次创建,这是可以做到的,也是完全值得的。
  和本文提到的其它实践一样,持续集成的主要思路是将软件过程末期的软件继承分摊到软件的全过程。虽然没有办法评判两种持续方式的成本,但是持续集成可以获得很多额外的好处。单次集成最要命的地方是除bug的过程,尤其是那些隐藏的很深,让人觉得无从下手的bug。如果说写代码是一种享受,那修复bug的过程绝对是一种煎熬。在这个过程中花费的时间有时候是惊人的,更糟糕的问题是,这部分的时间根本无法估计,这令项目管理者头疼不已。向编码者询问进度时得到的回复永远都是"还差一点儿"。
  持续集成避免了这种尴尬处境,由于间隔的时间很短,集成中出现的问题可以很轻易的发现。即便无法定位错误,最差的情况也可以不把代码集成到软件中。这样,软件的质量就会比较高。此外,持续集成的另一个重要任务是运行自动化测试,保证所有的代码都是经过测试的,没有发生问题的。这里的测试源自于单元测试,在本文的测试一章,可以找到更为详细的讨论。 对一些没有持续集成经验的团队来说,持续集成像是一块吊的很高的饼,看得见却摸不着。要做好持续继承并不容易,但我们可以使用持续集成的思路,来接近持续集成的目标。
  持续集成最好的做法是自动化的构建和测试。这要求软件组织拥有很好的配置管理机制,以及丰富的测试脚本编写经验。对于一个企业应用软件来说(抱歉,我只有这方面的经验),软件设计包括很多的因素,要把这些因素都考虑到持续集成的过程中并不是一件容易的事情。因此很多组织都可能缺少这两个因素,但没有关系,我们可以利用半手工的方式来完成持续集成,然后再慢慢的将持续集成的过程自动化。这里提供一个半自动持续集成的思路和改进过程。 首先是定义职责。如果你采用了代码非集体所有权的形式,那么,请明确的指定各个类或包(对于面向过程语言来说是函数和模块)的负责团队(或个人)。同样,你还需要指定数据库模式的负责人。这些代码之间可能会有交叉的地方,但没有关系,只要保证沟通,少量的交叉职责没什么特别的。最好还必须指定一个专门负责持续集成的人,可以让项目中的不同人交替担任该职责。
  其次是定义自动化代码。所有的测试都必须写成测试代码的形式,并能够运行。所有的数据库模式定义也必须编写为DDL的形式,而不是使用数据库工具。千万别偷这个懒。总的原则是,能够写成代码的都写成代码,只有代码才是可以执行的。我承认,工作量是很大的,但这是必须的。
  再次是定义集成的频度。频度的制定取决于团队的规模和沟通质量。对于小的团队而言,一小时一次的集成也是可行的。对于大的项目,可以划分子团队,子团队中采用频繁集成(一小时一次),子团队之间采用日集成(每天集成一次)。有了子团队的集成保证,整个团队的集成一般不会有什么问题。
  接下来是使用工具。最需要的是版本控制工具,可以选用正式的,例如ClearCase。也可以选用简单的,例如SourceSafe,还可以选用免费的(CVS)。都没有关系,关键在于是否合用。代码和文档的集成都通过这个工具,数据库的集成则通过数据库管理员(就是第一步指定的负责数据库模式的人)。而集成负责人负责协调集成过程,保证集成的成功。
  最后是发现问题,这只是一个开始,你在集成的过程一定会遇到各种各样的问题的。例如集成的时间,数据的相关性,设计的耦合度,测试代码的变化等。没有关系,这里存在一个自适应的过程。一开始的持续集成过程一定是错误百出的,慢慢的就会稳定下来,这时候就是改进的时候了,改进的主要目标始终都是过程自动化。有时候,为了保证集成的质量,
代码标准
  代码标准是非常基础的管理常识。但是我们这里并不打算再赘述代码标准的问题。我们将重点讨论开发标准。开发标准包括各种各样的标准。例如过程的标志、文档的标准、设计模型的标准、代码风格的标准、变量命名的标准、大小写标准等等。在XP中,其实并不非常强调标准,因为XP提倡简单的做法,但有时候标准往往是违背这一准则的,因为它会带来额外的标准化成本。而重量级方法之所以笨重,过分遵循标准正是一大原因。
  但在实际的过程中,我认为宁可多投入一些资源在标准制定的执行上。这和国内目前的软件开发实际情况有关系。国外的轻量级方法出现在重量级方法之后,大部分的程序员都经历过强制性的标准化过程。但是国内不同,虽然轻量级方法很好,但是理解或执行不当的话,却常常导致画虎不成反类犬的后果。国内很多软件组织的开发过程仍然处于无序的状态,而开发人员也鲜有标准过程的经验,在这样一种情况下,盲目的推崇敏捷的做法,其实骨子里仍然是一种混沌的状态。
  因此,标准的制定和执行总是值得的,虽然会需要一定的成本,但这个成本同它的带来的改善沟通、促进积累等效益比起来不算什么。
  但是标准的制定绝对不是要把程序员上洗手间的时间都规范起来,如何保持标准的平衡是敏捷方法的重点。XP认为代码规范就已经足够了。但我认为至少还有几种标准是需要重视的:
  文档标准:这是一个大的话题,最好的文档标准是UML。一幅图胜过千言万语。当UML2.0出世之后,UML将会越来越强大。因此,使用UML来代替部分的文档是必要的。UML的相关资料有很多,这里不做过多的讨论,但关于UML的一句忠告是,不要试图在一开始就利用UML的所有类型的图,也不要一开始就利用UML所有特性。这种做法,和摆弄文字处理器的特性,与不写文档的做法没啥区别。
  设计标准:同样是一个大的话题。设计标准包括如何进行设计,如何表示设计,如何设计架构,如何设计各个层次等等问题。在一个组织中贯彻设计标准是一个长期的过程。但仍然是那句话,做总是比不做的好。
  代码风格标准:XP非常看中代码的可读性,代码可读性好代表了程序员的水平,设计人员的努力。提高代码可读性的最佳实践是重构和复审,这两项实践在本文的其它位置都有详细的讨论。
  界面标准:界面标准有时候只是一个很小的问题,但对于现代的软件来说,界面正扮演越来越重要的角色。而在软工领域,优秀界面的关键是一致性。同样的按钮必须有同样的大小、位置和字体。制定一份界面标准是非常关键的。 如何制定标准:标准的制定其实并不需要大量的说明文档,这些文档晦涩难懂,也不会有多少人会看,就算看了也未必会懂。只能是浪费时间。最好的方式是示例和培训。文档标准的说明就编写一份文档范例,并加以简短的说明。再辅以面对面的讲解,其效果要远远超过说明文档。

三、从一个项目谈XP在国内的应用

曲俊生(来自IBM DW中国)    2003年05月03日

我有话说……

  目前国内对于XP方面的研究和应用此起彼伏,各种关于XP的书籍争相出版,对于以XP为代表的"敏捷软件工程"方法的争论也在网络上随处可见。之所以出现这样的情况,是因为国内的用户在软件项目的实施过程中遇到了很多问题,例如项目的交付时间推迟、用户需求变更频繁等,我们的软件工程师迫切的希望能够找到解决问题的"银弹"。对于高度动态、通过非常短的迭代周期来应对需求变化的极限编程方法论来讲,确实能够从一定程度上解决问题。但是,对于国内的软件开发项目来说,XP并非"银弹",它的一些最佳实践不是都适合国内的情况。本文结合一个具体的软件开发项目,讨论一下XP 在国内的应用情况。

XP简介

传统软件开发方法

  在最近的数十年中,很多企业的CEO们都面临着增加盈利的压力,因此,他们采用各种方法,例如裁员、业务外包、BPR、ERP、CRM等等。以上种种,使得世界500强的大部分企业在20世纪90年代的后期一直保持者二位数的利润增长。但是很多迹象表明,在传统的企业业务模型中已经没有多少可供削减开支的地方,因此,需要进行彻底的改革。在软件开发领域,情况更是如此。

  自上个世纪60年代以来,软件工程思想逐渐形成与发展,也出现了很多软件开发模型与方法,例如瀑布模型、快速原型、增量模型和螺旋模型等[1]。而在90年代以后,卡耐基梅隆软件学院推出的CMM,更是对于软件开发的过程管理,提出了确切的衡量指标。但是,最近的研究表明,有50%的项目会拖延交付,有30%以上的项目会超出预算,软件开发领域的项目情况比软件工程刚刚提出的时候相比,只是有很小的提高。详细的数据[4](数据来自美国GSM研究机构, Michael Mah)如下表所示:

  传统的软件开发过程,以RUP为代表,强调项目的可控性,是一个用例驱动的基于UML和构件式架构的迭代增量式开发过程。RUP定义了初始、细化、实现和部署4个阶段,分别对应着关键里程碑的划分。RUP对于角色、流程、工件和活动的要求是灵活、可配置的,所以它广泛的适用于各种类型的项目。但是,在RUP的各个流程碑,都规定了要交付的成果,尤其是对于需求的变更以及文档,它强调及时的更新与同步。以上这些都决定了RUP是一种重量级的软件开发方法,比较适合大中型的项目和产品开发。

XP以及其核心价值

  最近,出现了很多轻量型的软件开发方法,例如水晶模型、适应模型以及极限编程等。它们都强调,软件开发是人与人合作进行的过程,因此成功的软件开发过程应该充分利用人的优势,而弱化人的缺点,突出了人在软件开发过程中的作用[2]。

Kent Beck在XP的开篇之作《Extreme Programming Explained - Embrace Change》中提出了极限编程这一创新的软件过程方法论。极限编程是一种高度动态的过程,它通过非常短的迭代周期来应对需求的变化。在极限编程中,包括四个基本活动:编码、测试、聆听与反馈,XP项目的状态变迁如下图所示[3][4]:

Kent Beck指出,XP有四个核心价值是我们应该注意的[3][4]:

  · 沟通:项目中发现的问题往往是由于开发人员与设计人员、设计人员与客户之间的沟通不畅造成的,因此,在XP项目中没有沟通是不可能的。

  · 简单:XP认为应该尽量保持代码的简单,只要它能工作就可以。Kent Beck指出与其实现一个复杂的的系统,不如设计一个能够满足目前需要的、简单的系统,因为你所考虑的情况可能永远都不会发生。

  · 反馈:尽快获得用户的反馈,并且越详细越好,使得开发人员能够保证自己的成果符合用户的需要。

  · 勇气:这是最重要的核心价值。因为XP强调要"拥抱变化",因此对于用户的反馈,要勇于对自己的代码进行修改,丢掉坏的代码。

  下面我们将要谈到的XP的最佳实践就体现了上述四个核心价值。实际上,XP中并没有多少新的观点,它的一些最佳实践也都是长久以来都在使用中的。

XP的适用环境

  从XP项目状态图以及它的核心价值中我们可以看到,XP弱化针对未来需求的设计,非常注重当前的简化。它的实践,有一个非常关键的假设就是开发人员只注重眼前需求而依赖重构来适应需求的变动所带来的风险与开销要小于需求变化使得事先充分设计失效的代价;反之,实施XP就是不明智的[5]。

  因此,XP适合规模小、进度紧、需求变化大、质量要求严的项目。它希望以最高的效率和质量来解决用户目前的问题,以最大的灵活性和最小的代价来满足用户未来的需求,XP在平衡短期和长期利益之间做了巧妙的选择。

  我们可以看到,XP并不是解决问题的"银弹",它也有不适合的情况。Beck曾经建议在以下情况下不宜采用XP:

  · 中大型的项目(项目团队超过10人);

  · 重构会导致大量开销的应用;

  · 需要很长的编译或者测试周期的系统;

  · 不容易进行测试的应用;

  · 团队人员异地分布的项目;

  · 不能接收XP文化的组织和团队;

项目概况及背景

  我们公司是亚洲领先的电子商务解决方案供应商,在J2EE架构的项目执行方面有丰富的经验,结合RUP形成了自己的一套电子商务项目实施方法论,并在多个项目中成功进行实施。同时,由于具体项目时间和成本的限制,也出现了许多问题,主要有以下两点:

  项目交付后,用户提出很多的修改意见,有些甚至涉及系统架构的修改:出现这种情况的主要原因是很多项目虽然是采用增量迭代式的开发周期,但是在部署前才发布版本,用户只是在项目部署后才看到真正的系统,因此会发现很多界面、流程等方面的问题;

  对于用户提交BUG的修改周期过长:开发人员在作开发的时候,对于单元测试的重视程度不够,模块开发结束后就提交给测试人员进行测试,而测试人员由于时间的关系,并不能发现所有的问题;在用户提交BUG后,开发人员由于项目接近尾声,对于代码的修改产生惰性,同时又没有形成有效的回归测试方法,因此,修改的周期比较长。

  针对XP的核心价值,可以看到,如果我们能够加强与用户的沟通、增加项目中测试实施的力度、提高开发人员的勇气,就可以从一定程度上解决上述问题。

  从2001年开始,公司内部展开对于XP等敏捷方法的研究,希望能够借鉴一些做法,来完善项目方法论。

2002年5月,我们决定在公司的一个新的项目中启用XP的一些最佳实践,来检验其效果。该项目是为一家国际知名手机生产厂商的合作伙伴提供手机配件定购、申请、回收等服务,项目的情况如下表所示:

  从上表中可以看出,该项目是一个小型项目,而且项目小组成员对于XP在项目开始之前都有一定的了解,另一方面,客户要求的项目周期比我们预期估计的时间有一定的余地,因此我们决定利用这个项目进行XP的试验性实践。

XP的最佳实践以及在项目中的应用

  在项目执行过程中,我们基本上还是采用RUP的软件过程,而没有死板的套用XP 的做法,例如:在需求分析阶段,我们还是采用Use Case来对需求进行描述,而不是XP规定的CRC卡片;在系统分析与设计阶段,首先进行系统的架构设计,而不是简单的套用XP的"简单设计"实践。

  下面我们结合项目的具体情况,讨论一下XP的12个最佳实践。

现场客户 ( On-site Customer )

  · XP: 要求至少有一名实际的客户代表在整个项目开发周期在现场负责确定需求、回答团队问题以及编写功能验收测试。

  · 评述:现场用户可以从一定程度上解决项目团队与客户沟通不畅的问题,但是对于国内用户来讲,目前阶段还不能保证有一定技术层次的客户常驻开发现场。解决问题的方法有两种:一是可以采用在客户那里现场开发的方式;二是采用有效的沟通方式。

  · 项目:首先,我们在项目合同签署前,向客户进行项目开发方法论的介绍,使得客户清楚项目开发的阶段、各个阶段要发布的成果以及需要客户提供的支持等;其次,由项目经理每周向客户汇报项目的进展情况,提供目前发布版本的位置,并提示客户系统相应的反馈与支持。

代码规范 ( Code Standards )

  · XP: 强调通过指定严格的代码规范来进行沟通,尽可能减少不必要的文档。

  · 评述: XP对于代码规范的实践,具有双重含义:一是希望通过建立统一的代码规范,来加强开发人员之间的沟通,同时为代码走查提供了一定的标准;二是希望减少项目开发过程中的文档,XP认为代码是最好的文档。

  对于目前国内的大多数项目团队来说,建立有效的代码规范,加强团队内代码的统一性,是理所当然的;但是,认为代码可以代替文档却是不可取的,因为代码的可读性与规范的文档相比合适由一定的差距。

  同时,如果没有统一的代码规范,代码全体拥有就无从谈起。

  · 项目: 在项目实施初期,就由项目的技术经理建立代码规范,并将其作为代码审查的标准。

每周40小时工作制 ( 40-hour Week )

  · XP: 要求项目团队人员每周工作时间不能超过40小时,加班不得连续超过两周,否则反而会影响生产率。

  · 评述: 该实践充分体现了XP的"以人为本"的原则。但是,如果要真正的实施下去,对于项目进度和工作量合理安排的要求就比较高。

  · 项目: 由于项目的工期比较充裕,因此,很幸运的是我们并没有违反该实践。

计划博弈 ( Planning Game )

XP: 要求结合项目进展和技术情况,确定下一阶段要开发与发布的系统范围。

  · 评述: 项目的计划在建立起来以后,需要根据项目的进展来进行调整,一成不变的计划是不存在。因此,项目团队需要控制风险、预见变化,从而制定有效、可行的项目计划。

  · 项目: 在系统实现前,我们首先按照需求的优先级做了迭代周期的划分,将高风险的需求优先实现;同时,项目团队每天早晨参加一个15分钟的项目会议,确定当天以及目前迭代周期中每个成员要完成的任务。

系统隐喻 ( System Metaphor )

  · XP: 通过隐喻来描述系统如何运作、新的功能以何种方式加入到系统。它通常包含了一些可以参照和比较的类和设计模式。XP不需要事先进行详细的架构设计。

  · 评述: XP在系统实现初期不需要进行详细的架构设计,而是在迭代周期中不断的细化架构。对于小型的系统或者架构设计的分析会推迟整个项目的计划的情况下,逐步细化系统架构倒是可以的;但是,对于大型系统或者是希望采用新架构的系统,就需要在项目初期进行相信的系统架构设计,并在第一个迭代周期中进行验证,同时在后续迭代周期中逐步进行细化。

  · 项目: 开发团队在设计初期,决定参照STRUTS框架,结合项目的情况,构建了针对工作流程处理的项目框架。首先,团队决定在第一个迭代周期实现配件申请的工作流程,在实际项目开发中验证了基本的程序框架;而后,又在其它迭代周期中,对框架逐渐精化。

简单设计 ( Simple Design )

  · XP: 认为代码的设计应该尽可能的简单,只要满足当前功能的要求,不多也不少。

  · 评述: 传统的软件开发过程,对于设计是自顶而下的,强调设计先行,在代码开始编写之前,要有一个完美的设计模型。它的前提是需求不变化,或者很少变化;而XP认为需求是会经常变化的,因此设计不能一蹴而就,而应该是一项持续进行的过程。

Kent Beck认为对于XP来说,简单设计应该满足以下几个原则:

1.成功执行所有的测试;

2.不包含重复的代码;

3.向所有的开发人员清晰地描述编码以及其内在关系;

4.尽可能包含最少的类与方法。

  对于国内大部分的软件开发组织来说,应该首先确定一个灵活的系统架构,而后在每个迭代周期的设计阶段可以采用XP的简单设计原则,将设计进行到底。

  · 项目: 在项目的系统架构经过验证后的迭代周期内,我们始终坚持简单设计的原则,并按照Kent Beck的四项原则来进行有效的验证。对于新的迭代周期中出现需要修改既有设计与代码的情况,首先对原有系统进行"代码重构",而后再增加新的功能。

测试驱动 ( Test-driven )

  · XP: 强调"测试先行"。在编码开始之前,首先将测试写好,而后再进行编码,直至所有的测试都得以通过。

  · 评述: RUP与XP对测试都是非常的重视,只是两者对于测试在整个项目开发周期内首先出现的位置处理不同。XP是一项测试驱动的软件开发过程,它认为测试先行使得开发人员对自己的代码有足够的信心,同时也有勇气进行代码重构。测试应该实现一定的自动化,同时能够清晰的给出测试成功或者失败的结果。在这方面,xUnit测试框架做了很多的工作,因此很多实施XP的团队,都采用它们进行测试工作。

  · 项目: 我们在项目初期就对JUNIT进行了一定的研究工作,在项目编码中,采用JBUILDER6提供的测试框架进行测试类的编写。但是,不是对所有的方法与用例都编写,而只是针对关键方法类、重要业务逻辑处理类等进行。

代码重构 ( Refactoring )

  · XP: 强调代码重构在其中的作用,认为开发人员应该经常进行重构,通常有两个关键点应该进行重构:对于一个功能实现和实现后。

  · 评述: 代码重构是指在不改变系统行为的前提下,重新调整、优化系统的内部结构以减少复杂性、消除冗余、增加灵活性和提高性能。重构不是XP所特有的行为,在任何的开发过程中都可能并且应该发生。

  在使用代码重构的时候要注意,不要过分的依赖重构,甚至轻视设计,否则,对于大中型的系统而言,将设计推迟或者干脆不作设计,会造成一场灾难。

  · 项目: 我们在项目中将JREFACTORY工具部署到JBuilder中进行代码的重构,重构的时间是在各个迭代周期的前后。代码重构在项目中的作用是改善既有设计,而不是代替设计。

成对编程 ( Pair Programming )

  · XP: 认为在项目中采用成对编程比独自编程更加有效。成对编程是由两个开发人员在同一台电脑上共同编写解决同一问题的代码,通常一个人负责写编码,而另一个负责保证代码的正确性与可读性。

  · 评述: 其实,成对编程是一种非正式的同级评审 ( Peer Review )。它要求成对编程的两个开发人员在性格和技能上应该相互匹配,目前在国内还不是十分适合推广。成对编程只是加强开发人员沟通与评审的一种方式,而非唯一的方式。具体的方式可以结合项目的情况进行。

  · 项目: 我们在项目中并没有采用成对编程的实践,而是在项目实施的各个阶段,加强了走查以及同级评审的力度。需求获取、设计与分析都有多人参与,在成果提交后,交叉进行走查;而在编码阶段,开发人员之间也要在每个迭代周期后进行同时评审。

  · XP: 认为开发小组的每个成员都有更改代码的权利,所有的人对于全部代码负责。

  · 评论: 代码全体拥有并不意味者开发人员可以互相推委责任,而是强调所有的人都要负责。如果一个开发人员的代码有错误,另外一个开发人员也可以进行BUG的修复。

  在目前,国内的软件开发组织,可以在一定程度上实施该实践,但是同时需要注意一定要有严格的代码控制管理。

  · 项目: 我们在项目开发初期,首先向开发团队进行"代码全体拥有"的教育,同时要求开发人员不仅要了解系统的架构、自己的代码,同时也要了解其它开发人员的工作以及代码情况。这个实践与同级评审有一定的互补作用,从而保证人员的变动不会对项目的进度造成很大的影响。

  在项目执行中,有一个开发人员由于参加培训,缺席项目执行一周,由于实行了"代码全体拥有"的实践,其它的开发人员成功地分担了该成员的测试与开发任务,从而保证项目的如期交付。

持续集成 ( Continuous Integration )

  · XP: 提倡在一天中集成系统多次,而且随着需求的改变,要不断的进行回归测试。因为,这样可以使得团队保持一个较高的开发速度,同时避免了一次系统集成的恶梦。

  · 评述: 持续集成也不是XP专有的最佳实践,著名的微软公司就有每日集成 ( Daily Build ) 的成功实践。但是,要注意的是,持续集成也需要良好的软件配置变更管理系统的有效支持。

  · 项目: 使用VSS作为软件配置管理系统,坚持每天进行一次的系统集成,将已经完成的功能有效地结合起来,进行测试。

小型发布 ( Small Release )

  · XP: 强调在非常短的周期内以递增的方式发布新版本,从而可以很容易地估计每个迭代周期的进度,便于控制工作量和风险;同时,也可以及时处理用户的反馈。

  · 评论: 小型发布突出体现了敏捷方法的优点。RUP强调迭代式的开发,对于系统的发布并没有作出过多的规定。用户在提交需求后,只有在部署时才能看到真正的系统,这样就不利于迅速获得用户的反馈。

  如果能够保证测试先行、代码重构、持续集成等最佳实践,实现小型发布也不是一件困难的事情,在有条件的组织可以考虑使用。

  · 项目: 项目在筹备阶段就配置了一台测试与发布服务器,在项目实施过程中,平均每两周(一个迭代周期结束后)进行一个小型发布;用户在发布后两个工作日内,向项目小组提交"用户接收测试报告",由项目经理评估测试报告,将有效的BUG提交至Rational Clear Case,并分配给相应的开发人员。项目小组应该在下一个迭代周期结束前修复所有用户提交的问题。

  以上是XP的最佳实践在项目中的应用情况,让我们查看以下该项目的详细统计数据:

  其中,项目执行过程中提交了一个"用户需求变更",该变更对于项目周期的影响为6个工作日。

  项目实施后,在用户接收测试中,只提交了2个BUG,而且在提交当天就得到了解决。目前,项目运行平稳,并得到了用户的好评。因此,我们认为,XP在该项目中的实施有效地保证了项目质量和项目周期。

总结

RUP与XP 都是在总结了很多项目实践的过程中发展起来的软件开发过程, 只是它们在处理需求变更的方法不同。其实它们还是有很多相似之处,例如,它们的基础都是面向对象方法,都重视代码、文档的最小化和设计的简化,采用动态适应变化的演进式迭代周期等等。

  目前,国内执行XP的理想情况应该是:在保持组织既有的开发过程和生命周期模型的情况下,根据应用类型、项目特点和组织文化,借鉴、采取个别对项目有效的XP做法,将RUP进行一定的剪裁,形成自己的软件开发过程。

  在项目的实施过程中,我们感觉到XP对于执行者的要求是比较高的,因为它要求开发团队必须具备熟练的代码设计技能和严格的测试保障技术,了解面向对象和模式,掌握了重构和OO测试技术,习惯了测试先行的开发方式等等。

  因此,对于目前国内的软件开发组织来说,应该首先加强对于软件开发过程化和系统架构设计的掌握,然后,才是利用XP等敏捷方法来完善软件开发过程。

 

来源: http://read.pudn.com/downloads135/doc/576061/%CF%B5%B7%D6%B1%CA%BC%C7/%C3%F4%BD%DD%B1%E0%B3%CC.doc

posted @ 2011-03-31 18:58  张云贵  Views(4127)  Comments(0Edit  收藏  举报