敏捷开发修炼之道 (三)敏捷反馈、敏捷编码

第5章:敏捷反馈

实践是绝对必需的。我们会遵循这原则,确保你明确知道项目的正确状态,而不是主观臆测。

经常的监督 - 频繁反馈以确保代码不会变坏。如何让守护天使来监督你的代码。

防止你设计的接口或API变得笨重和难用,这时,你就要先用它再实现它

可以看到为什么不同环境,就有不同问题

自动验收测试来保证代码是正确的。

甘特图、PERT图或者日历工具。其实,你想要的是能度量真实的进度

需要再倾听用户的声音

守护天使

编写能产生反馈的代码。

       敏捷式的单元测试正是采取了相同、相似的过程,并且还让其更上一层楼。不用扔掉桩程序,你把它保存下来,还要让其可以自动化地持续运行。

       用代码来测试变量的具体值(以及跟踪运行了多少个测试)。

清楚自己要测试的内容:

  1. 确保测试是可重复的。
  2. 测试你的边界条件。
  3. 不要放过任何一个失败的测试。

只要有了单元测试,就要让它们自动运行。

在后台架设一个构建机器,不断获得最新版本的源代码,然后编译代码,并运行单元测试,如果有任何错误它会让你及时知道。

结合本地单元测试,运行每个编译,构建机器不断编译和运行单元测试,这样你就拥有了一个守护天使。

如果你是一个新手,建议阅读《单元测试之道》。如果想要自动化地连接单元测试,可以阅读《项目自动化之道》。

开始单元测试的理由:

  1. 单元测试能及时提供反馈。
  2. 单元测试让你的代码更加健壮。
  3. 单元测试是有用的设计工具。
  4. 单元测试是让你自信的后台。
  5. 单元测试是解决问题时的探测器。
  6. 单元测试是可信的文档。
  7. 单元测试是学习工具。

使用自动化的单元测试。好的单元测试能够为你的代码问题提供及时的警报。如果没有到位的单元测试,不要进行任何设计和代码修改。

平衡的艺术

  1. 单元测试是优质股,值得投资。但一些简单的属性访问方法或者价值不大的方法,是不值得花费时间进行测试的。
  2. 人们不编写单元测试的很多借口都是因为代码中的设计缺陷。通常,抗议越强烈,就说明设计越糟糕。
  3. 单元测试只有在达到一定测试覆盖率的时候,才能真正地发挥作用。你可以使用一些测试覆盖率工具,大致了解自己的单元测试的覆盖情况。
  4. 不是测试越多质量就会越高,测试必须要有效。如果测试无法发现任何问题,也许它们就是没有测试对路。

先用它再实现它

       编码之前,先写测试。

       什么是成功地实现特定功能的最低成本。总之,程序员很容易走向另一个极端——一些不必要的过于复杂的事情——测试优先会帮助我们,防止我们走偏。

       好的设计不意味更多的类。

TDD有机会让你编写代码之前(或者至少在深入到实现之前),可以深思熟虑将如何用它。这会迫使你去思考它的可用性和便利性,并让你的设计更加注重实效。

当然,设计不是在开始编码的时候就结束了。你需要在它的生命周期中持续地添加测试,添加代码,并重新设计代码。

先用它再实现它。将TDD作为设计工具,它会为你带来更简单更有实效的设计。

平衡的艺术

  1. 不要把测试优先和提交代码之前的测试等同起来。测试先行可以帮助你改进设计,但是你还是需要在提交代码之前做测试。
  2. 任何一个设计都可以被改进。
  3. 你在验证一个想法或者设计一个原型的时候,单元测试也许并不适合。但是,万一这些代码不幸仓促演变成了一个真正的系统,就必须要为它们添加测试(但是最好能重新开始设计系统)。
  4. 单纯的单元测试无法保证好的设计,但它们会对设计有帮助,会让设计更加简单。

不同环境,就有不同问题

每次在修改或者重构代码的时候,在提交代码之前,你会运行测试用例。那么现在所要做的就是在各种支持的平台和环境中运行这些测试用例。

使用自动化会节省时间。

       持续集成。

       用一个持续集成工具,周期性地从源代码控制系统中取得代码,并运行代码。

       要在多个平台上测试,你只要为每个平台设置持续集成系统就行了。

       不同环境,就有不同问题。使用持续集成工具,在每一种支持的平台和环境中运行单元测试。要积极地寻找问题,而不是等问题来找你。

平衡的艺术

  1. 硬件比开发人员的时间便宜。但如果你有很多配置,要支持大量的平台,可以选择哪些平台需要内部测试。
  2. 只因为不同的栈层顺序、不同的单词大小写等, 就能发现很多平台上的bug。因此,即使运行用Solaris的客户比用Linux的少很多,你仍然要在两个系统上都进行测试。
  3. 你不希望因为一个错误而收到5次通知轰炸(这就像是双重征税,会导致电子邮件疲劳症)。可以设置一个主构建平台或者配置,降低其他构建服务器的运行频率,这样在它失败的时候,你就有足够的时间来修复主构建平台。或者汇总所有错误报告信息到一个地方,进行统一处理。

自动验收测试

有一个办法可以使验收测试不同于单元测试。你应该让用户在不必学习编码的情况下,根据自己的需要进行添加、更新和修改数据。你有很多方法来实现它。

       FIT,即集成测试框架,它很实用,可以更容易地使用HTML表格定义测试用例,并比较测试结果数据。

       使用FIT,客户可以定义带有新功能的使用样本。客户、测试人员和开发人员(根据样本)都可以创建表格,为代码描述可能的输入和输出值。开发人员会参照带有正开发的代码结果的FIT表格中的样本编写测试代码。测试结果成功或者失败,都会显示在HTML页面中,用户可以很方便地查阅。

为核心的业务逻辑创建测试。让你的客户单独验证这些测试,要让它们像一般的测试一样可以自动运行。

平衡的艺术

  1. 不是所有客户都能给你提供正确的数据。如果他们已经有了正确的数据,就根本不需要新系统了。
  2. 你也许会在旧系统(也许是电脑系绕,也许是人工系统)中发现以前根本不知道的bug,或者以前不存在的真正问题。
  3. 使用客户的业务逻辑,但是不要陷入无边无际的文档写作之中。

度量真实的进度

不应该去计算工作量完成的百分比,而应该测定还剩下多少工作量没有完成。

花费的时间很可能要比最初估计时间长。没有关系,我们希望这能作为下一次的参考。在为下一个任务估计工作量时,可以根据这次经验调整评估。

如果能一直让下一步工作是可见的,会有助于进度度量。最好的做法就是使用待办事项。

        待办事项就是等待完成的任务列表。

度量剩下的工作量。不要用不恰当的度量来欺骗自己或者团队。要评估那些需要完成的待办事项。

平衡的艺术

  1. 6分钟作为一个时间单位,它的粒度实在太细了,这不是敏捷的做法。
  2. 一周或者一个月的时间单元,它的粒度太粗了,这也不是敏捷的做法。
  3. 关注功能,而不是日程表。
  4. 如果你在一个项目中花费了很多时间来了解你所花费的时间,而没有足够的时间进行工作,那么你在了解你所花费的时间上花费的时间就太多了。听懂了吗?
  5. 一周工作40个小时,不是说你就有40个小时的编码时间。你需要减去会议、电话、电子邮件以及其他相关活动的时间。

倾听用户的声音

当出了错误,你要尽可能地提供详细信息。

       每一个抱怨的背后都隐藏了一个事实。找出真相修复真正的问题。

平衡的艺术

  1. 没有愚蠢的用户。
  2. 只有愚蠢、自大的开发人员。
  3. “它就是这样的。”这不是一个好的答案。
  4. 如果代码问题解决不了,也许可以考虑通过修改文档或者培训来弥补。
  5. 你的用户有可能会阅读所有的文档,记住其中的所有内容。但也可能不会。

第6章:敏捷编码

在开发过程中便细心“照看”代码。在编写代码时,每天付出一点小的努力,可以避免代码“腐烂”,并且保证应用程序不至变得难以理解和维护。

       都易于理解、扩展和维护。

       代码要清晰地表达意图。

       用代码沟通。

       动态评估取舍。   

       增量式编程。

       编写内聚的代码。

告知,不要询问。

       通过设计能够根据契约进行替换的系统。

代码要清晰地表达意图

更注重可读性,代码清晰程度的优先级应该排在执行效率之前。

代码必须明确说出你的意图,而且必须富有表达力。

要编写清晰的而不是讨巧的代码。向代码阅读者明确表明你的意图。可读性差的代码一点都不聪明。

平衡的艺术

  1. 有意图的编程并不是意味着创建更多的类或者类型。这不是进行过分抽象的理由。
  2. 使用符合当时情形的耦合。例如,通过散列表进行松耦合,这种方式适用于在实际状况中就是松耦合的组件。不要使用散列表存储紧密耦合的组件,因为这样没有明确表示出你的意图。

用代码沟通

利用代码本身;利用注释来沟通代码之外的问题。

要尽量避免使用神秘的变量名。

注释可用来为读者指定一条正确的代码访问路线图。

可能要说明以下信息:

  1. 目的:为什么需要这个方法?
  2. 需求(前置条件):方法需要什么样的输入,对象必须处于何种状态,才能让这个方法工作?
  3. 承诺(后置条件):方法成功执行后,对象现在处于什么状态,有哪些返回值?
  4. 异常:可能会发生什么样的问题?会抛出什么样的异常?

用注释沟通。使用细心选择的、有意义的命名。用注释描述代码意图和约束。注释不能替代优秀的代码。

平衡的艺术

  1. Pascal定理的创始人Blaise PascarR曾说,他总是没有时间写短信,所以只好写长信。请花时间去写简明扼要的注释吧。
  2. 在代码可以传递意图的地方不要使用注释。
  3. 解释代码做了什么的注释用处不那么大。相反,注释要说明为什么会这样写代码。
  4. 当重写方法时,保留描述原有方法意图和约束的注释。

动态评估取舍

要想让应用成功,降低开发成本与缩短上市时间,二者的影响同样重要。

       没有适宜所有状况的最佳解决方案。你必须对手上的问题进行评估,并选出最合适的解决方案。

       动态评估权衡。考虑性能、便利性、生产力、成本和上市时间。

平衡的艺术

  1. 如果现在投入额外的资源和精力,是为了将来可能得到的好处,要确认投入一定要得到回报(大部分情况下,是不会有回报的)。
  2. 真正的高性能系统,从一开始设计时就在向这个方向努力。
  3. 过早的优化是万恶之源。
  4. 过去用过的解决方案对当前的问题可能适用,也可能不适用。不要事先预设结论,先看看现在是什么状况。

增量式编程

采用增量式的编程方式。增量式编程可以精炼并结构化你的代码。代码被复杂化、变成一团乱麻的几率减少了。所开发的代码基于即时的反馈,这些反馈来自以小步幅方式编写代码和测试的过程。

采取增量式编程和测试,会倾向于创建更小的方法和更具内聚性的类。

在很短的编辑/构建/测试循环中编写代码。

保持简单

简单不是简陋。

优雅的代码第一眼看上去,就知道它的用处,而且很简洁。

平衡的艺术

  1. 代码几乎总是可以得到进一步精炼,但是到了某个点之后,再做改进就不会带来任何实质性的好处了。这时开发人员就该停下来,去做其他方面的工作了。
  2. 要将目标牢记在心:简单、可读性高的代码。强行让代码变得优雅与过早优化类似,同样会产生恶劣的影响。
  3. 当然,简单的解决方案必须要满足功能需求。为了简单而在功能上妥协,这就是过分简化了。
  4. 太过简洁不等于简单,那样无法达到沟通的目的。
  5. 一个人认为简单的东西,可能对另一个人就意味着复杂。

编写内聚的代码

内聚性用来评估一个组件(包、模块或配件)中成员的功能相关性。内聚程度高,表明各个成员共同完成了一个功能特性或是一组功能特性。

类也要遵循内聚性。

内聚性会影响一个组件的可重用性。

让类的功能尽量集中,让组件尽量小。要避免创建很大的类或组件,也不要创建无所不包的大杂烩类。

平衡的艺术

  1. 有可能会把一些东西拆分成很多微小的部分,而使其失去了实用价值。当你需要一只袜子的时候,一盒棉线不能带给你任何帮助。
  2. 具有良好内聚性的代码,可能会根据需求的变化,而成比例地进行变更。考虑一下,实现一个简单的功能变化需要变更多少代码。

告知,不要询问

将命令与查询分离开来。

告知,不要询问。不要抢别的对象或是组件的工作。告诉它做什么,然后盯着你自己的职责就好了。

根据契约进行替换

Liskov替换原则[Lis88]告诉我们:任何继承后得到的派生类对象,必须可以替换任何被使用的基类对象,而且使用者不必知道任何差异。

那么继承和委托分别在什么时候使用呢?

  1. 如果新类可以替换已有的类,并且它们之间的关系可以通过is-a来描述,就要使用继承。
  2. 如果新类只是使用已有的类,并且两者之间的关系可以描述为has-a或是uses-a,就使用委托吧。

平衡的艺术

  1. 相对继承来说,委托更加灵活,适应力也更强。
  2. 继承不是魔鬼,只是长久以来被大家误解了。
  3. 如果你不确定一个接口做出了什么样的承诺,或是有什么样的需求,那就很难提供一个对其有意义的实现了。

你可能感兴趣的:(守护天使,自动验收测试,倾听用户的声音,动态评估取舍,增量式编程)