《敏捷软件开发(原则模式与实践)》读书心得

刚看完瀑布模型过程代表作--《软件是这样炼成的》,书中介绍了一种庞大的、重型的过程方法。为了比较,又看了这本关于敏捷开发的书。
关于敏捷开发,之前陆陆续续有些了解,《敏捷软件开发(原则模式与实践)》的作者作为敏捷开发的创始者之一,在书中系统的说明了敏捷开发的思想以及相关实践。
敏捷开发(Agile Development),是一种面临迅速变化的需求快速开发软件的能力。为了获取这种敏捷性,我们需要使用一些可以提供必要的纪律和反馈的实践。我们需要使用一些可以保持我们的软件灵活、可维护的设计原则,并且我们需要知道一些已经被证明针对特定的问题可以平衡这些原则的设计模式。

本书其实就是按照上面这一段话进行编排,把敏捷开发的思想、实践(XP)、设计原则、设计模式进行了讲述。
作为一种开发思想,敏捷开发只是提供了一些原则性的、指导性的思想,而XP(eXtreme Programming 极限编程)是敏捷开发的实践之一。
敏捷开发强调尽早地、经常的进行交付,然后接受业务反馈进行迭代开发,这是一种轻量过程的开发模式。敏捷开发基于一个最重要的事实是:需求总是在变化。用户的需求是一直会变的,另外,用户不可能一下子提出所有的需求,直到他们看到了产品(或原型)。敏捷开发能通过业务的逐步反馈、逐步沟通,达到需求不断优化、功能不断完善来满足业务的变化。
和瀑布模型不同的是,敏捷开发缺少了明确的设计阶段,并且敏捷开发的进度衡量是可工作软件(可工作的功能占比),而不是传统意义上的文档。这就解决了在瀑布模型中,需要分析、设计、编码之间的相关文档冻结,最后导致交付的成品不能符合业务当下的要求,一种可能是需求分析的不完善,或者是当前的业务已经发生了变化。
作为敏捷思想最有名的实践,极限编程提出了符合敏捷开发的工作方式,客户作为团队成员、用户故事(索引卡片)、迭代、回顾会、测试驱动编程、持续集成(每日构建)、结对编程等等。这里面有很多理想的成分,在现实中很难落地,特别是在中国这个文化背景中。例如结对编程、客户一同办公,在中国的现状下是很难实现的。
所以在实际的实践中,往往是把敏捷思想和传统瀑布过程管理模型进行结合和剪裁。例如,客户不能参加到团队中,可以以需求分析师作为客户代表,随时反馈业务的需求;不能做到结对编程,但是可以在有问题的时候,按需派出专人进行指导。但是尽早经常交付、迭代等思想还是可以采用,虽然迭代、持续构建对测试团队的自动化测试能力、回归测试能力要求很高。 相关极限编程的应用现状参照:极限编程的应用现状
为了满足敏捷开发拥抱变化的思想,需要做到各次迭代之间软件结构的可维护性、灵活性,所以需要开发人员遵循相关面向对象的设计原则以及采用各种设计模式。作者对设计原则和设计模式讲述了很多,但是对比起《Head First 设计模式》一书,略显枯燥,所以本书这部分的章节基本略过了。
本书最后的“两个公司的讽刺小品”,把瀑布型和敏捷开发的过程通过故事的形式展现出来,看了让人忍俊不禁,在一笑之余,还是明白了敏捷型给开发团队带来优势。

敏捷软件开发宣言

  • 我们一直在实践中探寻更好的软件开发方法,
  • 身体力行的同时也帮助他人。由此我们建立了如下价值观:
  • 个体和互动 高于 流程和工具
  • 工作的软件 高于 详尽的文档
  • 客户合作 高于 合同谈判
  • 响应变化 高于 遵循计划

也就是说,尽管右项有其价值,我们更重视左项的价值。

敏捷宣言遵循的原则(12条)

我们遵循以下原则:

  • 我们最重要的目标,是通过持续不断地及早交付有价值的软件使客户满意。
  • 欣然面对需求变化,即使在开发后期也一样。为了客户的竞争优势,敏捷过程掌控变化。
  • 经常地交付可工作的软件,相隔几星期或一两个月,倾向于采取较短的周期。
  • 业务人员和开发人员必须相互合作,项目中的每一天都不例外。
  • 激发个体的斗志,以他们为核心搭建项目。提供所需的环境和支援,辅以信任,从而达成目标。
  • 不论团队内外,传递信息效果最好效率也最高的方式是面对面的交谈。
  • 可工作的软件是进度的首要度量标准。
  • 敏捷过程倡导可持续开发。责任人、开发人员和用户要能够共同维持其步调稳定延续。
  • 坚持不懈地追求技术卓越和良好设计,敏捷能力由此增强。
  • 以简洁为本,它是极力减少不必要工作量的艺术。
  • 最好的架构、需求和设计出自自组织团队。
  • 团队定期地反思如何能提高成效,并依此调整自身的举止表现。

面向对象设计的11个原则

  1. 单一职责原则(SRP)
    就一个类而言,应该仅有一个引起它变化的原因。
  2. 开放-封闭原则(OCP)
    软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改的。
  3. Liskov替换原则(LSP)
    子类型(subtype)必须能够替换掉它们的基类型(base type)。
  4. 依赖倒置原则(DIP)
    • 高层模块不应该依赖于低层模块,两者都应该依赖于抽象。
    • 抽象不应该依赖于细节,细节应该依赖于抽象。
  5. 接口隔离原则(ISP)
    不应该强迫客户依赖于它们不要的方法。接口属于客户,不属于它所在的类层次结构。
  6. 发布重用等价原则(REP)
    重用的粒度就是发布的粒度
  7. 共同重用原则(CRP)
    一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中所有其它类。
  8. 共同封闭原则(CCP)
    包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他的包不造成任何影响。
  9. 无环依赖原则(ADP)
    在包的依赖关系图中不允许存在环。
  10. 稳定依赖原则(SDP)
    朝着稳定的方向进行依赖。
  11. 稳定抽象原则(SAP)
    包的抽象程度应该和其稳定程度一致。

其中:
1-5的原则关注所有软件实体(类、模块、函数等)的结构和耦合性,这些原则能够指导我们设计软件实体和确定软件实体的相互关系;
6-8的原则关注包的内聚性,这些原则能够指导我们对类组包;
9-11的原则关注包的耦合性,这些原则帮助我们确定包之间的相互关系。

 极限编程实践

  1. 完整团队
    XP项目的所有参与者(开发人员、客户、测试人员等)一起工作在一个开放的场所中,他们是同一个团队的成员。这个场所的墙壁上随意悬挂着大幅的、显著的图表以及其他一些显示他们进度的东西。
  2. 计划游戏
    计划是持续的、循序渐进的。每2周,开发人员就为下2周估算候选特性的成本,而客户则根据成本和商务价值来选择要实现的特性。
  3. 客户测试
    作为选择每个所期望的特性的一部分,客户可以根据脚本语言来定义出自动验收测试来表明该特性可以工作。
  4. 简单设计
    团队保持设计恰好和当前的系统功能相匹配。它通过了所有的测试,不包含任何重复,表达出了编写者想表达的所有东西,并且包含尽可能少的代码。
  5. 结对编程
    所有的产品软件都是由两个程序员、并排坐在一起在同一台机器上构建的。
  6. 测试驱动开发
    编写单元测试是一个验证行为,更是一个设计行为。同样,它更是一种编写文档的行为。编写单元测试避免了相当数量的反馈循环,尤其是功功能能验证方面的反馈循环。程序员以非常短的循环周期工作,他们先增加一个失败的测试,然后使之通过。
  7. 改进设计
    随时利用重构方法改进已经腐化的代码,保持代码尽可能的干净、具有表达力。
  8. 持续集成
    团队总是使系统完整地被集成。一个人拆入(Check in)后,其它所有人责任代码集成。
  9. 集体代码所有权
    任何结对的程序员都可以在任何时候改进任何代码。没有程序员对任何一个特定的模块或技术单独负责,每个人都可以参与任何其它方面的开发。
  10. 编码标准
    系统中所有的代码看起来就好像是一人单独编写的。
  11. 隐喻
    将整个系统联系在一起的全局视图的概念。它是系统的未来影像,是它使得所有单独模块的位置和外观变得明显直观。如果模块的外观与整个隐喻不符,那么你就知道该模块是错误的。
  12. 可持续的速度
    团队只有持久才有获胜的希望。他们以能够长期维持的速度努力工作,他们保存精力,把项目看作是马拉松长跑,而不是全速短跑。

文章摘要

  • 有些东西讲得简明扼要能够给人以智慧的启迪。
  • mentor 导师
    check 支票
    magic number (魔数)意指直接写在程序里面的具体的数值。
  • 人与人之间的交互是复杂的,并且其效果从来都难以预测,但却是工作中最为重要的方面。
  • 我们建议从使用小工具开始,先使用一个免费的系统直到能够证明该系统已经不再合适,再考虑大一点的系统。不要认为更大的、更好的工具可以自动的帮你做得更好。通常,它们造成的障碍要大于带来的帮助。
  • 如果文档和代码之间失去同步,那么文档就会变成庞大的、复杂的谎言,会造成重大的误导。
  • 对于团队来说,编写并维护一份系统原理和结构方面的文档将总是一个好主意,但是那份文档应该是短小的(Short)并且主题突出的(salient)。“短小”的意思就是说,最多有一二十页。“主题突出的”意思是说,应该仅论述系统的高层结构和概括的设计原理。
    直到迫切需要并且意义重大时,才来编制文档。
  • 成功的项目需要有序、频繁的客户反馈。不是依赖于合同或者关于工作的描述,而是让软件的客户和开发团队密切的在一起工作,并尽量经常地提供反馈。
    成功的关键在于和客户之间真诚的协作,并且合同知道了这种协作,而不是试图去规定项目范围的细节和固定成本下的进度。
  • 计划不能考虑得过远...当团队增加了对于系统的认识,当客户增加了对需求的认识...计划将会遭到形态(shape)上的变化,而不仅仅是日期上的改变。
    较好的策略是:为下两周做详细的计划,为下三个月做粗略的计划,再以后就极为粗糙的计划。我们应该清楚的知道下两周要完成的任务,粗略的了解一下以后三个月要实现的需求。至于系统一年后将要做什么,有一个模糊的想法就行了。
  • 密歇根大学的一项研究表明,在“充满积极讨论的屋子(War room)”里工作,生产率非但不会降低,反而会成倍的提高。
  • 计划游戏(planning game)的本质是划分业务人员和开发人员之间的职责。业务人员(也就是客户)决定特性的重要性,开发人员决定实现一个特性所划分的代价。
  • 重构:就是在不改变代码行为的前提下,对其进行一系列的改造(Transformation),旨在改进系统结构的实践活动。
    重构就好比用餐后对厨房的清理工作。
  • 隐喻:它是将整个系统联系在一起的全局视图;它是系统的未来景象,是它使得所有单独模块的位置和外观变得明显直观。
  • 即使没有完成所有的用户素材,迭代也要在先前指定的日期结束。可以把一些为完成的素材放到下一迭代,在保证本次迭代功能完整的前提下。
  • 素材分解成开发任务,一个任务就是一个开发人员能够在4~16个小时之内能实现的一些功能。
  • 烈火验真金,逆境磨意志。
  • 首先编写测试可以迫使我们使用不同的观察点。我们必须从程序调用者的有利视角去观察我们将要编写的程序。... 为了是程序成为易于调用和可测试的,必须和周边环境解耦。这样,首先编写测试迫使我们接触软件中的耦合(Forces us to decouple teh sofeware)
  • 测试就像一套范例,它帮助其他程序员了解如何使用代码。这份文档是可以编译的、可运行的。它保持最新,它不会撒谎。
  • 为了使验收测试无需通过用户界面就能够获得对于业务规则的访问,就必须要以满足这个目的的方式来接触用户界面和业务规则之间的耦合。...验收测试可以促使你在大的方面做出优良的系统结构决策。
  • 测试最重要的好处就是它对于架构和设计的影响。为了使一个模块或者应用程序具有可测试性,必须要对它进行解耦合。越是具有可测试性,耦合关系就越弱。全面地考虑验收测试和单元测试的行为对于软件的结构具有深远的正面影响。
  • 画一幅图来探究一个想法是没有错的。然而,画一幅图后,不应该假定该图就是相关任务的最好设计。你会发现最好的设计是在你首先编写测试,一小步一小步前进时形成的。
  • 在按照我的理解方式审查了软件开发的生命周期后,我得出一个结论:实际上满足工程设计标准的唯一软件文档,就是源代码清单。... 源代码就是设计。
  • OCP是面向对象设计的核心所在,背后的主要机制是抽象和多态。
    LSP是使OCP成为可能的主要原则之一。
    DIP是面向对象设计的标志所在。
  • 依赖倒置原则:接口所有权的倒置。我们通常会认为工具库应该拥有他们的接口。但是当应用了DIP时,我们发现往往是客户拥有抽象的接口,而他们的服务者则从这些抽象接口派生。
    (客户定义了需要什么,服务提供者按照这个要求来提供服务)

  • 事实上,包的依赖关系图和描绘应用程序的功能之间几乎没有关系。相反,它们是应用程序可构建性的映射图。...包的依赖关系结构是和系统的逻辑设计一起增长和演化的。
  • 用例:描述了用户所期望的系统行为。...用例图是用来进行人与人之间的交流的,主要是用于分析师和干系人之间的交流。它们有助于按照不同类型的系统来组织系统的功能。
  • Martin文档第一定律:知道迫切需要并且意义重大时,才来编制文档。
  • 领域模型(Domain)是一组图,这些图有助于定于出现在用例中的术语。这些图显示了问题中关键对象以及它们之间的关系。...领域模型是一种描述工具,用来帮助人们记录它们的决策以及相关之间的交流是非常重要的。领域模型中的对象未必对应面向对象的设计,这样的对应也没有多大的价值。...领域模型里面的类是问题领域中的概念元素,和软件类没有直接关系。...我们使用它们来和用户进行交流的,而不是为了说明软件结构。...两者是不同的概念层次
  • 通过创建一个领域模型,我们更好的理解了手边的问题。这种更好的理解有助于我们对用例进行改善和补充。这两者之间的这种迭代是自然的,也是必要的。
  • 架构指的是构成应用程序的骨架(Skeleton)的软件结构。
  • 有些东西讲得简明扼要能够给人以智慧的启迪