'ArchUnit Rule to prevent access to derived classes?

A colleague did some refactoring and moved a method to a superclass to be able to reuse it from an other child-class, too. The IDE handled that quickly and without any complains. That method, however, internally referred to a constant which was not moved with the method, i.e. the parent class thus now referred to a constant in one of its child-classes which of course is a no-no!

How can I phrase an ArchUnit rule that prevents such references from parent to members/methods/local classes and enums/etc. in child-classes (or in general: anything in classes further down the hierarchy)?



Solution 1:[1]

For direct dependencies, you can use the following custom ArchCondition:

@ArchTest
ArchRule rule = noClasses().should(new ArchCondition<JavaClass>("depend on their children") {
    @Override
    public void check(JavaClass parentClass, ConditionEvents events) {
        parentClass.getDirectDependenciesFromSelf().stream()
            .filter(dependency -> dependency.getTargetClass() != parentClass
                    && dependency.getTargetClass().isAssignableTo(parentClass.getName()))
            .forEach(dependency -> events.add(satisfied(dependency, dependency.getDescription())));
    }
});

It can easily be adapted to also catch transitive dependencies such as in this example:

class Parent { 
    Friend friend; 
}
class Friend { 
    Child child; 
}
class Child extends Parent { 
}

You can basically replace getDirectDependenciesFromSelf with getTransitiveDependenciesFromSelf:

@ArchTest
ArchRule rule = noClasses().should(new ArchCondition<JavaClass>("depend on their children") {
    @Override
    public void check(JavaClass parentClass, ConditionEvents events) {
        parentClass.getTransitiveDependenciesFromSelf().stream()
            .filter(dependency -> dependency.getTargetClass() != parentClass
                    && dependency.getTargetClass().isAssignableTo(parentClass.getName()))
            .forEach(dependency -> events.add(satisfied(dependency, parentClass.getName()
                    + " (transitively) depends on its child: " + dependency.getDescription())));
    }
});

FYI: I'm using the following static imports:

import static com.tngtech.archunit.lang.SimpleConditionEvent.satisfied;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Manfred