DEV Community

Cover image for Leveraging command pattern with Spring
DigitalCrafting
DigitalCrafting

Posted on

Leveraging command pattern with Spring

Intro

Every Java developer is either using Spring or at least heard of it, especially about Dependency Injection mechanism it provides. With a simple annotation

@Component
public class MyComponent {}
Enter fullscreen mode Exit fullscreen mode

we can create a bean and then inject it into our class

@Autowired
private MyComponent component;
Enter fullscreen mode Exit fullscreen mode

and Spring will handle the rest. The caveat to this, is that when extending some common interface, we need to tell specifically which instance we want to inject or mark one of the implementations as @Primary.

Or do we ?

The Trick

There is a simple way to inject ALL of our implementations into the class, and it looks like this:

 @Autowired
private Map<String, CommonInterface> interfacesMap;
Enter fullscreen mode Exit fullscreen mode

It's a super simple mechanism, which allows us to create a map of instances of our CommonInterface where the class name is the key.

The Leverage

We can leverage this to create easy to use Command Pattern implementation. For that we will need 4 interfaces or abstract classes:

  • Command
  • CommandResult
  • CommandExecutor
  • CommandDispatcher

Command and CommandResult will be only used to pass parameters/request and to receive the answer/response respectively. The CommandExecutor will use generics to bind it to Command:

public interface CommandExecutor<C extends Command, R extends CommandResult> {
    R execute(C command);
}
Enter fullscreen mode Exit fullscreen mode

Now the core of this solution - the CommandDispatcher. It will be responsible for triggering the execute method of CommandExecutor based on the Command type passed to it. The abstract class will look like this:

public abstract class CommandDispatcher {
    protected Map<Class, CommandExecutor> preparedMap = new HashMap<>();

    public <C extends Command, R extends CommandResult> R dispatch(C command) {
        return (R) preparedMap.get(command.getClass()).execute(command);
    }
}
Enter fullscreen mode Exit fullscreen mode

In the concrete implementation of the executor, using mechanism shown at the beginning we can inject all of our CommandExecutors into the class and using reflections we can bind Executor to the Command:

@Service
public class SpringCommandDispatcher extends CommandDispatcher {
    private final Map<String, CommandExecutor> rawMap;

    public SpringCommandDispatcher(Map<String, CommandExecutor> rawMap) {
        this.rawMap = rawMap;
    }

    @PostConstruct
    private void setUp() {
        if (rawMap != null && !rawMap.isEmpty()) {
            for (CommandExecutor commandExecutor : rawMap.values()) {
                Type command = ((ParameterizedType)commandExecutor.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
                preparedMap.put((Class) command, commandExecutor);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

And that's it. To dispatch some command we simply inject the CommandDispatcher and use it like this:

SomeCommandResult result = commmandDispatcher.dispatch(new SomeCommand());
Enter fullscreen mode Exit fullscreen mode

The major advantage of using Command Pattern this way is separatation of the 'business' logic from the Command, thanks to which we can have different implementations of the same Command for different versions of our application.

Summary

Working example of this can be found in my github.

This was my first ever post about what I know so I hope You enjoyed it and that it will come handy in Your projects 😊

Top comments (0)