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 {}
we can create a bean and then inject it into our class
@Autowired
private MyComponent component;
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;
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);
}
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);
}
}
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);
}
}
}
}
And that's it. To dispatch some command we simply inject the CommandDispatcher
and use it like this:
SomeCommandResult result = commmandDispatcher.dispatch(new SomeCommand());
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)