让开发自动化: 除掉构建脚本中的气味

发表于:2008-04-03来源:作者:点击数: 标签:自动化
您把多少时间花在维护项目构建脚本上?也许远远超出您预期的或者可以忍受的时间。其实大可不必遭受如此痛苦的经历。在这一期的 让开发自动化 中,开发自动化专家 Paul Duvall 将演示如何改进很多常见的妨碍团队创建一致的、可重复的、可维护的构建的实践。
您把多少时间花在维护项目构建脚本上?也许远远超出您预期的或者可以忍受的时间。其实大可不必遭受如此痛苦的经历。在这一期的让开发自动化 中,开发自动化专家 Paul Duvall 将演示如何改进很多常见的妨碍团队创建一致的、可重复的、可维护的构建的实践。

当描述代码之类的东西时,我不喜欢 “气味(smell)”这个词。因为用拟人的手法来谈论比特和字节往往令人觉得很怪异。并不是说“气味”这个词不能准确地反映出某种表明代码可能有错误的症状,只是我觉得这样听起来很滑稽。然而,我依然选择再次用这种令人厌烦的方式来描述软件构建,坦白说,这是因为这些年我见过的很多构建脚本都散发着难闻的气味

在创建构建脚本时,即使是伟大的程序员也常常会遇到困难。就好像最近才学会如何编写程序性 代码似的 —— 他们还会编写庞大的单块构建文件、通过复制-粘贴编写代码、对属性进行硬编码等等。我总是很想知道为什么会这样。也许是因为构建脚本没有被编译成客户最终会使用的东西?然而我们都知道,要创建客户最终使用的代码,构建脚本是中心,如果那些脚本败絮其中,那么要想有效地创建 软件,就需要克服重重挑战。

幸运的是,您可以轻松地在构建(不管是 Ant、Maven 还是定制的)之上部署一些实践,它们虽然可以帮助您创建一致的、可重复的、可维护的构建,但其过程会很长。学习如何创建更好的构建脚本的一种有效的方法是搞清楚哪些事情不要 去做,理解其中的道理,然后看看做事的正确 方法。在本文中,我将详细论述您应该避免的 9 种最常见的构建中的气味,为什么应该避免它们,以及如何修复它们:

  • 惟 IDE 的构建
  • 复制-粘贴式的编写脚本方法
  • 冗长的目标
  • 庞大的构建文件
  • 没有清理干净
  • 硬编码的值
  • 测试失败还能构建成功
  • 魔力机
  • 格式的缺失
关于本系列
作为一名开发人员,我们的工作就是为用户将过程自动化。然而,我们当中有很多人却忽视了将我们自己的开发过程自动化的机会。为此,我编写了 让开发自动化 这个系列的文章,专门探索软件开发过程自动化的实际应用,并教您何时以及如何成功地应用自动化。

这里无意给出完整的列表,不过这份列表的确代表了近年来我读过的和写过的构建脚本中,我遇到的较为常见的一些气味。有些工具,例如 Maven,是为处理与构建有关的很多管道而设计的,它们可以帮助减轻部分气味。但是无论使用什么工具,还是有很多问题会发生。

避免惟 IDE 的构建

惟 IDE(IDE-only)的构建是指只能通过开发人员的 IDE 执行的构建,不幸的是,这似乎在构建中很常见。惟 IDE 的构建的问题是,它助长了 “在我的计算机上能运行”问题,即软件在开发人员的环境中可以运行,但是在任何其他人的环境中就不能运行。而且,由于惟 IDE 构建自动化程度不是很高,因而为集成到持续集成(Continuous Integration)环境带来极大的挑战。实际上,没有人为的干预,惟 IDE 常常无法自动化。

我们要清楚:使用 IDE 来执行构建并没有错,但是 IDE 不应该成为构建软件的惟一环境。特别是,一个完全用脚本编写的构建,可以使开发团队能够使用多种 IDE,因为只存在从 IDE 到构建的依赖性,而不存在相反方向的依赖性,如图 1 所示:


图 1. IDE 与构建的依赖关系
IDE 与构建的依赖关系

惟 IDE 的构建有碍自动化,清除的惟一方法就是创建可编写脚本的构建。有足够的文档和太多的书籍可以为您提供指导(见 参考资料),而像 Maven 之类的项目也为从头开始定义构建提供了极大的方便。不管采用何种方法,都是选择一种构建平台,然后尽快地让项目成为可编写脚本的。





回页首


复制-粘贴就像廉价的香水

复制代码是软件项目当中一个常见的问题。实际上,甚至很多流行的开放源码项目都存在 20% 到 30% 的复制代码。代码复制令软件程序更难于维护,同理,构建脚本中的复制代码也存在这样的问题。例如,想象一下,假设您需要通过 Ant 的 fileset 类型引用特定的文件,如清单 1 所示:


清单 1. 复制-粘贴 Ant 脚本
<fileset dir="./brewery/src" >  <include name="**/*.java"/>  <exclude name="**/*.groovy"/></fileset>

如果需要在其他地方引用这组文件,例如为了编译、检查或生成文档,那么最终您可能会在多个地方使用相同的 fileset。如果在将来某个时候,您需要对那个 fileset 做出修改(比如说排除 .groovy 文件),那么最终可能需要在多个地方做更改。显然,这不是可维护的解决方案。然而,要除掉这股气味其实很简单。

如清单 2 所示,通过 Ant 的 patternset 类型可以引用一个逻辑名称,以表示所需要的文件。那么,当需要向 fileset 添加(或排除)文件时,只需更改一次


清单 2. 复制-粘贴 Ant 脚本
<patternset id="sources.pattern">  <include name="**/*.java"/>  <exclude name="**/*.groovy"/></patternset>...<fileset dir="./brewery/src">  <patternset refid="sources.pattern"/></fileset>

对于精通面向对象编程的人来说,这种修复方法看上去很熟悉:既定的惯例不是在不同的类中一次又一次地定义相同的逻辑,而是将那个逻辑放在一个方法中,在不同地方都可以调用这个方法。于是,这个方法成为惟一的维护点,从而可以限制错误级联并可以鼓励重用。

原文转自:http://www.ltesting.net