【问题标题】:Archunit test: Classes can access only interfaces they implementArchunit 测试:类只能访问它们实现的接口
【发布时间】:2021-07-14 11:00:07
【问题描述】:

我有两个包,一个用于接口,一个用于实现。我想将类的访问限制在它们实现的接口上。 说:

package com.interfaces
interface AI {
}

package com.interfaces
interface BI {
}

package com.impls
class A implements AI{
}

package com.impls
class B implements BI{
}

我已经有一个规则来确保 com.impls 中的每个类都实现了来自 com.interfaces 的接口:

ArchRuleDefinition.classes()
        .that().resideInAPackage("com.impls..")
.should().implement(JavaClass.Predicates.resideInAPackage("com.interfaces.."))

现在我需要确保一个类只访问它实现的接口。如果有帮助,我也可以依赖上面示例中的命名约定。我正在寻找类似的东西:

ArchRuleDefinition.classes()
        .that().resideInAPackage("com.impls..")
.should().implement(JavaClass.Predicates.resideInAPackage("com.interfaces.."))
.andShould().onlyHaveDependentClassesThat().haveSimpleNameContaining("THE NAME OF THE IMPLEMENTING CLASS")

我们将在任何地方使用接口,并注入实现(Spring Boot)。但是不允许实现访问它没有实现的接口。所以允许 AI 访问 A 但不允许访问 B。允许 BI 访问 B 但不允许访问 A!另一方面,接口不限于实现。它们将在其他包中使用。

有没有办法重用规则第一部分中的名称或在 ArchUnit 中以另一种方式限制访问?

【问题讨论】:

  • andShould().onlyHaveDependentClassesThat().haveSimpleNameContaining("THE NAME OF THE IMPLEMENTING CLASS") 是否意味着 "A 应该只具有包含 A" 等的简单名称的依赖类?
  • @Manfred 不,我在问题中添加了详细信息(thnx)。
  • 对不起,我仍然对措辞感到困惑:当您说允许 AI 访问 A 时,您想到的是哪种访问?对于 ArchUnit,依赖关系(由于实现接口)将从 AAI,反之亦然。所以我仍然认为my answer 做你真正想要的。 ??????
  • @Manfred 我认为我们非常接近。关键是,我必须让实现访问接口包。我只是试图限制这种访问,以确保一个类只访问它实现的接口,而不是接口包中的任何其他接口。

标签: archunit


【解决方案1】:

如果您想防止A 本身以外的任何类依赖于A 等,您可以使用

ArchRuleDefinition.classes()
  .that().resideInAPackage("com.impls..")
  .should().implement(JavaClass.Predicates.resideInAPackage("com.interfaces.."))
  .andShould(new ArchCondition<JavaClass>("not have other classes depending on them") {
    @Override
    public void check(JavaClass javaClass, ConditionEvents events) {
      List<JavaClass> otherClassesThatDependOnJavaClass = javaClass.getDirectDependenciesToSelf().stream()
              .map(Dependency::getOriginClass)
              .filter(origin -> !origin.equals(javaClass))  // TODO: inner classes?
              .collect(toList());
      boolean satisfied = otherClassesThatDependOnJavaClass.isEmpty();
      String message = satisfied
              ? "no other classes depend on " + javaClass.getName()
              : otherClassesThatDependOnJavaClass.stream().map(JavaClass::getName)
                .collect(joining(", ", "other classes depend on " + javaClass.getName() + ": ", ""));
      events.add(new SimpleConditionEvent(javaClass, satisfied, message));
    }
  });

如果这对您来说是一个用例,您可能希望调整过滤器(请参阅TODO)以考虑内部类。

您要求的.filter(origin -&gt; !origin.getSimpleName().contains(javaClass.getSimpleName())) 很容易实现,但可能过于宽松。

【讨论】:

    猜你喜欢
    • 2020-09-18
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 2021-12-24
    • 2015-07-28
    • 1970-01-01
    相关资源
    最近更新 更多