如清单 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>

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

  不要掺入冗长目标的气味

  Martin Fowler 在他撰写的 Refactoring 这本书中,对代码中存在冗长方法的气味这个问题做了精妙的描述 —— 过程越长,越难理解。实际上,冗长方法终会担负太多的责任。当谈到构建时, 冗长目标这种构建气味是指更难于理解和维护的脚本。清单 3 展示了一个相当冗长的目标:

  清单 3. 冗长目标

双击代码全选
 
<target name="run-tests">
 <mkdir dir="${classes.dir}"/>
 <javac destdir="${classes.dir}" debug="true">
  <src path="${src.dir}" />
  <classpath refid="project.class.path"/>
 </javac>
 <javac destdir="${classes.dir}" debug="true">
  <src path="${test.unit.dir}"/>
  <classpath refid="test.class.path"/>
 </javac>
 <mkdir dir="${logs.junit.dir}" />
 <junit fork="yes" haltonfailure="true" dir="${basedir}" printsummary="yes">
  <classpath refid="test.class.path" />
  <classpath refid="project.class.path"/>
  <formatter type="plain" usefile="true" />
  <formatter type="xml" usefile="true" />
  <batchtest fork="yes" todir="${logs.junit.dir}">
   <fileset dir="${test.unit.dir}">
    <patternset refid="test.sources.pattern"/>
   </fileset>
  </batchtest>
 </junit>  
 <mkdir dir="${reports.junit.dir}" />
 <junitreport todir="${reports.junit.dir}">
  <fileset dir="${logs.junit.dir}">
   <include name="TEST-*.xml" />
   <include name="TEST-*.txt" />
  </fileset>
  <report format="frames" todir="${reports.junit.dir}" />
 </junitreport>
</target>

  这个冗长的目标(相信我,我还见过冗长得多的目标)要执行四个不同的过程:编译源代码、编译测试、运行 JUnit 测试和创建一个 JUnitReport。要担负的责任已经够多了,更不用说将所有 XML 放在一个地方所增加的相关的复杂性。实际上,这个目标可以拆分成四个不同的、逻辑上的目标,如清单 4 所示:

  清单 4. 提取目标

双击代码全选

<target name="compile-src">
  <mkdir dir="${classes.dir}"/>
  <javac destdir="${classes.dir}" debug="true">
   <src path="${src.dir}" />
   <classpath refid="project.class.path"/>
  </javac>
 </target>
 
 <target name="compile-tests">
  <mkdir dir="${classes.dir}"/>
  <javac destdir="${classes.dir}" debug="true">
   <src path="${test.unit.dir}"/>
   <classpath refid="test.class.path"/>
  </javac>
 </target>
 <target name="run-tests" depends="compile-src,compile-tests">
  <mkdir dir="${logs.junit.dir}" />
  <junit fork="yes" haltonfailure="true" dir="${basedir}" printsummary="yes">
   <classpath refid="test.class.path" />
   <classpath refid="project.class.path"/>
   <formatter type="plain" usefile="true" />
   <formatter type="xml" usefile="true" />
   <batchtest fork="yes" todir="${logs.junit.dir}">
    <fileset dir="${test.unit.dir}">
     <patternset refid="test.sources.pattern"/>
    </fileset>
   </batchtest>
  </junit>  
 </target>
 <target name="run-test-report" depends="compile-src,compile-tests,run-tests">
   <mkdir dir="${reports.junit.dir}" />
   <junitreport todir="${reports.junit.dir}">
   <fileset dir="${logs.junit.dir}">
    <include name="TEST-*.xml" />
    <include name="TEST-*.txt" />
   </fileset>
   <report format="frames" todir="${reports.junit.dir}" />
  </junitreport>
 </target>