敏捷软件开发——第2章 极限编程概述

第2章 极限编程概述程序员

做为开发人员,咱们应该记住,XP并不是惟一选择。--Pete McBreen,软件技术专家

在第1章中,咱们概述了有关敏捷软件开发方法方面的内容,但它没有确切地告诉咱们去作些什么;其中给出了一些泛泛的陈述和目标,却没有给出实际的指导方法。本章要改变这种情况。

2.1  极限编程实践

2.1.1  完整团队

咱们但愿客户、管理者和开发人员紧密地工做在一块儿,以便于彼此知晓对方所面临的问题,并共同去解决这些问题。谁是客户?XP团队中的客户是指定义产品的特性并排列这些特性优先级的人或者团体。有时,客户是和开发人员同属一家公司的一组业务分析师、质量保证专家和/或者市场专家。有时,客户是用户团体委派的用户表明。有时,客户事实上是支付开发费用的人。可是在XP项目中,不管谁是客户,他们都是可以和团队一块儿工做的团队成员。

最好的状况是客户和开发人员在同一个房间中工做,次一点的状况是客户和开发人员之间的工做距离在100m之内。距离越大,客户就越难成为真正的团队成员。若是客户工做在另一幢建筑或另一个州,那么他将会很难融合到团队中来。

若是确实没法和客户工做在一块儿,该怎么办呢?个人建议是去寻找可以在一块儿工做、愿意并可以代替真正客户的人。


2.1.2  用户故事

为了进行项目计划,必需要了解需求,可是却无需了解得太多。对于作计划而言,了解需求只须要到可以估算它的程度就足够了。你可能认为,为了对需求进行估算,就必需要了解该需求的全部细节。其实并不是如此。你必须知道存在不少的细节,也必须知道细节的大体分类,可是你没必要知道特定的细节。

需求的具体细节极可能会随时间而改变,一旦客户开始看到集成到一块儿的系统,就更会如此。看到新系统的问世是关注需求的最好时刻。所以,不要去捕获某个在很长一段时间以后才会实现的需求的特定细节,不然极可能会致使无用功以及对需求不成熟的关注。

在XP中,咱们和客户反复讨论,以获取对于需求细节的理解,可是不去记录那些细节。咱们更愿意客户在索引卡片上写下一些共识的言语,这些只言片语能够提醒咱们记起此次交谈。基本上在客户进行书写的同一时刻,开发人员在该卡片上写下对应于卡片上需求的估算。估算是基于在和客户进行交谈期间所获得的对于细节的理解进行的。

用户故事(user story)就是正在进行的关于需求的谈话的助记符。它是一个计划工具,客户可使用它并根据需求的优先级和估算代价来安排实现该需求的时间。

2.1.3  短交付周期

XP项目每两周交付一次能够工做的软件。每两周的迭代都实现了利益相关者的一些需求。在每次迭代结束时,会给利益相关者演示迭代生成的系统,以获得他们的反馈。

迭代计划

每次迭代一般耗时两周。迭代是一次较小的交付,可能会被加入到产品中,也可能不会。迭代计划由一组用户故事组成,这些用户故事是客户根据开发人员肯定的预算选出来的。

开发人员经过度量在之前的迭代中所完成的工做量来为本次迭代设定预算。只要估算成本的总量不超过预算,客户就能够为本次迭代选择任意数量的用户故事。

一旦迭代开始,客户就赞成再也不修改当次迭代中用户故事的定义和优先级别。迭代期间,开发人员能够自由地将用户故事分解成任务(task),并依据最具技术和商业意义的顺序来开发这些任务。

发布计划

XP团队一般会建立一个发布计划来规划出随后大约6次迭代的内容。这就是所谓的发布计划。一次发布一般须要3个月的工做。它表示了一次较大的交付,一般这次交付会被加入到产品中。发布计划是由客户根据开发人员给出的预算所选择的、排好优先级别的一组用户故事组成。

开发人员经过度量在之前的发布中所完成的工做量来为本次发布设定预算。只要估算成本的总量不超过预算,客户就能够为本次发布选择任意数目的用户故事。客户一样能够决定在本次发布中用户故事的实现顺序。若是开发团队强烈要求的话,客户能够经过指明哪些用户故事应该在哪次迭代中完成的方式,制订出发布中最初几回迭代的内容。

发布计划不是一成不变的。客户能够随时改变发布的内容。他能够取消用户故事,编写新的用户故事,或者改变用户故事的优先级别。可是,客户应该尽可能不去更改一次迭代。

2.1.4  验收测试

能够以客户指定的验收测试的形式来记录有关用户故事的细节。用户故事的验收测试是在就要实现该用户故事以前,或者在实现该用户故事的同时才开始编写的。验收测试使用脚本语言编写,这样它们能够自动、反复地运行 。这些测试共同来验证系统是否按照客户指定的行为运转。

验收测试是由业务分析师、质量保证专家以及测试人员在迭代期间编写的。编写验收测试使用的语言对于程序员、客户以及业务人员来讲都很容易阅读和理解。程序员就是从这些测试中了解他们正在实现的故事的真实工做细节。这些测试成为真正的项目需求文档。验收测试描述了每一个特性的全部细节,并用做验证这些特性是否被正确完成的决定性依据。

一旦经过一项验收测试,就将该测试加入到已经经过的验收测试集合中,并决不容许该测试再次失败。这个不断增加的验收测试集合天天会屡次运行,每当系统被建立时,都要运行这个验收测试集。若是一项验收测试失败了,那么系统建立就宣告失败。于是,一项需求一旦被实现,就再不会遭到破坏。系统从一种工做状态迁移到另外一种工做状态,期间,系统的不能工做状态时间决不容许超过几个小时。

2.1.5  结对编程

代码都是由结对的程序员使用同一台工做站共同完成的。结对人员中,一个控制键盘并输入代码。另外一个观察着输入的代码,寻找着代码中的错误和能够改进的地方 。两我的认真地进行着交互。他们都全身心地投入到软件的编写中。

两人频繁互换角色。控制键盘的可能累了或者遇到了困难,他的同伴会取得键盘的控制权。在一个小时内,键盘可能在他们之间来回传递好几回。最终生成的代码是由他们两人共同设计、共同编写的,两人功劳均等。

结对的关系要常常变换。天天至少要改变一次,这样每一个程序员在一天中能够在两个不一样的结对中工做。在一次迭代期间,每一个团队成员应该和全部其余的团队成员在一块儿工做过,而且他们应该参与了本次迭代中所涉及的每项工做。

结对编程会极大地促进知识在团队中的传播。仍然会须要一些专业知识,那些须要必定专业知识的任务一般须要合适的专家去完成,可是那些专家几乎将会和团队中的全部其余人结对。这将加快专业知识在团队中的传播。这样,在紧要关头,其余团队成员就可以代替所须要的专家。Williams 和Nosek 的研究代表,结对非但不会下降编程人员的效率,反而会大大减小缺陷率。

2.1.6  测试驱动开发

第4章会详细地讨论这个主题。在此,咱们仅进行大体的介绍。

编写全部产品代码的目的都是为了使失败的单元测试可以经过。首先编写一个单元测试,因为它要测试的功能还不存在,因此它会运行失败。而后,编写代码使测试经过。

编写测试用例和代码之间的更迭速度是很快的,基本上在几分钟左右。测试用例和代码共同演化,其中测试用例按部就班地对代码的编写进行指导(参见第6章中的例子)。

做为结果,一个很是完整的测试用例集就和代码一块儿发展起来。程序员可使用这些测试来检查程序是否正确地工做。若是结对的程序员对代码进行了小的更改,那么他们能够运行测试,以确保更改没有对程序形成任何的破坏。这会很是有利于重构(在本章后面介绍)。

当为了使测试用例经过而编写代码时,那么所编写的代码天生就是可测试的。更重要的是,这样作会强烈地激发你去解除各个模块间的耦合,以便可以独立地对它们进行测试。于是,以这种方式编写的代码的设计每每具备更弱的耦合。面向对象设计的原则在进行这种解耦方面具备巨大的帮助做用(参见本书第二部分)。


2.1.7  集体全部

每一对编程者都具备签出(check out)任何模块并对它进行改进的权力。每一个程序员都不会对任何一个特定的模块或技术单独负责。每一个人都参与GUI方面的工做 ,每一个人都参与中间件方面的工做,每一个人都参与数据库方面的工做。任何人都不会比其余人在一个模块或者技术上具备更多的权威。

这并不意味着XP不须要专业知识。若是你的专业领域是有关GUI的,那么你最有可能去从事GUI方面的任务,可是你也将会被邀请去和别人结对从事有关中间件和数据库方面的任务。若是你决定去学习另外一门专业知识,那么你能够承担相关的任务,并和可以传授你这方面知识的专家一块儿工做。你不会被限制在本身的专业领域。

2.1.8  持续集成

程序员天天会屡次签入(check in)他们的代码并进行集成。规则很简单:第一个签入的只要完成签入就能够了,全部后面签入的人负责代码的合并工做。

XP团队使用非阻塞的源代码控制工具。这就意味着程序员能够在任什么时候候签出任何模块,而无论是否有其余人已经签出了这个模块。当程序员完成了对于模块的修改并把该模块签入时,他必须把他所作的改动和在他前面签入该模块的程序员所做的任何改动进行合并。为了不合并的时间过长,团队的成员会很是频繁地检查他们的模块。

结对人员会在一项任务上工做一到两个小时。他们建立测试用例和产品代码。在某个适当的间歇点,也许远在这项任务完成以前,他们决定把代码签入回去。他们首先确保全部的测试都可以经过,而后把新的代码集成进当前的代码库中。若是须要,他们会对代码进行合并。若是有必要,他们会和在签入上有冲突的其余程序员协商。一旦集成进了他们的更改,他们就构建新的系统。他们运行系统中的每个测试,包括当前全部有效的验收测试。若是他们破坏了原先能够工做的部分,他们会进行修正。一旦全部的测试都经过了,他们就算完成了这次签入工做。

于是,XP团队天天会进行屡次系统构建。他们会从头开始建立整个系统 。若是系统的最终结果是一张CD,他们就刻录该CD。若是系统的最终结果是一个能够访问的Web站点,他们就安装该Web站点,或许会把它安装在一个测试服务器上。

2.1.9  可持续的开发速度

软件项目不是全速短跑,它是马拉松长跑。那些一跃过起跑线就开始尽力狂奔的团队将会在远离终点前就筋疲力尽。为了快速地完成开发,团队必需要以一种可持续的速度前进。团队必须保持旺盛的精力和敏锐的警觉。团队必需要有意识地保持稳定、适中的速度。

XP的规则不容许团队加班工做。在版本发布前的一个星期是该规则的惟一例外。若是发布目标就在眼前而且可以一蹴而就,则容许加班。

2.1.10  开放的工做空间

团队在一个开放的房间中一块儿工做。房间中有一些桌子。每张桌子上摆放了两到三台工做站。每台工做站前有两把椅子。墙壁上挂满了状态图表、任务明细表、UML图,等等。

房间里充满了交谈的嗡嗡声,结对编程的两人坐在互相可以听获得的距离内,每一个人均可以得知另外一人是否遇到了麻烦,每一个人都了解对方的工做状态,程序员们都处在适合于激烈地进行讨论的位置上。

可能有人认为这种环境会分散人的注意力。很容易会让人担忧因为持续的噪音和干扰而一事无成。事实上并不是如此。并且,密歇根大学的一项研究代表,在"充满积极讨论的屋子"(war room)里工做,生产率非但不会下降,反而会成倍地提升 。

2.1.11  计划游戏
 

第3章中会详细介绍XP的计划游戏。在这里,仅作简要介绍。

计划游戏(planning game)的本质是划分业务和开发之间的职责。业务人员(也就是客户)决定特性的重要性,开发人员决定实现一个特性所花费的代价。

在每次发布和迭代的开始,开发人员向客户提供一个预算。客户选择那些所需的代价合计起来小于等于该预算的用户故事。开发者所提供的预算是基于他们在最近一次迭代或者发布中所完成的工做量进行的。

依据这些简单的规则,采用短周期迭代和频繁的发布,很快客户和开发人员就会适应项目的开发节奏。客户会了解开发人员的开发速度。基于这种了解,客户可以肯定项目会持续多长时间,以及会花费多少成本。

2.1.12  简单设计

XP团队使他们的设计尽量的简单、有表达力。此外,他们仅仅关注于计划在本次迭代中要完成的用户故事,而不会考虑那些将来的用户故事。团队更愿意在一次次的迭代中不断地变迁系统的设计,使之对正在实现的用户故事而言始终保持在最优状态。

这意味着XP团队的工做可能不会从基础设施开始。他们并不先去选择数据库或者中间件,而是先以最简单的可能方式实现第一批用户故事。只有当出现一个用户故事迫切须要基础设施时,他们才会引入该基础设施。

下面3条XP指导原则(mantra)能够对开发人员进行指导。

(1) 考虑可以工做的最简单的事情。XP团队老是尽量寻找能实现当前用户故事的最简单的设计。在实现当前的用户故事时,若是可以使用平面文件,就不去使用数据库;若是可以使用简单的socket链接,就不去使用ORB或者Web Service;若是可以不使用多线程,就别去用它。咱们尽可能考虑用最简单的方法来实现当前的用户故事。而后,选择一种咱们可以实际获得的和该简单性最接近的解决方案。

(2) 你不须要它。是的,可是咱们知道总有一天会须要数据库,会须要ORB,也总有一天得去支持多用户。因此,咱们如今就须要为那些东西作好准备,不是吗?

若是在确实须要基础设施前拒绝引入它,那么会发生什么呢?XP团队会对此进行认真的考虑。他们开始时假设将不须要那些基础设施。只有在有证据,或者至少有十分明显的迹像代表如今引入这些基础设施比继续等待更加合算时,团队才会引入这些基础设施。

(3) 一次,而且只有一次。极限编程者不能容忍重复的代码。不管在哪里发现重复的代码,他们都会消除这些重复。

致使代码重复的因素有许多。最明显的是经过鼠标选中一段代码,而后四处进行粘贴。当发现那些重复的代码时,咱们会经过建立一个函数或基类的方法来消除它们。有时两个或多个算法很是类似,可是它们之间又存在有微妙的差异,咱们会把它们变成函数,或者使用TEMPLATE METHOD模式(请参见第22章)。不管重复代码源于何处,一旦发现,就必须被消除。

消除重复最好的方法就是抽象。毕竟,若是两种事物类似的话,一定存在某种抽象可以统一它们。这样,消除重复的行为会迫使团队提炼出许多的抽象,并进一步减小代码间的耦合。

2.1.13  重构

第5章会对重构进行详细的讨论 ,下面只是一个简单的介绍。

代码每每会腐化。随着咱们添加一个又一个的特性,处理一个又一个的错误,代码的结构会逐渐退化。若是对此置之不理的话,这种退化最终会致使纠结不清、难于维护的混乱代码。

XP团队经过常常性的代码重构来扭转这种退化。重构就是在不改变代码行为的前提下,对其进行一系列小的改造,旨在改进系统结构的实践活动。每一个改造都是微不足道的,几乎不值得去作。可是全部的这些改造叠加在一块儿,就造成了对系统设计和构架显著的改进。

在每次细微改造以后,咱们都会运行单元测试以确保改造没有形成任何破坏,而后再去作下一次改造。如此往复,周而复始。经过这种方式,咱们能够在改造系统设计的同时,保持系统能够工做。

重构是持续进行的,而不是在项目结束时、发布版本时、迭代结束时甚至天天快下班时才进行的。重构是咱们每隔一个小时或者半个小时就要去作的事情。经过重构,咱们能够持续地保持代码尽量干净、简单而且具备表达力。

2.1.14  隐喻

隐喻(metaphor)是惟一一个不具体、不直接的XP实践,也是全部XP实践中最难理解的一个。极限编程者在本质上都是务实主义者,隐喻这个缺少具体定义的概念使咱们以为很不舒服。的确,一些XP的支持者常常讨论把隐喻从XP的实践中去除。然而,在某种意义上,隐喻倒是XP全部实践中最重要的实践之一。

想象一下智力拼图玩具。你怎样知道如何把各个小块拼在一块儿?显然,每一块都与其余块相邻,而且它的形状必须与相邻的块完美地吻合。若是你眼睛看不见可是具备很好的触觉,那么经过持之以恒地筛选每一个小块,不断地尝试它们的位置,也可以拼出整个图形。

可是,相对于各个小块的形状而言,还有一种更为强大的力量把这些复杂的小块拼装在一块儿。这就是整张拼图的图案。图案是真正的向导。它的力量是如此之大,以致于若是图案中相邻的两块不具备互相吻合的形状,那么你就能够判定拼图玩具的制做者把玩具作错了。

这就是隐喻。它是将整个系统联系在一块儿的全局视图。它是系统的愿景,是它使得全部单独模块的位置和外观变得明显直观。若是模块的外观与整个系统的隐喻不符,那么你就知道该模块是错误的。

隐喻一般能够归结为一个名字系统。这些名字提供了一个系统组成元素的词汇表,而且有助于定义它们之间关系。

例如,我曾经开发过一个以每秒60个字符的速度将文本输出到屏幕的系统。以这样的速度,字符充满整个屏幕须要一段时间。因此咱们让产生文本的程序把产生的文本放到一个缓冲区中。当缓冲区满了的时候,咱们把该程序交换到磁盘上。当缓冲区快要变空时,咱们把该程序交换回来并让它继续运行。

咱们用装卸卡车拖运垃圾来比喻整个系统。缓冲区是小卡车。屏幕是垃圾场。程序是垃圾制造者。全部的名字相互吻合,这有助于咱们从总体上去考虑系统。

举另外一个例子,我曾经开发过一个分析网络流量的系统。每30分钟,系统会轮询几十个网络适配器,并从中获取监控数据。每一个网络适配器为咱们提供一小块由几个单独变量组成的数据。咱们称这些数据块为"面包切片"。这些面包切片是待分析的原始数据。分析程序"烤制"这些切片,于是被称为"烤面包机"。咱们把数据块中的单个变量称为"面包屑"。总之,它是一个有用而且有趣的隐喻。

固然,隐喻不只仅是一个名字系统。隐喻是系统的愿景,它指导着全部开发者去选择合适的名字,把函数放到合适的位置,建立出新的合适的类和方法,等等。

算法

2.2  结论

极限编程是一组简单、具体的实践,这些实践结合在一块儿造成了一个敏捷开发过程。极限编程是一种优良、通用的软件开发方法。对于大多数项目团队来讲,能够拿来直接采用,也能够增长一些实践,或者对其中的一些实践进行修改后再采用。数据库

相关文章
相关标签/搜索