软件架构之开发方法
- 第6章:开发方法
- 6.1 软件生命周期
- 6.2 软件开发模型
- 6.2.1 瀑布模型
- 6.2.2 演化模型
- 6.2.3 螺旋模型
- 6.2.4 增量模型
- 6.2.5 构件组装模型
- 6.3 统一过程
- 6.4 敏捷方法
- 6.4.1 极限编程
- 6.4.2 特征驱动开发
- 6.4.3 Scrum
- 6.4.4 水晶方法
- 6.4.5 其他敏捷方法
- 6.5 软件重用
- 6.5.1 软件重用
- 6.5.2 构件技术
- 6.6 基于架构的软件设计
- 6.6.1 ABSD 方法与生命周期
- 6.6.2 基于架构的软件开发模型
- 6.7 形式化方法
第6章:开发方法
软件开发方法是软件开发的方法学。自从“软件危机”爆发以来,软件研究人员就在对开发方法进行不断地研究,以期能够提高软件的质量、降低软件的成本。经过 40 多年的研究,人们提出了很多开发方法,如最初的结构化开发到现在非常流行的面向对象的开发方法等。本章将介绍软件生命周期、软件开发模型、软件重用技术、逆向工程及形式化开发方法。
6.1 软件生命周期
软件生命周期也就是软件生存的周期。同万物一样,软件也有诞生和消亡,软件生命周期就是指软件自开始构思与研发到不再使用而消亡的过程。有关软件生命周期的阶段划分,不同的标准有不同的规定。在 GB8566-88(《软件工程国家标准——计算机软件开发规范》)中将软件生命周期划分为 8 个阶段:可行性研究与计划、需求分析、概要设计、详细设计、集成测试、确认测试,使用和维护。
(1)可行性研究与计划:在决定是否开发软件之前,首先需要进行可行性研究,来确定开发软件的必要性,并根据可行性研究的结果初步确定软件的范围,风险、开发成本等内容。从而指定出初步的软件开发计划。通过可行性研究,如指定该软件具有研发的必要,则将产生《可行性研究报告》和《软件开发计划》,并进入分析的阶段。
(2)需求分析:需求分析是软件开发的重要阶段。经过可行性研究后,初步确定了软件开发的目标和范围,之后则需要对软件的需要进行细致的分析,来确定软件要做成什么样的。需求分析是软件开发过程中极其重要的一环,如果需求分析出现了重大偏差,那么软件开发必然会偏离正确的道路,越走越远。尤其是需求分析的错误如果在软件开发后期才被发现,修正的代价是非常大的。
(3)设计概要:概要设计确定整个软件的技术蓝图,负责将需求分析的结果转化为技术层面的设计方案。在概要设计中,需要确定系统架构、各子系统间的关系、接口规约、数据库模型、编码规范等内容。概要设计的结果将作为程序员的工作指南,供程序员了解系统的内部原理,并在其基础上进行详细设计和编码工作。
(4)详细设计:详细设计完成编码前最后的设计,详细设计在概要设计的基础上,进行细化,如类设计。详细设计不是开发过程中必需的阶段,在一些规模较小、结构简单的系统中,详细设计往往被省略。同样,在某一次软件开发中,可能只会对部分关键模块进行详细设计。
(5)实现:实现过程包括编码和单元测试。单元测试指的是对刚刚编写出的一个小的程序单元进行测试,如某一个过程、方法或函数。因为单元测试的对象是小的程序单元,而不是完整的程序,因此往往需要编写一些测试程序来进行测试。有效的单元测试可以大大提高编码的质量,降低软件系统的缺陷率。
(6)集成测试:集成测试又称为组装测试。通过单元测试的程序并不意味着没有缺陷,当程序单元被集成到一起进行交互的时候,往往会出现单元测试中不能发现的问题。同单元测试不同,集成测试必须经过精心的组织,指定集成测试计划,确定如何将这些程序单元集成到一起,按照什么样的顺序进行测试,使用哪些测试数据等问题。
(7)确认测试:当完成集成测试后,软件之间的接口方面的错误已经排除,这时需要验证软件是否同需求一致,是否达到了预期目标。同集成测试一样,确认测试也需要进行计划和组织,逐步地验证软件系统同需要的一致性。经过确认测试的软件将投入正常使用,并进入维护期。
(8)使用和维护:即使通过了单元测试、集成测试和确认测试,也不可能发现软件系统中的全部缺陷;软件系统的需求也会根据业务的发展变化而变化。因此,在软件使用过程中,必须不断地对软件进行维护,修正软件中的缺陷,修改软件中已经不能适应最新情况的功能或者增加新的功能。软件维护的过程会贯穿整个软件的使用过程。当使用和维护阶段结束后,软件系统也就自然消亡,软件系统的生命周期结束。
6.2 软件开发模型
在计算机刚刚诞生的年代,计算机是一种只有天才才能掌握的工具。人们对软件的认知仅仅停留在程序的层面上,所谓的软件开发就是那些能够掌握计算机的天才们写的一些只有计算机才能理解的二进制序列。但随着技术的发展,软件的复杂度不断提高,人们进入了大规模软件开发的时代。这时,人们发现,软件系统已经变得非常复杂,需要遵循一定的开发方法才能取得成功,于是称这些模式化的开发方法为开发模型。
6.2.1 瀑布模型
顾名思义,瀑布模型就如同瀑布一样,从一个特定的阶段流向下一个阶段,如图 6-1所示。
1.瀑布模型的核心思想
瀑布模型认为,软件开发是一个阶段化的精确的过程。就像要制造一艘航空母舰,首先需要知道航空母舰的参数(长、宽、高、排水量、航速等)。在这些参数的技术上需要对航空母舰进行设计,设计包括总体设计和详细设计。只有设计得一清二楚的图纸才能交付施工,否则造出的零件肯定拼装不到一起。制造完毕后,要把这些零件一个一个地拼装起来,拼装成发动机、船舱等部分,并检查这些部分是否符合设计标准,这就是集成测试。最后,把各个部分组合在一起,造出一艘巨大的航母。这个过程正如图 5-1 中的描述,软件要经过需求分析、总体设计、详细设计、编码、调试、集成测试和系统测试阶段才能够被准确地实现。在图 6-1 中,每一阶段都有回到前一阶段的反馈线,这指的是,在软件开发中当在后续阶
段发现缺陷的时候,可以把这个缺陷反馈到上一阶段进行修正。
从图 6-1 中可以看出瀑布模型的一个重要特点:软件开发的阶段划分是明确的,一个阶段到下一个阶段有明显的界线。在每个阶段结束后,都会有固定的文档或源程序流入下一阶段。在需求分析阶段结束后,需要有明确的描述软件需求的文档;总体设计结束后,需要有描述软件总体结构的文档;详细设计结束后,需要有可以用来编码的详细设计文档;而编码结束后,代码本身被作为文档流到下一个阶段。因此也称瀑布模型是面向文档的软件开发模型。
当软件需求明确、稳定时,可以采用瀑布模型按部就班地开发软件,当软件需求不明确或变动剧烈时,瀑布模型中往往要到测试阶段才会暴露出需求的缺陷,造成后期修改代价太大,难以控制开发的风险。
2.瀑布 V 模型
瀑布 V 模型是瀑布模型的一种变体。随着对瀑布模型的应用,人们发现,缺陷是无法避免的,任何一个阶段都会在软件中引入缺陷,而最后的测试也不能保证软件完全没有缺陷,只能争取在交付前发现更多的缺陷。测试成为软件开发中非常重要的环节,测试的质量直接影响到软件的质量。因此,人们对瀑布模型进行了小小的更改,提出了更强调测试的瀑布 V模型,如图 6-2 所示。
整个瀑布模型在编码与调试阶段转了个弯,形成了一个对称的 V 字。瀑布 V 模型同标准瀑布模型一样,在进行完需求分析后就将进入总体设计阶段,但是除总体设计外,需求分析还有一条虚线指向系统测试。这指的是,需求分析的结果将作为系统测试的准则,即需求分析阶段也将产生同软件需求一致的系统测试;同时软件产品是否符合最初的需求将在系统测试阶段得到验证。以此类推,总体设计对应了集成测试,详细设计对应了单元测试。瀑布 V 模型不但保持了瀑布模型的阶段式文档驱动的特点,而且更强调了软件产品的验证工作。
3.瀑布模型的缺点
虽然是经典的开发模型,但瀑布模型中仍存在一些难以克服的缺陷,即使是在改进的瀑布 V 模型中还是会存在。
首先,在瀑布模型中,需求分析阶段是一切活动的基础,设计、实现和验证活动都是从需求分析阶段的结果导出的。一旦需求分析的结果不完全正确,存在偏差,那么后续的活动只能放大这个偏差,在错误的道路上越走越远。事实上,由于用户和开发者的立场、经验、知识域都不相同,不同的人对同一件事物的表述也不同,这就造成需求分析的结果不可能精确、完整地描述整个软件系统。所以瀑布模型后期的维护工作相当繁重,而这些维护工作大多都是修正在需求分析阶段引入的缺陷。这个问题是瀑布模型难以克服的。
其次,瀑布模型难以适应变化。在瀑布模型中精确地定义了每一个阶段的活动和活动结果,而每一阶段都紧密依赖于上一阶段的结果。如果在软件的后期出现了需求的变化,整个系统又要从头开始。
再次,使用瀑布模型意味着当所有阶段都结束才能最终交付软件产品,所以在提出需求后需要相当长一段时间的等待才能够看到最终结果,才能发现软件产品究竟能不能够满足客户的需求。
最后,文档驱动型的瀑布模型除了制造出软件产品外还将产生一大堆的文档,大部分的文档对客户没有任何意义,但完成这些对客户没有意义的文档却需要花费大量的人力。所以瀑布模型也是一种重载过程。
6.2.2 演化模型
瀑布模型看起来很好,随着一个又一个阶段的流过,软件系统就被建立起来了。可是在应用软件开发的过程中,人们发现很难一次性完全理解用户的需求、设计出完美的架构,开发出可用的系统,这是由于人的认知本身就是一个过程,这个过程是渐进的、不断深化的。对于复杂问题,“做两次”肯定能够做得更好。那么,对于软件开发这个复杂而且与人的认知过程紧密相关的事也应该是一个渐进的过程。
演化模型正是基于这个观点提出的。一般情况下,一个演化模型可以看做若干次瀑布模型的迭代,当完成一个瀑布模型后,重新进入下一个迭代周期,软件在这样的迭代过程中得以演化、完善。根据不同的迭代特点,演化模型可以演变为螺旋模型、增量模型和原型法开发。
6.2.3 螺旋模型
螺旋模型将瀑布模型和演化模型结合起来,不仅体现了两个模型的优点,而且还强调了其他模型均忽略了的风险分析。螺旋模型的每一周期都包括需求定义、风险分析、工程实现和评审 4 个阶段,由这 4 个阶段进行迭代,软件开发过程每迭代一次,软件开发就前进一个层次。采用螺旋模型的软件过程如图 6-3 所示。
螺旋模型的基本做法是在“瀑布模型”的每一个开发阶段前,引入一个非常严格的风险识别、风险分析和风险控制。它把软件项目分解成一个个小项目,每个小项目都标识一个或多个主要风险,直到所有的主要风险因素都被确定。
螺旋模型强调风险分析,使得开发人员和用户对每个演化层出现的风险都有所了解,继而做出应有的反应。因此,螺旋模型特别适用于庞大而复杂、具有高风险的系统,对于这些系统,风险是软件开发潜在的、不可忽视的不利因素,它可能在不同程度上损害软件开发过程,影响软件产品的质量。减小软件风险的目标是在造成危害之前,及时对风险进行识别、分析,决定采取何种对策,进而消除或减少风险的损害。
与瀑布模型相比,螺旋模型支持用户需求的动态变化,为用户参与软件开发的所有关键决策提供了方便,有助于提高目标软件的适应能力,为项目管理人员及时调整管理决策提供了便利,从而降低了软件开发风险。
但是,不能说螺旋模型绝对比其他模型优越,事实上,螺旋模型也有其自身的缺点:
(1)采用螺旋模型,需要具有相当丰富的风险评估经验和专业知识。在风险较大的项目开发中,如果未能及时标识风险,势必会造成重大损失。
(2)过多的迭代次数会增加开发成本,延迟提交时间。
6.2.4 增量模型
演化模型的另一种形式是增量模型。在系统的技术架构成熟、风险较低的时候,可以采用增量的方式进行系统开发,这样可以提前进行集成测试和系统测试,缩短初始版本的发布周期,提高用户对系统的可见度。
对于增量模型,通常有两种策略。一是增量发布的办法。即首先做好系统的分析和设计工作,然后将系统划分为若干不同的版本,每一个版本都是一个完整的系统,后一版本以前一版本为基础进行开发,扩充前一版本的功能。在这种策略中,第一版本往往是系统的核心功能,可以满足用户最基本的需求,随着增量的发布,系统的功能逐步地丰富、完善起来。用户在很短的时间内就可以得到系统的初始版本并进行试用。试用中的问题可以很快地反馈到后续开发中,从而降低了系统的风险。在应用增量模型中需要注意:
(1)每一个版本都是一个完整的版本。虽然最初的几个增量不能完全地实现用户需求,但这些版本都是完整的、可用的。
(2)版本间的增量要均匀,这一点是很重要的。如果第一个版本花费一个月的时间,而第二个版本需要花费 6 个月的时间,这种不均匀的分配会降低增量发布的意义,需要重新调整。
另一种策略是原型法。同增量发布不同,原型法的每一次迭代都经过一个完整的生命周期。当用户需求很不明确或技术架构中存在很多不可知因素的时候,可以采用原型法。在初始的原型中,针对一般性的用户需求进行快速实现,并不考虑算法的合理性或系统的稳定性。这个原型的主要目的是获得精确的用户需求,或验证架构的可用性。一般情况下,会在后面的开发中抛弃这个原型,重新实现完整的系统。
6.2.5 构件组装模型
随着软构件技术的发展,人们开始尝试利用软构件进行搭积木式的开发,即构件组装模型。在构建组装模型中,当经过需求分析定义出软件功能后,将对构件的组装结构进行设计,将系统划分成一组构件的集合,明确构件之间的关系。在确定了系统构件后,则将独立完成每一个构件,这时既可以开发软件构件,也可以重用已有的构件,当然也可以购买或选用第三方的构件。构件是独立的、自包容的,因此架构的开发也是独立的,构件之间通过接口相互协作。
构件组装模型的一般开发过程如图 6-4 所示。
构件组装模型的优点如下:
(1)构件的自包容性让系统的扩展变得更加容易
(2)设计良好的构件更容易被重用,降低软件开发成本
(3)构件的粒度较整个系统更小,因此安排开发任务更加灵活,可以将开发团队分成若干组,并行地独立开发构件。
鱼与熊掌不可兼得,构件组装模型也有明显的缺点:
(1)对构件的设计需要经验丰富的架构设计师,设计不良的构件难以实现构件的优点,降低构件组装模型的重用度。
(2)在考虑软件的重用度时,往往会对其他方面做出让步,如性能等。
(3)使用构件组装应用程序时,要求程序员熟练地掌握构件,增加了研发人员的学习成本。
(4)第三方构件库的质量会最终影响到软件的质量,而第三方构件库的质量往往是开发团队难以控制的。
6.3 统一过程
统一过程(Unified Process,UP)是由 Rational 公司开发的一种迭代的软件过程,是一个优秀的软件开发模型,它提供了完整的开发过程解决方案,可以有效地降低软件开发过程的风险,经过裁剪的 UP 可以适应各种规模的团队和系统。
1.UP 的二维模型
UP 是一个很有特色的模型,它本身是一个二维的结构,如图 6-5 所示。对于 UP 而言,时间主线就是横轴的阶段,随着时间的流逝,软件开发活动总要经过初始、细化、构建和交付这 4 个阶段方能完成。而纵轴的工作流程则描述了在不同的阶段需要进行的主要工作。例如在初始阶段,软件组织需要进行大量的调研,对软件进行业务建模、需求,同时进行一些设计以验证建模的合理性,还要进行一些实施甚至测试和部署的工作,用以验证需求和设计的工作及开发系统原型,当然配置与变更管理、项目管理和环境是在任何阶段都是不能缺少的。
从这个模型中可以看出 UP 迭代的特点。任何一个阶段的工作都不是绝对的,都是相互交叠配合的。但每一个阶段都有其侧重点:
在初始阶段,开发者刚刚接入系统,此时最重要的工作是界定系统范围,明确系统目的。在这一阶段,业务建模和需求工作成了重头戏。
在细化阶段,开发者需要抽象出软件的逻辑模型,设计出软件的架构,在这一阶段,分析设计工作是最主要的工程活动。
在构建阶段,开发者需要基本完成系统的构建,使之成为一个完整的实体,并进行测试和部署,在这一阶段,实施和测试是最主要的活动。
当进入交付阶段(该阶段也经常被称为转移阶段),软件系统需求已经完全成熟或产品化,或进入下一个版本。在这一阶段不可避免地要对软件系统进行重构、修改、测试和部署。
在这 4 个阶段中,各有侧重点,但也不是像瀑布模型那样完全不允许其他活动的存在。在初始阶段,为了验证开发者的想法,就需要进行一部分的实施和测试;而即使到了交付阶段,需要也可能会发生变化,仍然需要进行部分业务建模、需求和设计的活动。
在每个阶段中,系统推进不是一蹴而就的。在图中将细化阶段划分为第 1 次细化和第 2 次细化,将构建阶段也划分为 3 个小阶段。在实际开发中,可以根据实际的需要划分为更多的小阶段来完成。
对于纵轴而言,业务建模、需求、分析设计、实施、测试、部署、配置与变更管理、项目管理、环境称为 UP 的 9 个核心工作流。可以把这 9 个工作流进行简单的分类以帮助理解,业务建模、需求、分析设计、实施、测试和部署是工程活动,而配置与变更管理、项
目管理和环境是管理活动。
在这 9 个工作流中,前 8 个可以说是绝大多数人都耳熟能详的东西,而“环境”工作流则相对难以理解。“环境”工作流很重要,也可以称之为“环境管理”。俗语说,“巧妇难为无米之炊”,“环境”工作流就是为软件开发准备“米”的活动。在软件开发中,需要为各种工作准备相应的工作环境,在工作环境中需要包含必需的工具、活动的指南、活动的流程规范、工作产品的模板、基本的开发设施等。在很多组织中,“环境”工作流没有得到应有的重视,或者完全被忽视,以为为开发者提供了工作台和计算机就万事大吉了,其实这种做法是错误的。
每一个开发团体都有自己特定的活动准则和规范,这些准则和规范是团体协作的基础,万万少不得。没有合理的工具配备,没有充分的指南、规范和模板,软件开发的活动肯定是放羊式的管理,管理者除了一些“羊毛”外什么也收获不到。观察 UP 模型就可以发现,在每一阶段的最开始,“环境”工作流都有一个小小的波峰。在这里面,开发团队需要为开发环境进行相应的准备并在后续活动中为开发环境提供支持。
2.UP 的生命周期
前面已经提到,UP 模型的时间主线是阶段,UP 的生命周期也是与阶段一一对应的。在 UP 的生命周期中共有 4 个里程碑:
(1)目标里程碑。目标里程碑对应着先启阶段的结束,当开发者可以明确软件系统的目标和范围时即达到了该里程碑。
(2)架构里程碑。架构里程碑是 UP 生命周期中的第二个里程碑,在这个里程碑前,开发者需要确定稳定的系统架构。
(3)能力里程碑。当系统已经足够的稳定和成熟并完成 Alpha 测试后,认为达到了第3 个里程碑。
(4)发布里程碑。在达到发布里程碑前,需要完成系统的测试、完成系统发布和用户培训等工作。
在经过这 4 个里程碑后,即为一个完整的生命周期,开发出一个新的版本。此时可以关闭该产品的开发,也可以迭代进入下一版本。
3.UP 的特点
UP 是一个特点鲜明的开发模型,下面列出 UP 的一些特点:
(1)UP 是一个迭代的二维开发模型,在生命周期的每一阶段都可以进行需求、设计等活动。UP 不但给出了迭代的生命周期,还给出了生命周期每一阶段的迭代指南。
(2)采用不同迭代方式的 UP 可以演变为演化模型或增量模型。
(3)UP 的迭代特点使得更容易控制软件开发的风险。
(4)虽然 UP 是一个迭代的开发模型,但 UP 本身并不属于敏捷方法。相反,一般认为,未经裁减的 UP 是一个重载过程。
(5)在实际应用中可以根据具体问题对 UP 进行裁减,从而使其可以适应各种规模的软件和开发团队。
4.架构设计师在 UP 中的活动
架构设计师在 UP 活动中承担着非常重要的角色。在 UP 中,架构设计师除了需要建立系统架构模型外,还需要:
(1)同需求人员和项目管理人员密切协作。
(2)细化软件架构。
(3)保持整个架构的概念完整性。
具体地说,架构设计师不但需要设计系统架构,还需要定义设计方法、设计指南、编码指南、评审设计等工作。因此,有人也称 UP 是一个以架构为中心的开发模型。
6.4 敏捷方法
2001 年 2 月,在美国的犹他州,17 位“无政府主义者”共同发表了《敏捷软件开发宣言》,在宣言中指出:
尽早地、持续地向客户交付有价值的软件对开发人员来说是最重要的。
拥抱变化,即使在开发的后期。敏捷过程能够驾驭变化,保持客户的竞争力。
经常交付可工作的软件,从几周到几个月,时间范围越小越好。
在整个项目中,业务人员和开发者紧密合作。
围绕士气高昂的团队进行开发,为团队成员提供适宜的环境,满足他们的需要,并给予足够的信任。
在团队中,最有效率的、也是效果最好的沟通方式是面对面地交流。
可以工作的软件是进度首要的度量方式。
可持续地开发。投资人、开发团队和用户应该保持固定的节奏。
不断追求优秀的技术和良好的设计有助于提高敏捷性。
要简单,尽可能减少工作量。减少工作量的艺术是非常重要的。
最好的架构、需求和设计都来自于一个自我组织的团队。
团队要定期地总结如何能够更有效率,然后相应地自我调整。
至此,敏捷软件联盟建立起来,敏捷软件开发方法进入了大发展的时代。这份宣言也就是敏捷方法的灯塔,所有的敏捷方法都在向这个方向努力。目前已形成多种敏捷方法,其中XP 传播最为广泛。
6.4.1 极限编程
XP 方法可以说是敏捷联盟中最鲜艳的一面旗帜,也是相对来说最成熟的一种。XP 方法的雏形最初形成于 1996—1999 年间,Kent Beck、Ward Cunningham、Ron Jeffery 夫妇在开发 C3 项目(Chrysler Comprehensive Compensation)的实践中总结出了 XP 的基本元素。在此之后,Kent Beck 和他的一些好朋友们一起在实践中完善提高,终于形成了极限编程方法。
XP 是一种轻量(敏捷)、高效、低风险、柔性、可预测、科学而且充满乐趣的软件开发方式。与其他方法论相比,其最大的不同在于:
(1)在更短的周期内,更早地提供具体、持续的反馈信息。
(2)迭代地进行计划编制,首先在最开始迅速生成一个总体计划,然后在整个项目开发过程中不断地发展它。
(3)依赖于自动测试程序来监控开发进度,并及早地捕获缺陷。
(4)依赖于口头交流、测试和源程序进行沟通。
(5)倡导持续的、演化式的设计。
(6)依赖于开发团队内部的紧密协作。
(7)尽可能达到程序员短期利益和项目长期利益的平衡。
XP 由价值观、原则、实践和行为四个部分组成,它们彼此相互依赖、关联,并通过行为贯穿于整个生命周期。
1.四大价值观
XP 的核心是其总结的沟通、简单、反馈、勇气四大价值观,它们是 XP 的基础,也是XP 的灵魂。
(1)沟通。通常,程序员给人留下的印象就是“内向、不善言谈”,项目中的许多问题就出在这些缺乏沟通的开发人员身上。由于某个程序员做出了一个设计决定,但是却不能够及时地通知团队中的其他成员,结果使得团队在协作与配合上出现很多麻烦。而在传统的开发方法中,并不在意这种口头沟通不畅的问题,而是希望借助于完善的流程和面面俱到的文档、报表、计划来替代,但是,这又引入了效率不高的新问题。
XP 方法认为,如果小组成员之间无法做到持续的、无间断的交流,那么协作就无从谈起。从这个角度来看,通过文档、报表等人工制品进行交流,具有很大的局限性。因此, XP 组合了诸如结对编程这样的最佳实践,鼓励大家进行口头交流、通过交流解决问题,提高效率。
(2)简单。XP 方法在工作中秉承“够用即好”的思路,也就是尽量地简单化,只要今天够用就行,不考虑明天会出现的新问题。这一点看上去十分容易,但要真正做到保持简单的工作其实是很难的,因为在传统的开发方法中,都要求开发人员对未来做一些预先规划,以便对今后可能发生的变化预留一些扩展空间。
沟通和简单之间还有一种相当微妙的互相支持关系。一方面,团队成员之间沟通得越多,就越容易明白哪些工作需要做,哪些工作不需要做;另一方面,系统越简单,需要沟通的内容也就越少,沟通也将更加全面。
(3)反馈。是什么原因使得客户、管理层这么不理解开发团队?究其症结,就是开发的过程中缺乏必要的反馈。在很多项目中,当开发团队经历过了需求分析阶段之后,在一个相当长的时间段中,是没有任何反馈信息的。整个开发过程对于客户和管理层而言就像一个黑盒子,进度完全看不到。而且,在项目开发过程中,这样的现象不仅出现在开发团队与客户、管理层之间,还包括在开发团队内部。因此,开发团队需要更加注重反馈。反馈对于任何软件项目的成功都是至关重要的,而在 XP 方法论中则更进一步,通过持续、明确的反馈来暴露软件状态的问题。
反馈与沟通有着良好的配合,及时和良好的反馈有助于沟通。而简单的系统,更有利于测试和反馈。
(4)勇气。在应用 XP 方法时,每时每刻都在应对变化:由于沟通良好,会有更多需求变更的机会;由于时刻保持系统的简单,新的变化会带来一些重新开发的需要;由于反馈及时,会有更多中间打断思路的新需求。总之,这一切使得开发团队处于变化之中,因此,这时就需要有勇气来面对快速开发,面对可能的重新开发。勇气可以来源于沟通,因为它使得高风险、高回报的试验成为可能;勇气可以来源于简单,因为面对简单的系统,更容易鼓起勇气;勇气可以来源于反馈,因为可以及时获得每一步前进的状态(自动测试),会让人更勇于重构代码。
在 XP 的四大价值观之下,隐藏着一种更深刻的东西,那就是尊重。因为这一切都建立在团队成员之间相互关心、相互理解的基础之上。
2.十二个最佳实践
在 XP 中,集成了 12 个最佳实践,有趣的是,它们没有一个是创新的概念,大多数概念和编程一样老。其主要的创新点在于提供一种良好的思路将这些最佳实践结合在一起,并且确保尽可能彻底地执行,使得它们能够在最大程度上互相支持。
(1)计划游戏。计划游戏的主要思想就是先快速地制定一份概要的计划,然后,随着项目细节的不断清晰,再逐步完善这份计划。计划游戏产生的结果是一套用户故事及后续的一两次迭代的概要计划。
(2)小型发布。XP 方法秉承的是“持续集成、小步快走”的哲学思维,也就是说每一次发布的版本应该尽可能地小,当然前提条件是每个版本有足够的商业价值,值得发布。由于小型发布可以使得集成更频繁,客户获得的中间结果越频繁,反馈也就越频繁,客户就能够实时地了解项目的进展情况,从而提出更多的意见,以便在下一次迭代中计划进去,以实现更高的客户满意度。
(3)隐喻。相对而言,隐喻比较令人费解。根据词典中的解释是:“一种语言的表达手段,它用来暗示字面意义不相似的事物之间的相似之处”。隐喻常用于四个方面:寻求共识、发明共享语汇、创新的武器、描述架构。
如果能够找到合适的隐喻是十分快乐的,但并不是每一种情况都可
以找到恰当的隐喻,因此,没有必要去强求,而是顺其自然。
(4)简单设计。强调简单的价值观,引出了简单性假设原则,落到实处就是“简单设计”实践。这个实践看上去似乎很容易理解,但却又经常被误解,许多批评者就指责 XP 忽略设计是不正确的。其实,XP 的简单设计实践并不是要忽略设计,而是认为设计不应该在编码之前一次性完成,因为那样只能建立在“情况不会发生变化”或者“我们可以预见所有的变化”之类的谎言的基础上。
(5)测试先行。对于有些团队而言,有时候程序员会以“开发工作太紧张”为理由,继而忽略测试工作。这样,就导致了一个恶性循环,越是没空编写测试程序,代码的效率与质量越差,花在找缺陷、解决缺陷的时间也越来越多,实际产能大大降低。由于产能降低,因此时间更紧张,压力就更大。
(6)重构。重构是一种对代码进行改进而不影响功能实现的技术,XP 需要开发人员在“闻到代码的坏味道”时,就有重构代码的勇气。重构的目的是降低变化引发的风险、使得代码优化更加容易。
(7)结对编程。从 20 世纪 60 年代开始,就有类似的实践在进行,长年以来的研究结果给出的结论是,结对编程的效率反而比单独编程更高。一开始虽然会牺牲一些速度,但慢慢地,开发速度会逐渐加快。究其原因,主要是结对编程大大降低了沟通的成本,提高了工作的质量。结对编程技术被誉于 XP 保证工作质量、强调人文主义的一个最典型的实践,应用得当还能够使开发团队协作更加顺畅、知识交流与共享更加频繁、团队稳定性也会更加牢固。
(8)集体代码所有制。由于 XP 方法鼓励团队进行结对编程,而且认为结对编程的组合应该动态地搭配,根据任务的不同、专业技能的不同进行最优组合。因此,每一个人都会遇到不同的代码,代码的所有制就不再适合于私有,因为那样会给修改工作带来巨大的不便。所谓集体代码所有制,就是团队中的每个成员都拥有对代码进行改进的权利,每个人都拥有全部代码,也都需要对全部代码负责。同时,XP 强调代码是谁破坏的(修改后出现问题),就应该由谁来修复。
集体代码所有制是 XP 与其他敏捷方法的一个较大不同,也是从
另一个侧面体现了 XP 中蕴含的很深厚的编码情节。
(9)持续集成。在前面谈到小型发布、重构、结对编程、集体代码所有制等最佳实践的时候,多次提到“持续集成”,可以说持续集成是这些最佳实践的基本支撑条件。
(10)每周工作 40 小时。这是最让开发人员开心、管理者反对的一个最佳实践了,加班、再加班早已成为开发人员的家常便饭,也是管理者最常使用的一种策略。而 XP 方法认为,加班最终会扼杀团队的积极性,最终导致项目的失败,这也充分体现了 XP 方法关注人的因素比关注过程的因素更多一些。不过,有一点是需要解释的,“每周工作 40 小时”中的“40”不是一个绝对数,它所代表的意思是团队应该保证按照“正常的时间” 进行工作。
(11)现场客户。为了保证开发出来的结果与客户的预想接近,XP 方法认为最重要的是需要将客户请到开发现场。就像计划游戏中提到过的,在 XP 项目中,应该时刻保证客户负责业务决策,开发团队负责技术决策。因此,在项目中有客户在现场明确用户故事,并做出相应的业务决策,对于 XP 项目而言有着十分重要的意义。
(12)编码标准。拥有编码标准可以避免团队在一些与开发进度无关的细枝末节问题上发生争论,而且会给重构、结对编程带来很大的麻烦。不过,XP 方法的编码标准的目的不是创建一个事无巨细的规则列表,而是要能够提供一个确保代码清晰,便于交流的指导方针。
有句经典名言“1+1>2”最适合表达 XP 的观点,Kent Beck 认为,XP 方法的最大价值在于,在项目中融会贯通地运用这 12 个最佳实践,而非单独使用。当然,可以使用其中的一些实践,但这并不意味着就应用了 XP 方法。XP 方法真正能够发挥其效能,就必须完整地运用 12 个实践。
6.4.2 特征驱动开发
FDD 方法来自于一个大型的新加坡银行项目。FDD 的创立者 Jeff De Luca 和 Peter Coad 分别是这个项目的项目经理和首席架构设计师。在 Jeff 和 Peter 接手项目时,客户已经经历了一次项目的失败,从用户到高层都对这个项目持怀疑的态度,项目组士气低落。
随后, Jeff 和 Peter 采用了特征驱动、彩色建模等方法,最终获得了巨大成功。
FDD 是也是一个迭代的开发模型。FDD 的每一步都强调质量,不断地交付可运行的软件,并以很小的开发提供精确的项目进度报告和状态信息。同敏捷方法一样,FDD 弱化了过程在软件开发中的地位。虽然 FDD 中也定义了开发的过程,不过一个几页纸就能完全描
述的过程深受开发者的喜爱。
1.FDD 角色定义
FDD 认为,有效的软件开发不可缺少的三个要素是:人、过程和技术。软件开发不能没有过程,也不能没有技术,但软件开发中最重要的是人。个人的生产率和人的技能将会决定项目的成败。为了让项目团队能够紧密地工作在一起,FDD 定义了 6 种关键的项目角色:
(1)项目经理。项目经理是开发的组织者,但项目经理不是开发的主宰。对于项目团队来说,项目经理应该是团队的保护屏障。他将同团队外界(如高层领导、人事甚至写字楼的物业管理员)进行沟通,努力为团队提供一个适宜的开发环境。
(2)首席架构设计师。不难理解,首席架构设计师负责系统架构的设计。
(3)开发经理。开发经理负责团队日常的开发,解决开发中出现的技术问题与资源冲突。
(4)主程序员。主程序员将带领一个小组完成特征的详细设计和构建的工作,一般要求主程序员具有一定的工作经验,并能够带动小组的工作。
(5)程序员。若干个程序员在主程序员的带领下形成一个开发小组,按照特征开发计划完成开发。
(6)领域专家。领域专家是对业务领域精通的人,一般由客户、系统分析员等担当。
领域专家作为关键的项目角色正是敏捷宣言中“业务人员同开发人员紧密合作”的体现。
根据项目规模的大小,有些角色是可以重复的。例如在一个小规模项目中,项目经理自身的能力很强,他就可以同时担当项目经理、首席架构设计师和开发经理的角色。
2.核心过程
FDD 共有 5 个核心过程,如图 6-6 所示。
(1)开发整体对象模型。开发整体对象模型也就是业务建模的阶段。不过 FDD 强调的是系统地完整地面向对象建模,这种做法有助于把握整个系统,而不仅仅关注系统中的若干个点。在这一阶段,领域专家和首席架构设计师相互配合,完成整体对象模型。
(2)构造特征列表。完成系统建模后,需要构造一个完整的特征列表。所谓特征指的是一个小的、对客户有价值的功能。采用动作、结果和目标来描述特征,特征的粒度最好可以在两周之内实现。在这一阶段中,可以整理出系统的需求。
(3)计划特征开发。很少看到有哪个软件在开发过程中明确包含计划过程,其实任何一个软件项目都必须有计划——无论是重载方法还是敏捷方法。在这一阶段中,项目经理根据构造出的特征列表、特征间的依赖关系进行计划,安排开发任务。
(4)特征设计。在这一阶段,主程序员将带领特征小组对特征进行详细设计,为后面的构建做准备。
(5)特征构建。特征构建和特征设计这两个阶段合并起来可以看做特征的实现阶段,这两个阶段反复地迭代,直到完成全部的开发。
3.最佳实践
组成 FDD 的最佳实践包括:领域对象建模、根据特征进行开发、类的个体所有、组成特征小组、审查、定期构造、配置管理、结果的可见性。
其中,最有特色的莫过于类的个体所有。几乎所有的开发模型都是代码共有,程序员们负责开发系统中的全部代码,并通过配置管理和变更控制来保持代码的一致性。在 FDD 中,将类分配给特定的任何小组,分配给 A 成员的代码将全部由 A 来维护,除 A 外的角色都不能修改它,只能使用它。这样做当然有它的优点:个人对所分配的类很容易保持概念的完整性;开发类代码的人肯定是最熟悉这个类的主人;而对这个类的支配感会促使开发人员产生自豪感,从而更出色地完成任务。不过 FDD 也提到了类个体所有的缺陷:项目中的依赖关系增强、当 A 需要 B 修改他自己的类时,必须等待 B 完成修改才能使用;类的个体所有增加了员工离职的损失。面对这些优点和缺陷,显然 FDD 认为类的个体所有对系统开发更有帮助。
除类的个体所有外,审查也是 FDD 中很具特色的一项实践。不少人都认为审查是非常严格的软件过程所特有的,因为进行审查不但要花费不少的人力和时间,对审查者本身的素质也有要求。然而在 FDD 中,明确地将审查作为一项最佳实践提出。审查是一种很有效的发现缺陷的手段,但经常被忽视,国内的软件组织中很少有严格审查制度保证软件质量。有效的审查可以发现很多潜在的问题,而这些问题往往是无法通过测试发现的,例如建模、需求和设计期的缺陷。这些潜在的缺陷大多要到系统测试甚至发布后才能发现,修正这些缺陷的代价是很大的。
6.4.3 Scrum
Scrum 是一个用于开发和维持复杂产品的框架,是一个增量的、迭代的开发过程。在这个框架中,整个开发过程由若干个短的迭代周期组成,一个短的迭代周期称为一个 Sprint,每个 Sprint 的建议长度是 2 到 4 周(互联网产品研发可以使用 1 周的 Sprint)。在 Scrum 中,使用产品 Backlog 来管理产品的需求,产品 Backlog 是一个按照商业价值排序的需求列表,列表条目的体现形式通常为用户故事。Scrum 团队总是先开发对客户具有较高价值的需求。在 Sprint 中,Scrum 团队从产品 Backlog 中挑选最高优先级的需求进行开发。挑选的需求在 Sprint 计划会议上经过讨论、分析和估算得到相应的任务列表,我们称它为 Sprint backlog。在每个迭代结束时,Scrum 团队将递交潜在可交付的产品增量。 Scrum 起源于软件开发项目,但它适用于任何复杂的或是创新性的项目。Scrum 的基本流程如图 6-7 所示。
1.Scrum 的五个活动
Scrum 主要包括:产品待办事项列表梳理、Sprint 计划会议、每日 Scrum 会议、Sprint 评审会议、Sprint 回顾会议等五个活动。
(1)产品待办事项列表梳理
产品待办事项通常会很大,也很宽泛,而且想法会变来变去、优先级也会变化,所以产品待办事项列表梳理是一个始终贯穿整个 Scrum 项目的活动。该活动包含但不限于以下的
内容:保持产品待办事项列表有序、把看起来不再重要的事项移除或者降级、增加或提升涌现出来的或变得更重要的事项、将事项分解成更小的事项、将事项归并为更大的事项、对事项进行估算。
产品待办事项列表梳理的一个最大好处是为即将到来的几个 Sprint 做准备。为此,梳理时会特别关注那些即将被实现的事项。需要考虑不少因素,这包括但不限于以下的内容:
理想情况下,下一个 Sprint 的备选事项都应该提升“商业价值”。开发团队需要能够在一个 Sprint 内完成每一个事项。每个人都需要清楚预期产出是什么。 产品开发决定了,有可能需要其他的技能和输入。因此,产品待办事项列表梳理最好是所有团队成员都参与的活动,而不单单是产品负责人。
(2)Sprint 计划会议
每个 Sprint 都以 Sprint 计划会议作为开始,这是一个固定时长的会议,在这个会议中,Scrum 团队共同选择和理解在即将到来的 Sprint 中要完成的工作。
整个团队都要参加 Sprint 计划会议。针对排好序的产品待办事项列表(Product Backlog),产品负责人和开发团队成员讨论每个事项,并对该事项达成共识,包括根据当前的“完成的定义”,为了完成该事项所需要完成的所有事情。所有的 Scrum 会议都是限定时长的。Sprint 计划会议推荐时长是 Sprint 中的每周对应两小时或者更少(例如,一个 Sprint 包含 2 个星期,则 Sprint 计划会议时长应为 4 个小时或者更少)。因为会议是限制时长的,Sprint 计划会议的成功十分依赖于产品待办事项列表的质量。这就是产品待办事项列表梳理十分重要的原因。
在 Scrum 中,Sprint 计划会议有两部分:
决定在 Sprint 中需要完成哪些工作
决定这些工作如何完成
第一部分:需要完成哪些工作?
在会议的第一部分,产品负责人向开发团队介绍排好序的产品待办事项,整个 Scrum 团队共同理解这些工作。
Sprint 中需要完成的产品待办事项数目完全由开发团队决定。为了决定做多少,开发团队需要考虑当前产品增量的状态,团队过去的工作情况,团队当前的生产能力,以及排好序的产品待办事项列表。做多少工作只能由开发团队决定。产品负责人或任何其他人,都不能给开发团队强加更多的工作量。
通常 Sprint 都有个目标,称作 Sprint 目标。这将十分有效地帮助大家更加专注于需要完成的工作的本质,而不必花太多精力去关注那些对于我们需要完成的工作并不重要的小细节。
第二部分:如何完成工作?
在会议的第二部分里,开发团队需要根据当前的“完成的定义”一起决定如何实现下一个产品增量。他们进行足够的设计和计划,从而有信心可以在 Sprint 中完成所有工作。前几天的工作会被分解成小的单元,每个工作单元不超过一天。之后要完成的工作可以稍大些,以后再对它们进行分解。
决定如何完成工作是开发团队的职责,决定做什么则是产品负责人的职责。在计划会议的第二部分,产品负责人可以继续留下来回答问题,以及澄清一些误解。
不管怎样,团队应该很容易找到产品负责人。
Sprint 计划会议的产出。Sprint 计划会议最终需要 Scrum 团队对 Sprint 需要完成工作的数量和复杂度达成共识,并预期在一个合理的条件范围内完成它们。开发团队预测并共同承诺他们要完成的工作量。总而言之:在 Sprint 计划会议中,开发团队和产品负责人一起考虑并讨论产品待办事项,确保他们对这些事项的理解,选择一些他们预测能完成的事项,创建足够详细的计划来确保他们能够完成这些事项。最终产生的待办事项列表就是“Sprint 待办事项列表(Sprint Backlog)”。
(3)每日 Scrum 会议
开发团队是自组织的。开发团队通过每日 Scrum 会议来确认他仍然可以实现 Sprint 的目标。这个会议每天在同样的时间和同样的地点召开。每一个开发团队成员需要提供以下三点信息:
从上一个每日 Scrum 到现在,我完成了什么;
从现在到下一个每日 Scrum,我计划完成什么;
有什么阻碍了我的进展。
每日 Scrum 中可能有简要的问题澄清和回答,但是不应该有任何话题的讨论。通常,许多团队会在每日 Scrum 之后马上开会处理他们遇到的任何问题。
每日 Scrum 既不是向管理层汇报,也不是向产品负责人或者 ScrumMaster 汇报。它是一个开发团队内部的沟通会议,来保证他们对现状有一致的了解。只有 Scrum 团队的成员,包括 ScrumMaster 和产品负责人,可以在会议中发言。其他感兴趣的人可以来旁听。在必要时,开发团队会基于会议中的发现重新组织他们的工作来完成 Sprint 的目标。
每日 Scrum 是 Scrum 的一个关键组成部分,它可以带来透明性,信任和更好的绩效。它能帮助快速发现问题,并促进团队的自组织和自立。所有 Scrum 会议都是限定时长的。每日 Scrum 通常不超过 15 分钟。
(4)Sprint 评审会议
Sprint 结束时,Scrum 团队和相关人员一起评审 Sprint 的产出。Sprint 评审会议的推荐时长是 Sprint 中的每一周对应一个小时(例如,一个 Sprint 包含 2 个星期,则 Sprint 评审会议时长为 2 个小时)。
讨论围绕着 Sprint 中完成的产品增量。由于 Sprint 的产出会涉及一些人的“利益”,因此一个明智的做法是邀请他们参加这个会议,这会很有帮助。这个会议是个非正式的会议,帮助大家了解我们目前进展到哪里,并一起讨论我们下一步如何推进。每个人都可以在Sprint 评审会议上发表意见。当然,产品负责人会对未来做出最终的决定,并适当地调整产品待办事项列表 Product Backlog。
团队会找到他们自己的方式来开 Sprint 评审会议。通常会演示产品增量,整个小组也会经常讨论他们在 Sprint 中观察到了什么、有哪些新的产品想法出现。他们还会讨论产品待办事项列表的状态、可能的完成日期以及在这些日期前能完成什么。Sprint 评审会议向每个人展示了当前产品增量的概况。因此,通常都会在 Sprint 评审会议中调整产品待办事项列表。
(5)Sprint 回顾会议
在每个 Sprint 结束后,Scrum 团队会聚在一起开 Sprint 回顾会议,目的是回顾一下团队在流程人际关系以及工具方面做得如何。团队识别出哪些做得好,哪些做得不好,并找出潜在的改进事项,为将来的改进制定计划。Sprint 回顾会议的推荐时长是 Sprint 中的每一周对应
一个小时(例如,一个 Sprint 包含 2 个星期,则 Sprint 回顾会议时长为 2 个小时)。 Scrum 团队总是在 Scrum 的框架内,改进他们自己的流程。
2.Scrum 的 5 大价值观
Scrum 的 5 大价值观为:
承诺—愿意对目标做出承诺。
专注—把你的心思和能力都用到你承诺的工作上去。
开放—Scrum 把项目中的一切开放给每个人看。
尊重—每个人都有他独特的背景和经验。
勇气—有勇气做出承诺,履行承诺,接受别人的尊重。
6.4.4 水晶方法
水晶方法(Crystal),是由 Alistair Cockburn 和 Jim Highsmith 建立的敏捷方法系列,其目的是发展一种提倡“机动性的”方法,包含具有共性的核心元素,每个都含有独特的角色、过程模式、工作产品和实践。Crystal 家族实际上是一组经过证明、对不同类型项目非常有效的敏捷过程,它的发明使得敏捷团队可以根据其项目和环境选择最合适的 Crystal 家族成员(分为 Crystal Clear,Crystal Yellow,Crystal Orange 和 Crystal Red 分别适用于不同的项
目)。水晶方法中,使用频度较高的是 Crystal Clear——透明水晶方法。透明水晶方法,适合于一个小团队来进行敏捷开发,人数在 6 人以下为宜。
透明水晶方法有七大体系特征:
(1)经常交付
任何项目,无论大小、敏捷程度,其最重要的一项体系特征是每过几个月就向用户交付已测试的运行代码。如果你使用了此体系特征,你就会发现,“经常交付”的作用还是很让人吃惊的。项目主办者根据团队的工作进展获得重要反馈。用户有机会发现他们原来的需求是否是他们真正想要的,也有机会将观察结果反馈到开发当中。开发人员打破未决问题的死结,从而实现对重点的持续关注。团队得以调整开发和配置的过程,并通过完成这些工作鼓舞团队的士气。
(2)反思改进
在我们的开发中,时常会出现这样那样的问题,技术难题、各种心事等,这会在很大的程度上影响项目的进展。而且,如果其他任务对这项任务有依赖的话,那么其他的任务也会被推迟,这就很可能会导致项目的失败。换句话说,如果,我们能够经常在迭代会中及时地反思和改进,那么,这种事情应该是不会发生的,或者说发生了,也能够很快地找到解决方案去应对它。事实上,从慌乱的日常
开发中,抽出一点时间来思考更为行之有效的工作方法就已经足够了。
(3)渗透式交流
渗透交流就是信息流向团队成员的背景听觉,使得成员就像通过渗透一样获取相关信息。这种交流通常都是通过团队成员在同一间工作室内工作而实现的。若其中一名成员提出问题,工作室内的其他成员可以选择关注或不关注的态度,可以加入到这个问题的讨论当中来,也可以继续忙自己的工作。
(4)个人安全
个人安全指的是当你指出困扰你的问题时,你不用担心受到报复。个人安全非常重要,有了它,团队可以发现和改正自身的缺点。没有它,团队成员们知而不言,缺点则愈发严重以至于损害整个团队。个人安全是迈向信任的第一步。有了信任,团队协作才能真正地实施,开发效率也就会直线上升的。
(5)焦点
所谓“焦点”,就是确定首先要做什么,然后安排时间,以平和的心态开展工作。确保团队成员清楚地了解他们自己最重要的任务是什么,确保他们能够有充分的时间去完成这些任务。
(6)与专家用户建立方便的联系
与专家用户持续建立方便的联系能够给团队提供:对经常交付进行配置以及测试的地方,关于成品质量的快速反馈,关于设计理念的快速反馈,最新的(用户)需求。
(7)配有自动测试、配置管理和经常集成功能的技术环境自动测试可以为开发人员在代码修改后就可以进行自动测试,并且能够发现存在的一些bug,以至开发人员能够及时地进行修改,对于他们来说,节省了时间,提高了效率,而且还不用为烦人的测试而苦恼。
配置管理系统允许人们不同步地对工作进行检查,可撤销更改,并且可以将某一系统设置保存后进行新系统的发布,当新系统出现问题,即可还原原系统的设置。
经常集成可以使得团队在一天之内对系统进行多次集成。其实,团队越频繁地对系统进行集成,他们就能够越快地发现错误,堆积到一起的错误也会越少,并使他们产生更新的灵感。
最好的团队是将这三大技术结合成“持续测试集成技术”。这样做他们可以在几分钟内发现因集成所产生的错误。
6.4.5 其他敏捷方法
除了上面介绍的几种敏捷方法,以下敏捷方法我们也需要掌握其基本特征。开放式源码:开放式源码指的是开放源码界所用的一种运作方式。
开放式源码项目有一个特别之处,就是程序开发人员在地域上分布很广,这使得它和其他敏捷方法不同,因为一般的敏捷方法都强调项目组成员在同一地点工作。开放源码的一个突出特点就是查错
排障(debug)的高度并行性,任何人发现了错误都可将改正源码的“补丁”文件发给维护者。然后由维护者将这些“补丁”或是新增的代码并入源码库。ASD 方法:ASD (Adaptive Software Development)方法由 Jim Highsmith 提出,其核心是三个非线性的、重叠的开发阶段:猜测、合作与学习。
6.5 软件重用
软件重用技术是一种重要的软件开发方法,虽然至今软件重用技术还不够成熟,离理想中的软件工厂还有很长的路要走,但现有的一些重用技术(例如,中间件、应用服务器等)已经改变了开发过程。
6.5.1 软件重用
软件产品与其他的产品不同,是抽象的,一旦产生就可以无限制地复制,因此重复利用软件产品的意义重大,可以节约大量的人力物力。软件重用指的是利用已经存在的软件元素建立新的软件系统,这其中的软件元素既可以是软件产品、源程序,也可以是文档、设计思想甚至是领域知识。软件重用可以直接提高软件的开发效率、降低软件的开发成本、缩短软件的开发周期、提高软件质量。
常见的软件重用形式包括:
(1)源代码重用。这是最简单也是最常见的重用形式,但由于软件系统的复杂性,很难大规模地重用已有源代码。
(2)架构重用。架构重用也很常见,随着软件架构风格和设计模式的推广和应用,架构重用已经对软件开发产生了重大的影响。
(3)应用框架的重用。随着软件技术的发展,应用框架的重用变得越来越普遍,很多成熟的软件公司都建立了自己的开发框架。在开源社区中,世界各地的技术爱好者也在不断地推出应用了各种新技术的开发框架,例如,应用了 AOP(Aspect Oriented Programming,面向方面编程)技术的 Spring 等。
(4)业务建模的重用。虽然不同的软件的业务领域各自不同,但人们还是总结出了一些常见领域的建模方法,重用这些领域模型可以降低因领域知识不足而造成的需求风险。
(5)文档及过程的重用。软件文档和软件过程也是软件开发中不可或缺的元素,有效地重用这些文档和过程也有助于提高开发效率和软件质量、降低开发成本。
(6)软构件的重用。关于软构件的重用,请参考 5.5.2 节。
(7)软件服务的重用。随着 Web 服务的提出,人们越来越关注服务的重用。SOA(Service-Oriented Architecture,面向服务的架构)提出了面向服务的软件架构,并定义了相应的标准。但 SOA 还不够成熟,相信这一领域在未来的几年中还将取得更大的进展。
6.5.2 构件技术
构件又称为组件,是一个自包容、可复用的程序集。首先,构件是一个程序集,或者说是一组程序的集合。这个集合可能会以各种方式体现出来,如源程序或二进制的代码。这个集合整体向外提供统一的访问接口,构件外部只能通过接口来访问构件,而不能直接操作构件的内部。
构件的两个最重要的特性是自包容与可重用。自包容指的是构件的本身是一个功能完整的独立体,构件内部与外部的功能界限清晰明确,可以独立配置与使用。而可重用既是构件的特点,也是构件出现的目的。早在 1968 年 NATO 软件工程会议,Mcllroy 的论文《大量生产的软件构件》中,就提出了“软件组装生产线”的思想。从那以后,使用构件技术实现软件复用,采用“搭积木”的方式生产软件,就成为软件人员的梦想。
构件的开发者和使用者往往不是相同的人或组织,所以必须定义构件的标准才能够消除其中的障碍。随着构件技术的发展,目前应用比较广泛的构件标准有 CORBA、Java Bean/EJB、COM/DCOM。
应用构件技术开发软件可以使用构件组装模型,见 5.5.1 中的介绍。
6.6 基于架构的软件设计
基于架构的软件设计(Architecture-Based Software Design,ABSD)是一种架构驱动方法。这种方法有 3 个基础:
(1)功能的分解。在功能分解中,ABSD 方法使用已有的基于模块的内聚和耦合技术。
(2)通过选择架构风格来实现质量和业务需求。
(3)软件模板的使用。软件模板利用了一些软件系统的结构。
然而,对于设计方法来说,软件模板的使用是一个新概念,下面,我们进行简单的介绍。 软件模板是一个特殊类型的软件元素,包括描述所有这种类型的元素在共享服务和底层构造的基础上如何进行交互。软件模板还包括属于这种类型的所有元素的功能,这些功能的例子有:每个元素必须记录某些重大事件,每个元素必须为运行期间的外部诊断提供测试点等。在软件产品线系统中,软件模板显得格外重要,因为新元素的引入是一个通用的技术,这种技术用来使产品线架构适应一个特定的产品。
ABSD 方法是递归的,且迭代的每一个步骤都是清晰定义的。因此,不管设计是否完成,架构总是清晰的,这有助于降低架构设计的随意性。
6.6.1 ABSD 方法与生命周期
图 6-8 描述了 ABSD 方法在生命周期中的位置。尽管我们没有描述一个需求获取、组织或跟踪的特定方法,但还是假设一个需求阶段至少部分地完成,从需求阶段(包括功能需求、质量和业务需求、约束等)获得了输出。ABSD 方法的输出是三个视图的概念构件的集合,包括能够产生每个概念构件的假定、软件模板的集合和那些已经做出的具体实现的决策,我们把具体实现决策当作附加约束来维护。
在 ABSD 方法中,必须记录所有做出的决策及这些决策的原理,这有利于决策的可跟踪性和决策评审。
ABSD 方法的输入由下列部分组成:
(1)抽象功能需求,包括变化的需求和通用的需求;
(2)用例(实际功能需求);
(3)抽象的质量和业务需求;
(4)质量因素(实际质量和业务需求);
(5)架构选项;
(6)约束。
下面,我们描述需求阶段的假定输出,即 ABSD 方法的输入。
1.抽象功能需求
ABSD 方法假定需求阶段的输出之一是功能需求的抽象描述,包括这些需求的粗略变化的描述。当获取需求时,考虑所有最终用户是重要的。
对一个特定系统来说,通常有不同类型的最终用户。不同的系统管理员(数据库管理员、系统管理员、网络管理员等)都可以是最终用户。维护工程师也可以是系统的最终用户。总之,一个最终用户就是当系统运行时使用系统的任何人员。
与抽象功能需求相联系的是对公共需求和与这些需求相关的粗略变化的描述,在设计阶段,理解这些需求之间的依赖关系是至关重要的。
我们必须在某种抽象级别上获取功能需求,产品的详细需求往往要等具体产品开发完成后才能知道。当详细需求明确时,抽象功能的获取为详细需求提供了分类。
2.用例
如前所述,用例是一个或多个最终用户与系统之间的交互的具体表述,在这里,最终用户既可以是操作人员,也可以是与系统进行交互操作的其他软件系统。虽然用例很容易找到和创建,甚至可能有成百上千个,但是,因为我们需要分析用例,所以必须限制用例的数量。
在架构设计阶段,只有重要的用例才有用。我们必须对所创建的用例进行分组、设置优先级,以便筛选出最重要的用例,剩下的用例可以在设计阶段的任何时候创建。
3.抽象的质量和业务需求
我们必须对待构建系统的质量和业务需求进行编号,每个质量属性都包含一个特定的刺激,以及希望得到的响应。质量需求要尽量具体化。
4.架构选项
对每个质量和业务需求,我们都要列举能够满足该需求的所有可能的架构。例如,如果需求是支持一系列不同的用户界面,则可能的架构选择就是把不同的用户界面分解成不同的构件。又如,如果需求是保持操作系统的独立性,则可能的架构选择就是构建虚拟的操作系统层,接受所有的操作系统调用,并解释之为当前操作系统所能支持。 在这个时候,只需列举所有可能的选项,而不需要对这些架构选项进行决策,这种列举取决于设计师的经验,既可来自某些书籍介绍,也可直接来自设计师本身的实践。
5.质量场景
正如用例使功能需求具体化一样,质量场景使质量需求具体化。质量场景是质量需求的特定扩充。与用例一样,质量场景也很容易找到和创建,可以创建很多个。我们必须对质量场景进行分组、设置优先级,只需验证最重要的质量场景。
6.约束
约束是一个前置的设计决策,设计过程本身包含决策。某些决策可以直接由业务目标导出而无须考虑对设计的影响。例如,如果一个公司在某个中间件产品上投入了大量资金,那么在产品的选择上就可以不必考虑其他决策。在需求获取阶段,约束主要来自系统的业务目标。
在某些特殊情况下,约束由遗留系统决定。今天,几乎没有软件系统不参考已有系统的,常见的情况是,新老系统同时并存,或者新系统替代老系统,但是必须尽可能重用老系统的功能。在设计阶段,虽然这些遗留系统处于被设计系统的外部,但设计师必须考虑遗留系统的特征。也就是说,在某种程度上,遗留系统影响着当前的设计,因此,理解遗留系统的结构和解决问题的技术都很重要。出于商业目的,可能要求重用遗留系统的构件,这种需求就变成了约束。
6.6.2 基于架构的软件开发模型
基于架构的软件开发模型(Architecture-Based Software Design Model,ABSDM)把整个基于架构的软件过程划分为架构需求、设计、文档化、复审、实现、演化等 6 个子过程,如图 6-9 所示。
1.架构需求
需求是指用户对目标软件系统在功能、行为、性能、设计约束等方面的期望。架构需求受技术环境和架构设计师的经验影响。需求过程主要是获取用户需求,标识系统中所要用到的构件。架构需求过程如图 6-10 所示。如果以前有类似的系统架构的需求,我们可以从需求库中取出,加以利用和修改,以节省需求获取的时间,减少重复劳动,提高开发效率。
(1)需求获取
架构需求一般来自三个方面,分别是系统的质量目标、系统的业务目标和系统开发人员的业务目标。软件架构需求获取过程主要是定义开发人员必须实现的软件功能,使得用户能完成他们的任务,从而满足业务上的功能需求。与此同时,还要获得软件质量属性,满足一些非功能需求。
(2)标识构件
在图 6-10 中虚框部分属于标识构件过程,该过程为系统生成初始逻辑结构,包含大致的构件。这一过程又可分为三步来实现。
第一步:生成类图。生成类图的 CASE 工具有很多,例如 Rational Rose 就能自动生成类图。
第二步:对类进行分组。在生成的类图基础上,使用一些标准对类进行分组可以大大简化类图结构,使之更清晰。一般地,与其他类隔离的类形成一个组,由泛化关联的类组成一个附加组,由聚合或组合关联的类也形成一个附加组。
第三步:把类打包成构件。把在第二步得到的类簇打包成构件,这些构件可以分组合并成更大的构件。
(3)需求评审
组织一个由不同代表(如分析人员、客户、设计人员、测试人员)组成的小组,对架构需求及相关构件进行仔细的审查。审查的主要内容包括所获取的需求是否真实反映了用户的要求,类的分组是否合理,构件合并是否合理等。必要时,可以在“需求获取—标识构件—需求评审”之间进行迭代。
2.架构设计
架构需求用来激发和调整设计决策,不同的视图被用来表达与质量目标有关的信息。架构设计是一个迭代过程,如果要开发的系统能够从已有的系统中导出大部分,则可以使用已有系统的设计过程。软件架构设计过程如图 6-11 所示。
(1)提出软件架构模型
在建立架构的初期,选择一个合适的架构风格是首要的。在这个风格基础上,开发人员通过架构模型,可以获得关于架构属性的理解。此时,虽然这个模型是理想化的(其中的某些部分可能错误地表示了应用的特征),但是,该模型为将来的实现和演化过程建立了目标。
(2)把已标识的构件映射到软件架构中
把在架构需求阶段已标识的构件映射到架构中,将产生一个中间结构,这个中间结构只包含那些能明确适合架构模型的构件。
(3)分析构件之间的相互作用
为了把所有已标识的构件集成到架构中,必须认真分析这些构件的相互作用和关系。
(4)产生软件架构
一旦决定了关键的构件之间的关系和相互作用,就可以在第 2 阶段得到的中间架构的基础上进行细化。
(5)设计评审
一旦设计了软件架构,我们必须邀请独立于系统开发的外部人员对架构进行评审。
3.架构文档化
绝大多数的架构都是抽象的,由一些概念上的构件组成。例如,层的概念在任何程序设计语言中都不存在。因此,要让系统分析师和程序员去实现架构,还必须得把架构进行文档化。文档是在系统演化的每一个阶段,系统设计与开发人员的通信媒介,是为验证架构设计和提炼或修改这些设计(必要时)所执行预先分析的基础。
架构文档化过程的主要输出结果是架构需求规格说明和测试架构需求的质量设计说明书这两个文档。生成需求模型构件的精确的形式化的描述,作为用户和开发者之间的一个协约。
软件架构的文档要求与软件开发项目中的其他文档是类似的。文档的完整性和质量是软件架构成功的关键因素。文档要从使用者的角度进行编写,必须分发给所有与系统有关的开发人员,且必须保证开发者手上的文档是最新的。
4.架构复审
从图 5-8 中我们可以看出,架构设计、文档化和复审是一个迭代过程。从这个方面来说,在一个主版本的软件架构分析之后,要安排一次由外部人员(用户代表和领域专家)参加的复审。
复审的目的是标识潜在的风险,以及早发现架构设计中的缺陷和错误,包括架构能否满足需求、质量需求是否在设计中得到体现、层次是否清晰、构件的划分是否合理、文档表达是否明确、构件的设计是否满足功能与性能的要求,等等。
由外部人员进行复审的目的是保证架构的设计能够公正地进行检验,使组织的管理者能够决定正式实现架构。
5.架构实现
所谓“实现”就是要用实体来显示出一个软件架构,即要符合架构所描述的结构性设计决策,分割成规定的构件,按规定方式互相交互。架构的实现过程如图 6-12 所示。
图 6-12 中的虚框部分是架构的实现过程。整个实现过程是以复审后的文档化的架构说明书为基础的,每个构件必须满足软件架构中说明的对其他构件的责任。这些决定即实现的约束是在系统级或项目范围内做出的,每个构件上工作的实现者是看不见的。
在架构说明书中,已经定义了系统中构件与构件之间的关系。因为在架构层次上,构件接口约束对外唯一地代表了构件,所以可以从构件库中查找符合接口约束的构件,必要时开发新的满足要求的构件。
然后,按照设计提供的结构,通过组装支持工具把这些构件的实现体组装起来,完成整个软件系统的连接与合成。
最后一步是测试,包括单个构件的功能性测试和被组装应用的整体功能和性能测试。
6.架构演化
在构件开发过程中,最终用户的需求可能还有变动。在软件开发完毕,正常运行后,由一个单位移植到另一个单位,需求也会发生变化。在这两种情况下,就必须相应地修改软件架构,以适应新的软件需求。架构演化过程如图 6-13 所示。架构演化是使用系统演化步骤去修改应用,以满足新的需求。主要包括以下七个步骤:
(1)需求变动归类首先必须对用户需求的变化进行归类,使变化的需求与已有构件对应。对找不到对应构件的变动,也要做好标记,在后续工作中,将创建新的构件,以对应这部分变化的需求。
(2)制订架构演化计划在改变原有结构之前,开发组织必须制订一个周密的架构演化计划,作为后续演化开发工作的指南。
(3)修改、增加或删除构件在演化计划的基础上,开发人员可根据在第一步得到的需求变动的归类情况,决定是否修改或删除存在的构件、增加新构件。最后,对修改和增加的构件进行功能性测试。
(4)更新构件的相互作用随着构件的增加、删除和修改,构件之间的控制流必须得到更新。
(5)构件组装与测试通过组装支持工具把这些构件的实现体组装起来,完成整个软件系统的连接与合成,形成新的架构。然后对组装后的系统的整体功能和性能进行测试。
(6)技术评审对以上步骤进行确认,进行技术评审。评审组装后的架构是否反映需求变动,符合用户需求。如果不符合,则需要在第 2 到第 6 步之间进行迭代。
(7)产生演化后的架构在原来系统上所作的所有修改必须集成到原来的架构中,完成一次演化过程。
6.7 形式化方法
形式化方法是指采用严格的数学方法,使用形式化规约语言来精确定义软件系统。非形式化的开发方法是通过自然语言、图形或表格描述软件系统的行为和特性,然后基于这些描述进行设计和开发,而形式化开发则是基于数学的方式描述、开发和验证系统。形式化方法包括形式化描述和基于形式化描述的形式化验证两部分内容。形式化描述就是用形式化语言进行描绘,建立软件需求和特性,即解决软件“做什么”的问题。形式化验证指的是验证已有的程序是否满足形式化描述的定义。形式化描述主要可以分为两类,一类是通过建立计算模型来描述系统的行为特性,另一类则通过定义系统必须满足的一些属性来描述系统。形式化描述又称之为形式化规约,相对于自然语言描述,形式化描述是精确的、可验证的,避免了模糊性与二义性,消除需求中相互矛盾的地方,避免需求定义人员和开发人员对需求的理解偏差。
形式化描述可以通过计算机技术进行自动处理,进行一致性的检查和证明,提高需求分析的效率和质量。通过形式化描述,需求分析的质量大大提高,很多自然语言描述无法避免的缺陷在需求分析阶段就会被发现,并得到解决,从而降低后期开发和维护的成本,并提升软件的质量和可靠性。
在一些要求高可靠性的关键应用上,采用形式化开发方法可以保证软件系统的可靠性。如巴黎地铁 14 号线和 Roissy 机场穿梭车的自动控制系统。这两个系统中的部分程序使用了形式化方法进行开发,并取得了很好的效果,如表 6-1 所示。
表 6-1 中的 ADA 代码行数表示运用形式化方法开发的软件系统规模,这些代码是形式化方法自动生成的,开发人员并不需要直接修改这些代码。
这两个案例都没有进行单元测试,而在非形式化开发中,这类关键
应用系统的软件的单元测试和集成测试都是非常重要的工作,通常要付出高昂的代价。形式化开发的优点可见一般。