使用简单的 JDepend

JDepend 是方便的帮助评定架构违背的工具之一。经过几年的发展, 该开源工具能够很好地与 Ant 和 Maven 集成; 此外,它对大量的 Java™API 提供支持,具有更细化的交互性。但是, 如同我已经指出的,它生成的报告本质上是被动的。根据您实际运行(并查看)它们的频率, 架构违背可能直到很难矫正的时候才会被发觉。
传入耦合(Afferent coupling)与传出耦合(Efferent coupling)

JDepend 中,传入耦合表示一些包的数量,这些包依赖某个经过分析的包。比如说,如果您正在使用日志框架或一个象 Struts 一样的 Web 框架, 您会希望这些包具有高的传入耦合, 因为整个代码库的很多包都依靠这些框架。 某包的传出耦合与传入耦合相反,是指某个经过分析的包所依赖的其他包的数量,也是说,它具有的依赖包的数量。

断言架构

要主动判定架构变动是否恰当,实际上是研究某特定包的耦合。事实上,通过对软件架构内关键包的传入和传出耦合进行监控, 以及观察预期值的偏离,您能轻松地发现错误的修改。

举例而言,在图 2 所示的修改中, data层的新传出耦合现在大于 0,因为该层目前要 直接与 business 层通信。当然,这种耦合是通过 data层中一个简单的 import(及对引用类的使用)而引入的。幸运的是,很容易通过 JUnit、JDepend 的 API 发现此类问题,还有,构建时需要一些技巧。

事实证明,在构建(如 Ant 或 Maven)上下文中,您能够运行使用 JDepend API 的 JUnit 测试主动辨别耦合值的变化; 此外,如果这些变化不正确,您可以使构建失败。 这实现了主动性,不是吗?

第一步是创建一个 JUnit 测试并且对 JDepend 做相应配置,如清单 2 所示:

清单 2. 在 JUnit 中设置 JDepend

import junit.framework.TestCase;
import jdepend.framework.JavaPackage;
import jdepend.framework.JDepend;

public class ArchitecturalRulesTest extends TestCase {
  private static final String DIRECTORY_TO_ANALYZE =
      "C:/dev/project-sandbox/brewery/classes";
  private JDepend jdepend;
  private String dataLayer = "com.beer.business.data";
  private String businessLayer = "com.beer.business.service";
  private Collection dataLayerViolations = new ArrayList<String>();

  public ArchitecturalRulesTest(String name) {
    super(name);
  }

  protected void setUp()throws IOException {
    jdepend = new JDepend();
    jdepend.addDirectory(DIRECTORY_TO_ANALYZE);
    // Calling the businessLayer from the dataLayer is a violation
    dataLayerViolations.add(businessLayer);
  }


清单 2 很长,我们总结以下几个要点:

    需要两个 JDepend 类:jdepend.framework.JavaPackage和 jdepend.framework.JDepend。

    待分析的源类位置由 DIRECTORY_TO_ANALYZE常量定义。JDepend 通过 调用 JDepend.addDirectory扫描该目录,该操作通过一个 fixture 完成(即 setUp()方法)。

    要分析的包由 “Layer” String定义。

    dataLayerViolations Collection添加了 businessLayer String(表示一个包)来指明 这是对期望架构的违背。

按照这四个要点,我已经有效地设置了 JDepend,以针对特定代码库发挥其魔力。 现在,我要设定一些精确的逻辑以说明耦合值的变化。

清单 3 中的 testDataLayer()测试用例是架构断言的核心。 该方法可判定是否存在对 dataLayer的任何违背 —— 如果 isLayeringValid()方法(在下面的 清单 4中定义)返回 false,测试用例 被认为失败,也意味着必然存在一处架构违背。

清单 3. 使用 JDepend 测试架构违背

 public void testDataLayer() {
  if (!isLayeringValid(dataLayer, dataLayerViolations)) {
    fail("Dependency Constraint failed in Data Layer");
  }
 }


清单 3 中测试用例所调用的方法如清单 4 所示:

清单 4. 循环查找每个包的传入耦合

private boolean isLayeringValid(String layer, Collection rules) {
 boolean rulesCorrect = true;
 Collection packages = jdepend.analyze();
 Iterator itor = packages.iterator();
 JavaPackage jPackage = null;
 String analyzedPackageName = null;
 while (itor.hasNext()) {
  jPackage = (JavaPackage) itor.next();
  analyzedPackageName = jPackage.getName();
  Iterator afferentItor = jPackage.getAfferents().iterator();
  String afferentPackageName = null;
  while (afferentItor.hasNext()) {
   JavaPackage afferentPackage = (JavaPackage) afferentItor.next();
   afferentPackageName = afferentPackage.getName();
  }
  rulesCorrect = isEfferentsValid(layer, rules, rulesCorrect, jPackage, analyzedPackageName);
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
 }
 return rulesCorrect;
}