在《敏捷软件开发》上中下系列的上篇里,我们探讨了开发人员做法,也回顾了技术优势如何大幅提高软件质量。第一部分把重点主要放在了测试-编码-重整循环上。现在我们转到中间一个圆环,看看敏捷开发做法如何在小组这一层次发挥作用。
让小组高效工作——小组做法
一旦每个开发人员都在紧密围绕中心圆环的反馈循环工作时,我们就可以看看整个开发小组能够如何以更加敏捷的方式工作。小组这一层次的做法是敏捷开发的核心,因为它们能够显示出小组成员可以如何更加高效地一起工作并推动共同进行技术决策。我们将分别从四个方面来讨论小组的这种改变——设定基调、基于小组的代码编写标准、提高和保持效率、采用“统一小组”方式(包括与开发小组直接相关的东西)的首要步骤。我们给你举的例子来自于3Q Solutions公司,这是一家生产财物管理系统并完全使用敏捷开发方法的软件公司。
设定基调——第一步
敏捷软件开发的一个中心思想是小组朝着一个共同的目标工作。尽管很多流程都提倡小组工作,但是敏捷开发(真正)融合了支持小组工作的做法,并将小组工作放到了日常做法里。在开始讨论小组做法之前,我们需要先为小组设定一个基调,让他们开始感觉更像是一个真正的小组。
开放的工作空间
为更加开放的、基于小组的敏捷开发方式设定基调的一种最佳方法是为小组创造一种开放的工作空间(open workplace)。这意味着要建立一个或多个开放的区域,并尽最大可能进行沟通和合作。你想要专门了解什么样的环境能够让配对编程更容易。小格间和办公室是与敏捷开发开放工作空间格格不入的,所以应该避免其出现。在一家与Exoftware有合作关系的公司里,开放空间区域只被用于工作,里面只有用于配对编程、集成和构建软件的机器。其它的所有区域都留给带有Internet连接和电话的个人计算机。如果你有这样的空间,这就是应该考虑的东西,因为它有助于清楚地表明“当我们在工作区的时候,我们在工作”。
不要低估开放的工作空间对于小组的重要性——这就是为什么我们将其作为第一步的原因。下面的一幅照片就是是3Q Solutions开发小组的工作空间。
请注意,两张大桌子(下面没有文件柜)被摆在一起,构成了最适合配对编程的办公桌。
集体主义主人翁精神
我们想要介绍的下一个思想是集体主义主人翁精神(Collective Ownership)。敏捷编程的这种中心思想是让每一个人都对整个系统负责,每一个人都有更改代码的自由。这是一种重要的思维方法,因为它让小组的注意力都集中到了项目上,从而确保有一个共同的目标。与配对编程相关的其它步骤也强调这种思想,但是尽早引入这种思想是非常好的。
简单设计
敏捷开发崇尚简单的渐进设计,而不是剧烈的颠覆式设计。其目标是(首先)只指设计我们所了解的项目的那些部分,仅此而已,然后让该设计随着时间的推移而逐渐改进,这有助于提高灵活性并将变化导致的成本最小化。
我们就从3Q Solutions公司举一个例子,有一个客户要求获得一个规则引擎(rules engine)。小组传统的做法是花上数月时间开发规则引擎,然后可能还是无法把它卖出去。在与客户共同协商的情况下,小组决定设计一个满足规则引擎工作要求的最简单系统,并为每一条规则创建一个瘦垂直系统(a thin vertical system)。这就给予了客户他们真正需要的东西——可证明的规则——并确保投资抵消了投入的时间。这样小组可以在保持灵活性的同时从一开始就不断改进设计。简单设计是一个复杂的领域,研究它的最佳方法是获得外部的帮助。
重要的成功因素
- 赞同——整个开发小组坚持尝试使用敏捷开发以及开发小组圆环里的做法极其重要。如果不能这样坚持,开始甚至保持这样的做法都是非常困难的。
- 沟通——这一点怎么强调都不够。保证小组里高层次的沟通和对诸如集体主义主人翁精神这样的概念的理解非常重要。
- 配对编程——配对编程为很多小组做法提供支持,并将加强小组的沟通和凝聚力。
- 行政——如果没有行政上的支持,创造开放工作空间将会非常困难。在某些情况下,当行政机构的官僚主义作风盛行的时候,我们只用进行一些改变就行了。
- 每日例会——这一个每天早上进行的简单会议,供开发人员讨论当日面临的工作和问题。这样的会议应该是站着开的,因为其时间不应该超过几分钟。
小组编写代码——第二步
既然我们已经安置好了工作空间,并设定了小组的基调,我们现在就需要看看小组是如何处理代码的。我们这里的目标是确保所有通过配对编程编写的代码都能无缝地集成在一起,并且符合小组所承认的标准。通过推动第二步的进行,我们为支持第一步还有很大一段路要走。
代码编写标准
无论你是否决定采用敏捷编程,代码编写标准(coding standard)是一个非常好的最佳做法。这一步骤涉及让小组创立一套他们能够完全理解和坚持使用的代码编写标准。代码编写标准给予我们下列优势:
- 它让我们能够轻松地读懂别人的代码,这样所有人都可以进行(代码)交换。
- 代码为未来接手的小组提供了一个绝佳的信息源,即使有小组成员离队。
- 新的小组成员有一套指导方针——而不是瞎猜。
大多数小组都会利用已有的框架,并围绕其构建自己的一套标准。这里的关键要素是开始,立即解决小组正在奋力解决的问题,然后根据需要向前推进。也不要为了标准而去强行推行标准——这毕竟是整个小组需要共同认可、相信和使用的东西。下面是3Q公司代码编写标准文档的一小段。
CamelCase
CamelCase里的一切、类名称都以大写字母开始,而方法和字段的名称则不需要。
任何内容都不要放在有大括号的那一行。
字段以下划线开头: | _fieldname |
变量名不以下划线开头: | variableName |
方法: | public void methodName(String stringValue) |
接口公开
公共方法在类的最上部,后面跟有受保护的方法,然后才是私有方法。将所有继承自抽象类或者实现结构的方法都靠前放置,这是一个好主意。
尽可能做到立即就能找到一个类,并马上可以感觉到其功能以及它如何实现该功能,而不需要滚屏。
方法和类的名称
让其名称能够说明其功能。注意,不同的开发人员对于什么样的方法可读有不同的看法,他们更喜欢从周围的类,甚至是方法里的参数看出其作用。对这一点还存在争议,但是从名字来判断一个方法的作用是肯定可行的,因此:
doIForAllX()
就不理想,但是:
setupAllTableRowItems()
就很好。
而:
createRows()
可能更好。
[getVarvscalculateVar, 直接的getter对方法]
[不要将查询与作业混在一起]
方法的抽象
方法里的代码的抽象程度应该与同一个方法里其他所有代码的相同。这样的话,事件的自然过程能够被弄清楚。例如:
public void initializeDataBase()
{
_connection = createConnection ();
setUpTable();.
For (inti=0;i<tableRows;i++)
SetUpTableRow(i);
}
你稍稍一瞥,不用费什么功夫就可以读懂它。我们在3Q的时候非常珍惜视力,所以把这段代码变成了几个清晰明了的步骤,就像下面这样:
public void initializeDataBase()
{
setUpConnection();
setUpTable();
setUpTableRow();
}
这就有可能:
1.感觉到事情进展得怎么样
2.很容易就浏览到我们希望找到的类的确切部分(如果我们对表格行的设置感兴趣,我们就按住Ctrl点击setUpTableRow())。
得墨忒尔法则(Law of Demeter,即最少知识法则)
类应该只能够访问那些可以直接从其字段或者变量访问到的方法。对送进来的对象或者类自行实例化的对象的参考也是如此。
一般情况下,不要这么做……
publicintcalculateRetirementFund()
{
return getClient().getRetirementDetails().getRetirementFund();
}
……而要这么做:
public void calculateRetirementFund (RetirementDetails details)
{
return details.getRetirementFund();
}
这有助于为类设定范围并减少不必要的方法调用和委派。
顺序选择迭代
一般可以将方法分为下面三种类型。一系列事件,一个接一个;对集合的搜索或过滤;以及对集合或者数组的迭代。
收集方法、向量创建、向量设置、向量功能(vector dosomething)
集合一次又一次地出现,每次都是同样的问题,主要同类型有关。如果在集合里有一个任意的运行库强制转换(casting),那么总有出现错误类型的机会,导致强制转换异常的出现。
让集合变成可以针对具体类型,这使得在编译的时候检查往集合里加入的内容成为可能,同时还让根据类型来适应自定义的集合方法变得更容易。
不要使用临时变量——用查询来替代临时变量
在有关重整的书上查找这个内容——“用查询来替代临时变量”,最好不要抱着临时变量不放,它会增加代码的复杂性,给阅读者带来困难,同时减少了对算法作进一步重整的可能性。
测试打破常规
过多的设置意味着不佳的模式。你应该只需要设置那些与你正在测试的类直接相关的对象。
尽量让单元测试精细化,这将带来可移植性更强的代码,并将它推向更加清晰、更加独立的实现。
通过回调制针测试
回调制针(backpointer)完全就是个麻烦事,应该避免其出现。它会带来相当多的异常,状态模式就是其中一个。一定要了解自己实现回调指针的理由。如果理由是“它会起作用”,那么你就在失去什么东西。
视图测试——将测试三要素实例化
在一个构造完好的应用程序里,视图层应该从域抽象出来,达到一种不需要创建视图就能够测试该应用程序的程度。不够精细的测试需要更加经常地更改。见上文测试打破常规。
这只是来自一个不断改进的小例子。我再提醒一遍,从简单的开始,保持其基本框架,得到所有人的同意。
连续集成
瀑布式方法的一个缺陷是代码库的集成往往每隔数周或者数月之久才进行一次。新的错误常常会随着代码的集成而不断暴露出来,我们不得不花额外的时间来更正错误并重新集成。如果集成不是频繁进行,那么反馈就不可能像应该的那么紧密。敏捷开发要求进行更加频繁的集成——在3Q的案例里,这意味着每天要集成一到两次。
大多数小组一般都会有一台构建计算机,成对的开发人员能够利用其检查在测试-编码-重整循环里编写好的代码。每对开发人员都有确信其代码在被集成到代码库之前就已经经过测试和重整。一旦检验完毕,自动化的构建计算机就会编译所有的代码,运行所有的测试,并通过显示器(向小组)显示出来——构建过程是否需要引起注意——例如:新加入的代码有没有破坏什么东西?
这会做两件事情:
- 从代码被集成(进代码库)到小组意识到存在问题之间的时间间隔会被减到最小。
- 构建显示器将信息传达给整个小组——不论是集成成功完成——还是需要引起注意——这让小组可以立即作出相应的反应。
像这样频繁的集成意味着软件的构建是不停进行的,任何人在任何时候都可以参与构建过程。构建过程需要被自动化,以便使集成尽可能地容易,这是十分重要的。下面就是3Q公司的构建监视器的向小组传达信息的一个例子。
就如上面图画所显示的,构建服务器能够向小组提供额外的信息。
重要的成功因素
- 自动化——这需要成为一个自动化的过程。否则你将不得不专门找一个开发人员来维持构建过程——这可不是一个有意思的工作。首先就要营造环境,取得设备和实现自动化。
- TCR和配对编程——对于这一层次的集成工作,小组必须按照测试-编码-重整循环来进行,这样他们才有信心保证所有的问题只会发生在集成过程里。如果没有TCR循环,这一部分的过程将会非常困难。
- 按部就班——就像这个小标题说的,不慌不忙地从简单的地方开始,然后随着时间的推移来逐步改进——尤其是在代码编写标准这一块。
保持高效率——第三步
就如我们在《上篇》里说的,敏捷开发过程是一项工作强度很大的编程方式。除此之外,软件开发本身就压力重重,而小组累垮的可能性非常高。
可持续的步伐意味着开发小组现在和未来的工作都将非常艰苦。加班不是我们希望鼓励的事情,尽管有的时候需要如此。如果小组不得不加班工作,那么我们想要尝试将可持续步伐里的加班时间控制在一到两周而不是一到两个月。再强调一遍,敏捷开发是一项强度很大的工作;配对编程要求很多交互和重视,测试-编码-重整循环也是如此。尽管敏捷开发会引发我们小组的最大潜能,但是我们需要清楚很多时候的大量加班会累垮整个小组的风险。
重要的成功因素
这是管理者必须十分清楚的一个领域。确保小组在整个项目里保持合理的步伐是其主要职责。
开始转移到统一小组——第四步
有的人可能认为Metaphor的概念应该来得更早一些,但是我们建议在这一阶段快结束的时候才引入它,因为这是我们首次提到客户/业务方(customer/business)。Metaphor是客户与开发人员之间系统的通用语言。它看起来可能不重要,但是以Exoftware的政府顾客为例,开发小组一般都把业务方(也就是定义系统需求的人)当作客户。但是对于业务方而言,“客户”指的是最终用户。这就导致开发人员和“业务方”之间的困惑和挫折。
Metaphor的作用不只是一门通用语言——它还与上下文和对系统是什么的高层次理解有关。在这里我们能够采取步骤做到真正地与我们的业务合作伙伴沟通并共享共同的目标。3Q公司使用一种叫做Adaptor Tree Hierarchy体系,它通过一门客户/业务方共同认可的语言给予开发人员一个广阔的系统视野。例如:
ThreeQData
- todaysDate
- marriage
- spouse
- economicindicators
- client
- lossofincomestory
- annualincome
- coveramortisationeroision
- ...
- managedfundstory
- pensionstory
这个树形结构的每一部分都可以扩展出更多细节,能够轻易地改变,并提供一个很好的系统视角,同为整个小组提供一门通用的语言。
重要的成功因素
- 坚持到底——只有当你坚持使用的时候Metaphor才会有效。它将会成为日常语言的一部分,但是适应它需要花时间。
- 从基本的开始——从Metaphor的基本框架开始,了解它,使用它,然后以此为基础来创建它。
- 取得帮助——让尽可能多的相关业务方/客户参与Metaphor的创建——让其他人尽早参与进来是至关重要的。
敏捷开发的小组做法的目的是帮助小组把重点放在集体工作上,并理解其共有的做法和目标。尽管有的做法,比如代码编写标准,能够在隔离的情况下完成,但是如果与具体的开发人员做法,例如测试-编码-重整和配对编程结合起来,那么这些小组做法将发挥最大效用。
本系列的最后一部分将探讨开发人员小组如何开始同客户方/业务构成“统一小组”。
Brian Swan是Exoftware公司教授敏捷开发的指导老师。他在敏捷开发的技术和管理方面具有相当丰富的经验,曾经带领很多小组成功地转换到了敏捷开发,并以敏捷开发的思想和做法来培训开发人员和管理人员。他在Exoftware公司和在敏捷开发方面的工作使他到过很多公司,并对其开发小组产生了持续的、积极的影响。Brian先前的经验还包括担任Napier大学的讲师,讲授软件开发和人机互动。Brian可以通过电子邮件联系上。