Modernized Pattern implementation with CDI, Part 2: The maximal decoupled “Virtual Chain Of Responsibility”

In this part I will show you a modernized, CDI-based version of the good old “Chain Of Responsibility” (CoR) pattern. As you will see, it is a perfect weapon if you want to design a system and write code that follows Uncle Bob’s “SOLID Principles”, especially the Open/Closed Principle, which is often one of the hardest to achieve.

The new design is based on an Interceptor that controls and performs the calls to the Chain Handlers, which have to be annotated with appropriate CDI-Qualifiers. The key difference to the usual Chain-Pattern is, that the chain handlers are not physically connected, i.e. a handler doesn’t know about any successor. Instead, the handlers are “virtually” connected by sharing a custom, Usecase-specific CDI-Qualifier, thus I would prefer to call this modernized version a “Virtual Chain Of Responsibility“.

Compared to the modernized factory-pattern, which I had shown in part 1 (see either the original post or the dzone-post), this CoR-design leads to an even more decoupled system, because you do not need to implement a factory anymore. Instead, it gets replaced by the generically implemented “ChainOfResponsibilityControllerInterceptor”. For an opensource implementation of this concept, see the section about the “magic4j-cdi-utils” library at the end of this article or visit magic4j.org.

How does it basically work?

The following UML-class diagram visualizes the key parts of the rewritten example from the part 1 blog about the factory-method pattern. If you are interested in the detailed design of the XML-Structure, then please find it in part 1.

Modernized "Chain Of Responsibility" Pattern - example classes

Modernized “Chain Of Responsibility” Pattern – example classes

Here you can see an Interface that is implemented by 3 classes. All annotations are visualized by stereotypes. Please note, that the Usecase-specific Qualifier-annotation must  be made at method level and not at class level (see the code snippets below for the details).

The first implementation on the left side is the so-called CoR-”Controller” to which the interceptor gets attached. This is also the class, that the client / caller needs to inject. The other two implementations are the specific Chain-Handlers: one for the validation of a Football-related MatchResult and one for a Tennis-related MatchResult. You might have many more implementation types in your real projects.

As soon as the client invokes the controllers’ validation-method, the interceptor can start it’s business:

First, the Controller-Interceptor detects all corresponding chain handlers by searching for beans, that have the @ChainOfResponsibilityHandler annotation at class level and the same custom Qualifier annotation as the Controller-method at method-level. Then it starts to call one after the other, until one “chain handler” finds itself responsible for the given input. That handler immediately performs the processing of the input data and signals to the controller, that no subsequent handler needs to be called anymore by returning a non-empty Java8-Optional. If an invoked handler finds itself not being responsible, it just  returns an empty Java8-Optional.

The Code-Snippets of the example

 


public interface OptionalMatchResultValidator {

    /**
     * Indicates if this implementation was responsible for the given
     * MatchResult (see below).
     *
     * @param teamMatchResult
     * @return an empty Optional if no validation was performed because this
     * concrete Validator-Implementation was not responsible for the given kind
     * of MatchResult. Otherwise an Instance of Response must be present in the
     * Optional.
     */
    Optional<Response> validateMatchResult(MatchResult teamMatchResult);
}

Listing 1: The example Business Interface for the Chain-Handlers and the Chain-Controller

Please note, that the interface method currently must return an Optional. The contract is, that if the returned Optional is empty the Interceptor must invoke the next chain handler. As soon as one Chain Handler finds itself responsible for the given input, it must return an Optional with an Instance. By the way, I personally find it convenient to return a “Response” Object that acts as a container for positive and negative results: It either contains the real outcome of the method or error messages in case of problems. The main reason for the Optional is that you can integrate the method into Java 8 call chains.


@ApplicationScoped
public class MatchResultValidatorChainController implements OptionalMatchResultValidator {

    @Override
    @ChainOfResponsibilityController
    @MatchResultValidator
    public Optional<Response> validateMatchResult(MatchResult teamMatchResult) {
        throw new IllegalStateException("Method-Body of a Chain-Controller should not have been invoked!");
    }
}

Listing 2: The Chain-Controller with the @ChainOfResponsibilityController Annotation

The Usecase-specific custom CDI-Qualifier is the @MatchResultValidator annotation. It needs to be present at method level. Secondly, you need to add the @ChainOfResponsibilityController-Annotation for applying the Interceptor to the method. This annotation is provided by the magic4j-cdi-utils library.

Please note, that the Interceptor doesn’t even call the method body of the intercepted Chain-Controller, because this would just lead to an ambiguity concerning the question which result the interceptor should provide to the client: Of course it must always return the result from the responsible chain handler and not the one from its own method body. That’s why this method throws an IllegalStateException if it is really called.


@ApplicationScoped
@ChainOfResponsibilityHandler
public class FootballOptionalMatchResultValidator implements OptionalMatchResultValidator {

    @Override
    @MatchResultValidator
    public Optional validateMatchResult(MatchResult teamMatchResult) {
        assertMatchResultNotNull(teamMatchResult);
        if (isFootballMatchResult(teamMatchResult)) {
            return doValidateFootballMatchResult(teamMatchResult);
        }
        return Optional.empty();
    }
...
}

Listing 3: The Chain-Handler for the Football-related MatchResult.

At class level, you need the @ChainOfResponsibilityHandler-Annotation which is also provided my the magic4-j-cdi-utils library. At method level, you need again the custom Usecase-specific CDI-Qualifier, here @MatchResultValidator. It is the task of each handler to detect, if the given input is addressed to him. In part 1, this detection was done centrally in the factory, which is a disadvantage compared to this modernized Chain-Pattern.


import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import javax.inject.Qualifier;

/**
 * @author stephan
 */
@Qualifier
@Retention(RUNTIME)
@Target({
    METHOD, FIELD, PARAMETER, TYPE
})
public @interface MatchResultValidator {
}

Listing 4: The custom Usecase-specific CDI-Qualifier for the example.

What’s the difference to the CDI-built-in Decorator-Pattern?

The CDI-built-in Decorator-Pattern is suitable, if you have “n” implementations of an interface and you want them all to be invoked in every case. As a side note: In contrast to the Decorator, where all actual decorators need to be listed in the beans.xml file, the magic4j-CoR-Interceptor detects all fitting Chain Handlers at runtime, which makes CoR also easier to use. You only need to add the ChainOfResponsibilityControllerInterceptor to the beans.xml file of the module that contains the CoR-Controller.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="annotated">
    <interceptors>
        <class>org.magic4j.cdiutils.chainofresponsibility.ChainOfResponsibilityControllerInterceptor</class>
    </interceptors>
</beans>

Why does the CoR-Design fit perfectly to the SOLID principles?

  1. Single Responsibility Principle: You can define one interface with one method per Usecase (example Usecase here “Validate MatchResult”) and have one implementation per kind of sports. This fits perfectly!
  2. Open/Closed Principle: Your Software should be open for extension but closed for modification: In our example: When a new type of sport is added, you do not have to change any existing code: no new else-branches in existing if-else-blocks and/or no new case-branches in existing switch-case-blocks. Instead you simply add a new implementation of the Usecase-specific interface. If it correctly annotated, the Interceptor will find it automatically at runtime. Each implementation can even be put into a separate module so that you do not even have to touch existing modules (this is the same as shown in the component diagram of the factory-method pattern in part1, which applies here as well).
  3. Liskov’s Substitution Principle: This principle does not primarily apply to our example because we assume, that we do not need an inheritance hierarchy that is deeper than two levels (interface and implementation). In case you have similar types of MatchResults, you would have to create common parent classes for those similar types in a common package or module. But this can lead quite fast to a violation of the Open-Closed-Principle, so you should examine carefully if it’s worth it. Imagine a third similar type of sport is added and it needs a slight modification in the base class, then you get a violation of the Open-Closed-Principle. In other words, creating common parent classed can lead to a clash between the Open-Closed- and the DRY-Principle which you have to balance reasonably from case to case.
  4. Interface Segregation Principle: It tells you to avoid big interfaces with many methods. This is already implicitly achieved, if you do it as proposed under item a).
  5. Dependency Inversion Principle: The Usecase-specific interface represents the abstraction that is required by this principle to decouple higher-level- from lower-level components.

Advantages to the “maximal decoupled parameterized factory method”-pattern from part 1?

My article about the modernized factory-method pattern resulted in an interesting discussion concerning the question if that factory is in fact so well decoupled as I explained it. The critics was, that the factory still has some kind of knowledge about the different implementations:

  1. It depends on specific CDI-Qualifiers per implementation type
  2. It has knowledge about the structure of the incoming XML because it needs to decide which injection point to use based on structural information in the XML.

This results also in the fact, that the factory needs to be modified e.g. when a new implementation type is added. Even though I still believe that this kind of factory still does a much better job in decoupling the high-level logic from the low-level implementations (as explained in detail in my replies to the comments) than usual factories, the Chain-Of-Responsibility as shown here does it even better. You only need one custom CDI-Qualifier per Usecase (instead of one per implementation) and there is no central place anymore, that needs knowledge about the structure of the input data. This knowledge has been split into the diverse chain handlers, where each one only needs to know about his kind of structure. Nevertheless you should be aware that you trade the explicit coding of the factory against some magic that happens under the covers of the “CoR-Interceptor”.

Available as new Feature in the magic4j-cdi-utils

I provided a reference implementation of this modernized Chain-Of-Responsibility pattern in my magic4j-cdi-utils library, version 1.1.0.0. The library is Opensource under the Apache 2 license. You can download it from the maven central repository via the following dependency:

<dependency>
    <groupId>org.magic4j</groupId>
    <artifactId>magic4j-cdi-utils</artifactId>
    <version>1.1.0.0</version>
</dependency>

2 thoughts on “Modernized Pattern implementation with CDI, Part 2: The maximal decoupled “Virtual Chain Of Responsibility”

  1. Robert

    Hi Stephan,

    I had a look at your magic4j library, I like your code aesthetically, it is easily readable and I can follow your thinking. I do not agree however with the content.

    Without going deep into the code, I just like to point out some symptoms which make it clear that this is not a good idea in general:

    * MatchResultValidatorChainController: As you solve the problem through interceptors, the method body is not actually called, that is why you throw an exception. Obviously if your reading the code, that will be a surprise each time. “I’m calling a method and it won’t be actually called by design???” I mean interceptors are ok for cross-cutting concerns like logging or statistics I guess, but this is going too far for me at least.
    * Almost exclusive use of reflection in the interceptor logic. As it does not know anything about anything, it has to rely on reflection. It needs (and actually does) check signatures and all that. It is unsafe and implicit.
    * Additional implicit coupling by assigning “special” meaning to Optional.

    I mean, I know these are subjective matters and obviously you might disagree, but I think these things can be solved easily through pure object-oriented design. CDI may be used to get all validator instances maybe, but it does not need to metastasize to virtually all classes.

    Reply
    1. StephanBauer Post author

      Hello Robert,
      many thanks for your review! You are indeed making some good points, but as you already expected, I do not agree to all of them:
      • Concerning the misuse of the interceptor as it is no real cross-cutting concern in your opinion, let me tell you first, what my primary goals are:
      a. Minimize boilerplate application code, so that developers can concentrate on the business logic. The default “Chain Of Responsibility” needs boilerplate code to setup the chain and instantiate the Chain-Handlers. For my “Virtual Chain”, the developer only needs to implement the custom Usecase-specific Qualifier-Annotation and annotate the Handlers and the Controller appropriately. That’s it – declarative style.
      b. Creating a design that follows the SOLID principles: In case of the CoR: if you don’t pay enough attention, you end up with compile-time dependencies between the Chain-Handlers. My “Virtual Chain” provides a solution for avoiding exactly that. There are for sure other solutions as well, but this one is implementable with minimal overhead and it leverages especially the Open-Closed Principle.
      c. The question, if the “ChainOfResponsibilityControllerInterceptor” provides a cross-cutting concern or not, appears to me as a minor issue compared to the opportunities it provides (as explained above). IMO, I can also argue, that detecting and invoking the Chain-Handlers is a cross-cutting concern from the point of view of the CoR-Pattern. It’s just that in the old-school chain-pattern implementation, you didn’t have the technical opportunity to move the boiler-plate code to somewhere else.
      d. Having said that, I want to underpin it with the analogy between my “Virtual Chain”-and the existing, built-in CDI-Decorator-Pattern. As stated in the CDI-Spec, a Decorator implements operations with business semantics and thus a decorator is not meant for cross-cutting concerns. Taking a closer look, you can see, that the equivalent of a Decorator-Bean is the Chain-Handler-Bean – and both provide business logic. Secondly, the equivalent to the @Delegate injection point of the Decorator pattern is the “ChainOfResponsibilityController”-Annotation. The biggest difference between both is, that the code that detects and invokes the Decorator-Beans is hidden in CDI-built-in classes whereas in my “Virtual Chain” it is of course the interceptor. As you can see, both cases are rather similar. I already thought about submitting this Virtual-Chain-Pattern to the CDI 2.0 working group, so that the logic can be drawn from my interceptor into built-in classes, just like in the Decorator-case. So to me, there is no general violation or misuse of interceptors in the virtual chain. Also, I was thinking about improving the current solution by replacing the interceptor with a CDI-Extension, but I couldn’t find an extension-specific way of “intercepting” the calls to the controller bean. Any idea about that is really welcome!
      • Concerning the uncalled controller-method: I agree, that the fact, that the controller method is actually not called from the interceptor is not nice. I was thinking about making it configurable if the interceptor should call the method or not. With this, the developer could decide for himself what he prefers. But on the one hand, my goal was to keep it simple for the first version and on the other hand, I didn’t want to lead any user into the misunderstanding that the return value from the controller method could also be what is actually returned to the caller. The problem here is, that the controller doesn’t have access to the result from the responsible Chain-Handler. Of course this could also be solved by letting the interceptor “inject” the result into an instance variable of the Controller instance, thus giving the developer the chance to do anything with that result inside the method body of the controller. But then he would also need to declare that injection point while in most cases, he will just return the injected result. This makes the whole story too complicated for my taste – at least for version 1.
      • Intensive use of Reflection in the interceptor-code: I believe that you cannot put the same restrictions concerning the usage of reflection onto framework code as you would usually do for normal application code. I believe, that many frameworks need to make massive use of reflection in order to fulfill their duties. It almost lies in the nature of frameworks that have to deal with dependency injection and object creation (see also OR-Mapper frameworks) to work with reflection.
      • Special meaning for java8-Optional: I was thinking about this as well for quite some time. It is no problem at all to make it configurable if the condition for a valid return value is “not null” or “non-empty Optional”. But again, I didn’t want to provide features in the first version that are neither vital for the general functioning nor was it absolutely foreseeable if someone will ever request it. I suppose I will add it now…
      Conclusion: I hope I was able to convince you, that the “Virtual Chain” is not an evil thing. I believe that the “Virtual Chain” is still object-oriented design, it just moves boilerplate code to a central place.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *