本节书摘来自异步社区《Microsoft.NET企业级应用架构设计(第2版)》一书中的第1章,第1.1节,作者: 【意】Dino Esposito(埃斯波西托) , Andrea Saltarello(索尔塔雷罗)著,更多章节内容可以访问云栖社区“异步社区”公众号查看
第1章 今天的架构师和架构
在计算机的最初年代,硬件成本远远大于软件成本。数十年之后,我们发现情况有了根本的变化。整个工业有了显著的进步,而硬件成本也急剧下降。另一方面,软件成本却大幅上升,这主要是因为开发自定义企业软件的复杂性提升了。
这种情况催生了一系列的准则,并以此指导工程师设计这类系统。架构这个术语源自建筑行业,现已普遍用于描述规划、设计和实现软件密集型系统的艺术。当我们两个还是青少年时,《爱是……》这部漫画(http://www.loveiscartoon.com)
正值流行。每期漫画都会为青少年推荐一则爱的名言。其中一期漫画说过这样的话:“爱是必需品,不是奢侈品。”是的,这对于软件架构也同样适用。
在第1章里,我们将会试着分享我们对架构的看法,以及确定并实现它的方法。其中,我们将会了解架构师在这个过程里扮演的角色,以及我们曾经遇到的控制软件项目运作的基本规律。毫无疑问,我们的经验仅仅是我们自己的。虽然我们曾在不同规模的有趣项目里工作过,但是我们的经验仍然受限于我们的见识以及教训。尽管如此,我们希望你把这本书看作一个出发点,和你的团队耐心、详细地探讨你们如何构建可以正常工作的东西。
注意:
你在本书里看到的一些定义来自于国际标准,其他则反映我们个人的见解、经验和感受。我们将会讲到架构的一些公认的最佳实践,但会辅以我们自己的经验作为补充。我们希望这种组合方式可以帮你把枯燥的国际标准和你在现实世界里看到的东西联系起来。
1.1 软件架构到底是什么
本书的其中一个作者曾经和一个架构工作室有着密切的联系。有一天,他们在讨论的时候突然提出这样一个问题:架构是什么?是艺术,还是仅仅为客户进行构建?
在软件里,架构这个术语恰到好处地指代为客户构建系统。仅此而已,不多也不少。
也就是说,详细描述为客户构建系统所涉及的东西是最难的部分。我们经常思索是否真的有定论。这个问题我们问过无数次,每次都得到相同的答案。虽然这条路不好走,但是我们也要尝试一下。最糟糕的结果是变成一场头脑风暴,但头脑风暴并没什么坏处。
那么,让我们试着理清软件架构是什么,至少搞清我们希望它是什么。
注意:
架构和架构师这两个术语常常形影不离。架构师是一个角色,我们将在本章稍后讨论架构师这个角色的职责。然而,架构师这个术语仍然让人认为是一种职业。不幸的是,这个术语背后的专业人士并非全能的。这个头衔通常带有修饰语,如企业、解决方案、安全等。除非我们特别指明,否则我们所说的专业人士通常是指软件架构师或者解决方案架构师。
1.1.1 把架构原则应用到软件中
我们之中的许多人在成为软件专家的过程中,一开始认为软件与现实世界的建筑有某种程度的关联,后来又反对这种观点。起初把这种类比引入软件行业是为了表达在构建计算机程序之前需要规划和设计。但是,设计和构建民用建筑与设计和构建可用的软件系统之间存在着本质的差异。
软件的存在是为了自动化商业流程和人类行为,而民用建筑则作为社区的服务而存在。一般地,民用建筑服务针对绝大多数人并提供最优的规划和实现。他们的目的并非为了满足少数利益相关者。此外,民用建筑的成本很高,造起来也很复杂,因此没人会对已经敲定的项目随意做出更改。至少,更改也只是偶然出现。
软件则不同。
软件通常只为小部分人构建,其中一些人自掏腰包,希望得到一些东西改善他们的组织运作。因此,需求会持续地提炼、添加、移除以及重新划分优先级。使用这种方式构建软件需要敏捷性,民用建筑采用大量前期设计的做法并不适用于此。
简而言之,今天的建筑架构和软件架构之间的距离已经没有20年前那么接近了。但是,许多字典仍在“架构”这个词语下面列出与软件相关的定义。软件架构被描述为“计算机系统内部组件的组合、整合和交互”。当然,大家都会认可这个定义。但是,我们认为它过于通用和抽象。
因此,它并不管用。
我们认为软件专家应该就一个更加细化的解释达成共识,它对那个定义进行细分,并把它们置于相应的上下文中。
1.根据标准定义架构
许多人好像忘记了软件架构有一个标准定义。更确切地说,自从美国国家标准学会/电器和电子工程师协会(ANSI/IEEE)于2000年9月发布了第1471号标准《软件密集型系统架构描述的推荐实践》以来,这个定义就一直存在了。经过这么多年,这份文档也变成国际标准化组织/国际电工委员会(ISO/IEC)的第42010号标准。
软件架构与软件密集型系统的组织有关,从利益相关者的角度解决问题和完成使命。这就是国际标准论文给软件架构这个术语的官方定义。
利益相关者指的是关注系统构建的所有个体。这包括系统的构建者(架构师、开发者、测试者),以及买家、最终用户、分析师、审核员和首席信息官。
关注点指的是利益相关者的兴趣所在,包括系统本身以及他们能在系统上施加的影响,不管是开发方面的、技术方面的、运维方面的、组织方面的、经济方面的,还是法律方面的。
注意:
我们知道很多人在遇到诸如ISO和ANSI等缩略词时都会感到害怕。我们有时倾向于避开这些引用,因为它们让人感到沉闷,让人想到大学时期的大量理论条文。不管怎样,标准论文有着大量我们认可的信息。在本章里,我们引用标准通常是为了说明在某些领域(如处理用户需求)大量工作已经完成得很好,可以重用。
2.软件密集型系统图表
图1-1所示是根据ISO、IEC和IEEE认证的论文描绘的软件架构官方图的核心部分。
系统存在于环境之中,而环境则通过驱动一系列开发和运维的决策来影响系统的设计。虽然当前标准把系统看作相互连接的组件的组合,但是架构也产生了一些后续难以更改的地方。简而言之,通过架构表达软件开发可以归结为做出一些关键的决定,它们将会影响开发生命周期,最终也会影响产出系统的质量。
3.我们自己对软件架构的看法
当新人加入我们公司时,不管他们的技能和角色如何,我们都会向他们介绍我们自己对软件架构的看法。
我们是意大利人,意大利人对咖啡是非常重视的。就意式咖啡而言,并没有很多不同种类的咖啡。唯一认可的分类就是好的意式咖啡和不太好的意式咖啡。但当我们来到星巴克,我们也和其他人一样有着我们自己的口味偏好。
我们想把这个咖啡的类比延伸到软件架构上。
和意式咖啡一样,我们也把世上的架构分为好的架构和不太好的架构。为了确保这种做法有意义,我们只探讨有助于开发能够正常工作的系统的架构。我们不在这里考虑可能导致系统出问题的不良架构,就像我们不喝不好的意式咖啡一样!在喝到不好的意式咖啡时,意大利人不会抱怨,只会直接离开,并给予恰当的(也是尊重的)反馈。
正如我们在星巴克会有我们自己的口味偏好,我们在描述架构时也会有我们自己的看法。我们的看法可以通过一个简单示意图阐述,我们认为它能表达我们是如何思考和工作的。(见图1-2。)
1.1.2 确认需求
系统的使命可以通过一组需求来描述。这些需求最终推动系统架构的形成。
抽象一点地说,需求就是系统的特征,它既可以是功能性的,也可以是非功能性的。功能性需求指的是为了实现特定场景系统必须提供的行为。非功能性需求指的是利益相关者明确提出的系统特性。
功能性和非功能性需求的定义是否标准并广为接受?事实上,用来确定软件系统质量特征的国际标准自1991年起就已经存在了。
1.ISO/IEC 9126标准概览
事实上,没有确认清楚需求是直接导致软件项目失败的常见原因之一,通常也是主要原因。ISO/IEC 9126标准定义了一组软件产品必须满足的质量特征。这个标准给出6个质量特征族群,并进一步细分为21个子特征。其主要族群包括功能、可靠性、可用性、效率、可维护性以及可移植性。表1-1详细解释这些族群以及与之相关的子特征。
子特征分成两类:外部特征和内部特征。外部特征面向用户,是系统的外部视图。内部特征面向系统,是系统的内部视图。外部特征标识功能性需求;内部特征标识非功能性需求。
新的ISO/IEC 25010标准于2011年3月发布,它取代了ISO/IEC 9126。ISO 25010包含8个产品质量特征和31个子特征。
2.功能性需求
功能性需求定义了软件该有的功能。功能通过输入、行为和输出来描述。功能性需求的主要问题在于描述期望行为。不幸的是,这个描述通常没有足够清楚、易懂。
在ISO/IEC 9126文档里,功能性需求被描述为软件提供功能的能力,在特定情况下使用软件时,这些功能可以满足明示和暗示的需求。软件架构对应“什么”;软件设计对应“如何”。但是,我们相信,我们往往忘记了第3个维度——“何时”。
你可能还记得1996年Ariane 5运载火箭的灾难。简单地说,在Ariane 5运载火箭初次飞行的过程中,起飞后40秒就坠毁了。根据官方报告,火箭自行解体的原因是一个未处理异常——数值转换溢出引发的连锁反应。未处理异常不受控制地上抛,向自行解体模块传递了不协调的轨道数字。由于火箭的状态被“正确地”解读为不可挽回地迷失于太空,最终,自我解体只不过是一个正常的决策罢了。讽刺的是,出现未处理异常的模块不应该执行,因为在起飞阶段是没有必要的。工程师应该知道起飞阶段有可能出现不协调的数字,他们没有捕获异常,而是简单地认为没有必要,因为这个模块在起飞阶段不会工作!
因此,需求的“何时”维度是有影响的。
你的互联网应用程序在连接性较差的情况下应该如何反应?它应该出错吗?还是应该等待并恢复?它应该对用户友好吗?它应该使用缓存数据,然后悄悄地切换到离线模式吗?没有公认的答案,应该如何反应取决于可靠性和可恢复性的需求如何。作为一名架构师,你有责任找出答案,有时候需要提出更多的问题,做出更多的思考。有了那些答案就能更好地理解系统,继而减少出错或者漏掉所需特性的机会。
理想情况下,所有功能性需求都是在开发阶段开始之前收集的,一旦进入开发阶段就不会更改。但是,更多的情况是,许多功能性需求只在开发阶段才被发现和完全理解。这就需要重新修订实现了,有时连模块甚至子系统的架构也需要重新修订。做不到这点会导致臭名昭著的“大泥球”综合症,我们会在下一章讨论这个问题。
重要:
在我们职业生涯开始那段时间里,不管与客户面谈时听到的是什么,我们都倾向于将其认为是金科玉律。就像傻乎乎的Dilbert卡通,我们会责怪自己没有很好地处理需求,不管这些需求如何大量、没有条理、充满矛盾或者缺此漏彼(顺便说说,前面提到的Dilbert卡通在这里可以看到:http://dilbert.com/strip/2001-04-14
)。“客户永远是对的”曾经是我们的座右铭,而客户的需求就像法律一样。这些年来,我们发现客户对于任何功能性决定总有最终解释,但是,我们有责任探求出一组明确的选择。我们需要成功确认从面谈得到的未经处理的需求,继而在此基础上制定出一组选择。
3.非功能性需求
非功能性需求是指利益相关者明确提出的系统特性。其常见特性包括可伸缩性、安全性,或者可访问性。功能性需求通常与代码和实现有关,而非功能性需求则通常与系统架构的选择和相关方面有关,这些东西往后难以更改。
举个例子,如何应对极致可伸缩性?
注意:
我们遇到的客户都声称,联系人应用程序的极致扩展性对于公司的生存和投资者的投资组合稳健而言是必不可少的。不管实际说的是什么,做的是什么,我们的陋见是,真正需要极致扩展性的情况是很少的。当然,除非你是在构建下一个Twitter或者Facebook!
扩展性是指随着连接用户的增长,产生了更多的工作和流量,但系统仍能提供相同性能的能力。要让系统更好地扩展,读写必须在最短时间内完成。这可能意味着读取时大量使用缓存,并且使用异步写入。
类似地,把系统设计成关键模块可以部署到独立的机器,能够有效地满足安全性需求。如果你使用ASP.NET MVC而不是ASP.NET Web Forms来实现网站,那就可以很好地满足可访问性需求,因为ASP.NET MVC在渲染HTML方面提供更多的控制。
一般来说,非功能性需求必须和功能性需求同时确认。后者产生软件规范;前者则在实现策略上提供帮助,并在技术决策上给予启发。功能性需求和非功能性需求本身是相关的,并且都会持续发生变化。不过,非功能性需求会迫使我们做出一些艰难决定。
注意:
一些在我们写书时提供过帮助的朋友在这点上有一些补充。虽然他们同意前面这节里的每一句话,但他们想澄清一点:在很多情况下,功能性需求也会影响系统架构的决策和相关方面,并且往后难以更改。完全正确!
4.收集需求
解释如何收集和记录需求的书很多。你可以从中学到,一个好的需求只针对一个东西,表达上没有歧义,可以轻易追溯到业务或者利益相关者的需求,不会作废等。
注意:
如果你想找一本解释软件需求理论的书,我们推荐Stephen Withall的《软件需求模式》(Microsoft Press,2007)。
不过,实际的情况是,业务人员描述的是他们认为自己想要的东西,开发人员构建的是他们认为业务人员想要的东西。不管你在收集需求上投入多少努力,总会出现隐瞒、忽略、忘记,或者被描述的东西只有一些人清楚,而另一些人并不清楚。这种沟通问题的根源是业务人员和开发人员使用不同的词汇。
我们坚信架构师和开发者应该和业务人员使用相同的语言。
不管代金券实际上如何编码才能完整实现,你都必须理解它是什么。更重要的是,你还需理解业务人员如何看待它。作为一名架构师,你不应该期望业务人员理解你的语言,例如数据库表、服务和协议之类的东西。相反,是你应该付出必要的努力去理解构成业务领域的实体含义。
重要:
我们在第5章“发现领域架构”介绍的一个关键概念促使技术人员和业务人员使用相同语言。这个概念就是统一语言,它是领域驱动设计方法学的支柱之一。
5.我们如何处理需求
处理功能性需求的一个简单、有效的办法是根据类别对它们进行分组,就像表1-1所示的那样。我们通常创建一个Microsoft Office Excel文档,一个标签对应一个类别。接着,我们把从面谈中收集到的需求放入对应的类别。
完成之后,我们回顾一遍,碰到空的或者只有一两个需求的标签就停下来。比如说,如果我们在可移植性里没找到需求,我们就应该停下来问一下,我们是否了解得足够多,我们是否提出了足够多的问题。没有明确的可移植性需求可能意味着可移植性并非一个需求,也可能意味着我们对它并不了解。
特别地,可移植性与软件可在不同的环境下使用的能力有关。如果服务后端没有这种需求,那么马上就会引出一个问题,这个后端是否应该只对Web客户端可用,或者是否也应该支持iOS和Android客户端?
1.1.3 什么是架构,什么不是
当你考虑创建或定义软件系统的架构时,你首先试着标识出一组可能的交互组件,它们共同完成被赋予的任务。国际标准并没提到任何可以把系统细分成多个部分的方法。假设在第一步里你得到一个概念性的架构以及一些不同角度的视图。在第二步里,你需要逐步逼近功能和物理架构。如何做到是很主观的,即使自顶向下的做法看起来很合理。你把组件划分得越来越细,然后从这里开始构建。
分解过程的具体实现取决于项目选用的方法学,你越是敏捷,分解过程越是迭代,表达越是清楚,步骤也越细致和频繁。分解过程的输出是一组将会交给开发团队的规范。此外,规范的内容和格式取决于选用的方法学。你越是敏捷,你留给开发者实现这个架构的*性和独立性就越多。
1.定义架构和实现之间的边界
你在分解系统时标识出来的构成组件代表了将以某种方式实现的功能。组件的设计,它们的接口、它们的职责,以及它们的行为毫无疑问都是架构的组成部分。但是,架构和实现之间是有一道物理边界把它们分隔开来的。
把这个边界标识出来很重要,因为在很大程度上它帮助我们定义开发团队里面的角色。尤其是它界定了架构师和开发者之间的边界。这些年来,我们了解到架构师与开发者之间的区别并没有苹果与橙子的那么大。如果把他们看作同类水果,那么,假设他们都是苹果,他们就是红苹果与青苹果,换句话说,只是不同口味,而不是不同类型的水果。并且,不存在哪种口味更好的说法。
架构和实现之间的边界会在你触及黑盒行为时显现出来。黑盒行为只是一块功能,可以轻易替换和重构而不会带来显著回归,并且对架构的其他部分造成很少影响,甚至没有影响。在黑盒行为之上的东西则可能与架构相关,并且可能需要你做出艰难决定。
那么,我们如何定义好的架构?答案是:架构里的所有艰难决策最后都被证实是对的。
2.艰难决定的科学
软件系统有些方面和特性,一旦进入开发阶段就难以更改(只是难,而不是不可能)。其他方面和特性则可以在任何时候毫不费力地更改,也不会对系统产生显著影响。
Martin Fowler在他的《企业应用架构模式》(Addison-Wesley,2002)里提到:
如果你发现有些东西比你想象的更易实现,那么它不再属于架构范畴了。最终,架构归结为重要的东西,不管这些东西是什么。
简而言之,我们认为架构这个词包含了所有在项目早期必须认真对待的东西。最终,架构就是找出需要正确处理的关键决策,这些决策在项目里应该尽早处理,但你却想尽可能押后处理。
3.艰难决定无处不在
当我们提及艰难的架构决定时,我们并不一定是指往后改起来很难或很昂贵的设计决定。难以更改的决定无处不在,从概念层的定义到构造函数的签名都有。
为了说明这点,我们来看几个架构问题的例子,如果它们在项目期间出现,你可能会陷入预算限制和期限问题。
第 1 个例子是改变业务逻辑的组织方式。业务逻辑的设计有几种方案:事务脚本(Transaction Script)、表模块(Table Module)、领域模型(Domain Model)、对命令和查询进行分离的领域模型,以及事件溯源(Event Sourcing)等。一旦你做出选择,比如说,表模块(这意味着你会把逻辑放入基于数据库的表构建的仓储组件,就像多年以来你对DataSet和DataTable做的那样),把它换成领域模型并非一个下午就能完成的事情。这种改变会对数据层以及应用程序(服务)层造成很大影响,对表现层也可能是这样。
第2个例子是换一个不同的库做相同的任务。假设你使用某个库开发了某个功能。某天,客户跟你说公司出了一个新的政策,IT部门不得从某个供应商采购产品。现在你要处理一个新的意料之外的非功能性需求,但代价是什么?在最好的情况下,你可以从授权供应商获取一个类似的工具,或者你可以自己构建一个类似的工具。此外,你也可以考虑渐进地改变架构,使得这个库变得不再必要。
注意:
如果你参与过Dino过去两年举办的研讨会,你可能已经听过这段经历。简单地说,Dino的公司投标了一个项目,为iOS、Android、Windows Phone和BlackBerry 4个平台构建一个时效性很强的移动应用。这个投标假设了PhoneGap是一个可行方案。不幸的是,使用PhoneGap构建的原型被客户拒绝了,团队之前的努力付诸东流了,而剩下的时间不足4周。
团队面临两个令人不爽的方案:在很短的时间内把事情做4遍,或者发明一些新的东西。团队设法在一个移动网站上重用PhoneGap的某些标记,然后在4个基础的、基本上静态的、只有图形的原生应用的窗体里整合Web视图。在功能方面,表现出色,甚至还有时间应对来自Apple的一次不合理的拒绝。然而,在压力方面,大家都知道后面会很痛苦。
第3个艰难决定的例子是改变类的成员的修饰符,这可能不常见。一般而言,当你使用sealed和virtual修饰符时,你承担的职责并不少。在C#里,每个类默认都不是密封的,类的每个方法都不是虚方法。在Java里,方法的情况有所不同,默认都是虚方法。从设计的角度来看,密封类更好。事实上,如果一开始你就知道一个类是密封的,你也据此创建你的代码,后来发现这个类支持继承更合理,你可以在不产生破坏性变化以及不损害兼容性的情况下把它改成非密封的。虚方法和类以及类的成员的可见性几乎也是这样。反过来的情况就不会那么顺利了。你通常不能在不破坏现有代码的情况下密封一个类或者把一个虚方法标记成非虚的。
4.环境使决策变难
大约10年前,在一个研讨会上,我们听到一个主讲者提到那段时间的一个大实话。他声称移动和云计算对CTO来说是最大的烦事,他们试图了解可以用两者来做什么。
这点记忆引出一种比较痛苦的架构艰难决定。当你知道某个技术或者某个模式可能有用却没有任何具体的证明时,你就会面临这种决定。你到处寻找案例分析,非常渴望找到它们,或者任何有助于确定采纳或排除某些东西的文献。
就众所周知的最佳实践做出决定很容易,因为有大量案例分析。然而,就新的技术(如NoSQL)做出决定相对来说比较困难,因为真实应用程序的案例分析少之又少,有时候甚至带有偏见。
这就是经典的青少年性思维:那个年龄的每个人都在谈论,但没人真正知道如何做。更过分的是,那个年龄的每个人都认为别人在做,所以每个人都认为自己也应该做。与真正的青少年之间真正的性一样,唯一的解决之道就是:放手去试!
1.1.4 架构流程
需求经由首席架构师处理之后会交由开发团队实现。每个人都同意好的架构和好的设计可以加快模块的实际开发。但构建应用程序实际上应该怎么做?
原则上,构建软件是3个群体共同努力的结果:项目管理、开发以及测试/质量控制(QA)。任务的划分理论上堪称完美。项目管理团队负责产品规范、质量标准的定义,以及日程安排。开发团队确保使用理想的算法以及根据最合适的模式写代码。最后,QA团队对应用程序的行为进行压力测试,目的是找出它的缺陷,以便通过更新使之变得更加强大。
3个团队在这个过程里必须协同工作,虽然某些项目的管理工作需要在开发工作开始之前完成,并且测试也要在某些代码构建之后才能开始。软件开发的整个理念是为团队定义协作规则以及定义他们应该如何互动。软件开发方法学正是为了解决这个问题。
方法学基本上有两个主要类型:瀑布和敏捷。使用瀑布模型的项目倾向于按顺序经历一系列阶段,以软件的发布告终。使用敏捷模型的项目倾向于来回迭代任务多次,直到应用程序准备妥当为止,有时候是因为最后期限快到了。选择方法学在某种程度上决定了架构流程的类型。
1.前期架构
前期架构(upfront architecture)要求一切都在开始之前安排妥当。大量设计工作会在开始写代码之前完成。编码通常被看作把定义明确的想法实际翻译成编译指令。
瀑布模型可以追溯到20世纪70年代。在这种模型里,软件开发会按顺序从一个阶段走到下一个阶段。基本上,仅当第N步100%完成并且所有人都满意才会进入第N+1步。图1-3给出了瀑布模型的示例。
在现实世界里,阶段之间存在某种重合是可以接受的,不过通常是指制订计划、做预算以及安排工作日程同时进行。这个方案对于客户来说很棒,因为它提供了一些明显的确定性。然而,现实有所不同。项目最终超出预算和最后期限,有时候甚至不能按照预期的需求交付,这些都不是罕见的。这种糟糕的结果在很大程度上是因为要在前期花费大量精力进行设计,这与现代软件的实际运作并不相容。
软件设计的根本问题是需求变化很快,当开发结束时,一些需求可能已经不同了。没有客户愿意为他们明知不能满足所有需求的软件付款。此外,客户根本不喜欢改变和新增的东西带来的可变成本。
有鉴于此,瀑布模型已是明日黄花,你可以将它的死亡归咎于软件开发是一种工程学。
2.渐现架构
今天对架构流程的看法是,任何团队都应该尽快开始开发,然后获取早期反馈,在真实代码上改进软件。这意味着快速前进,接受甚至拥抱变化,尽早交付一些有价值的东西,以及欢迎反馈。
渐现架构(emerging architecture)是增量构建软件的流程。初始启动之后,项目会经过一系列迭代,包括设计、编码和测试。每次迭代产生这个系统的一个可交付但不完整的版本。每次迭代,团队会着手处理设计更改,添加新的功能,直到符合整个规范为止。图1-4所示给出了迭代流程的示意图。
迭代开发形成敏捷方法学的基础。“敏捷”这个术语是特意挑选来表明与瀑布模型等重量级方法相反的立场。
在敏捷项目启动时,可能只有一些需求完全定义出来,但你知道在项目结束之前会有更多需求呈现出来或者要被澄清。凭借敏捷思维,这不是一个问题。开发过程会在迭代中变得清晰。在迭代开始时,你会和客户商谈现有需求应该实现的部分。在迭代过程里,你每次只会关注和实现单个需求。在迭代结束时,你交付一份可工作的软件。它可能是不完整的,但它可以工作。接着,你进入另一个迭代,关注另一组需求。如果在此期间某些东西发生改变或者被证明是错的,就会进行重构。这个过程会持续到没有更多东西要添加为止。迭代的长度以周为单位——通常是两周。总之,敏捷流程非常敏捷,足以应对变化。而变化在业务里是常态,不是例外。
敏捷方法学是一个概括性术语。当你提及敏捷方法学时,你并没有精确指明你实际想说的是哪种方法学。
软件开发最流行的敏捷方法学是极限编程(XP)。在XP里,各个阶段的执行都是极短的迭代,两周就会结束。编码和设计同时进行。
Scrum是另一个流行的敏捷方法学,但它针对的更多是管理项目而不仅仅是开发代码。Scrum并未指定任何软件开发模型,但它能与作为开发代码方法的XP很好地协同工作。若想更多了解Scrum,可以看一下Ken Schwaber的《Agile Project Management with Scrum》(Microsoft Press,2004)。
重要:
敏捷架构有时候会展现出矛盾的一面,就像在说,如果你使用敏捷,就不用做任何架构分析,你直接开始编码,关注功能,并且完全忽略建模。老实说,我们的观点是,虽然敏捷不排除任何这样的行为,但这种看法通常没有事实根据。根据我们的经验,敏捷的做法通常刚好相反,架构和建模问题的处理会贯穿整个开发的生命周期。
3.带有前期分析的渐现架构
值得一提的是还有第3种方案,介于瀑布和敏捷之间。这个过程包含大量初步的前期分析,然后开始采用经典的敏捷方法学。有些人把它称作非常有纪律的敏捷方案;也有些人把它称作Sprint Zero。
然而,更一般地说,我们发现所有软件开发方法学都有一些共同的特征:若干需要经历的阶段、若干将会产生软件的迭代,以及单个迭代通常的持续时间。所有阶段按顺序执行,至少有一个迭代以软件交付告终。不同方法学之间的区别是每个阶段进入的顺序、需要的迭代数量以及单个迭代的持续时间。接受这个假设之后,采用敏捷方法的步骤会比你原先所想的少很多。