I am pretty new to programming languages and only have limited knowledge about design patterns, so I hope you can help me with the following problem:
I have an application that operates on a group of different services. One functionality of the application is, to provide an interface to the user to call every method of the available services. Therefore I want to use the Command pattern because it allows me to add new commands just by adding new classes and not by changing the existing code. The parameters for each service command are passed to the constructor.
Commands:
public interface ICommand {
void Execute();
}
public abstract class Command<T> : ICommand {
public T Service { get; set; }
public abstract void Execute() { /* use service */ }
}
public class Command1 : Command<Service1> {
T1 param1;
...
public Command1(T1 param1, ...) { /* set parameters */ }
public override void Execute() { /* call first service1 method */ }
}
...
public class Command2 : Command<Service2> {
T2 param1;
...
public override void Execute() { /* call first service2 method */ }
}
...
The advantage is, that the user can instantiate a group of commands without knowing the application's interface and execute them at a later time when the service was set. The problem is, that I don't know how I can elegantly inject the services.
The application is mainly responsible for starting and stopping the services and keeping an instance of each service in a central place.
Application:
public class Application {
S1 Service1;
S2 Service2,
...
public void StartService(/* params */) { /* ... */ }
public void StopService(/* params */) { /* ... */ }
...
}
Question:
So my question is how do I get the correct service inside a command?
I thought about using some kind of Dependency injection, Service locator or Builder pattern, but I never used these patterns and I am not sure what's the best solution in this case and how to implement it correctly.
Update:
Thanks to the comment of @Andy and @Anders it's probably the best solution to use a Command class for my parameters and a CommandHandler class for the logic. The advantage is, that I can instantiate a command handler inside the Application class and pass the correct service in the handler's constructor. Also, I can create a command outside the Application without knowing the service a pass this command to the Application for execution.
To map commands to the correct command handler I a CommmandBus as suggested by @Andy, but I have some trouble implementing the java example in C# because there is no template map like Map<Class<? extends CommandHandler<?>>, CommandHandler<? extends Command>>
in C#.
So what's a clean solution to map a command to its handler in C#? I don't really like my solution below because I have to upcast the command.
My updated code:
public interface ICommand { }
public class ConcreteCommand : ICommand {
public Type1 Param1 { get; set; }
public Type2 Param2 { get; set; }
/* some more parameters */
}
public interface ICommandHandler<in TCommand> {
Task Handle(TCommand command);
}
public class ConcreteCommandHandler : ICommandHandler<ConcreteCommand> {
private readonly S1 service;
public ConcreteCommandHandler(S1 service) {
this.service = service;
}
public Task Handle(ConcreteCommand command) {
return service.DoSomething(command.Param1, ...);
}
}
public class CommandBus {
/* BAD: Return type of command handler hardcoded */
private readonly IDictionary<Type, Func<ICommand, Task>> handlerMap = new Dictionary<Type, Func<ICommand, Task>>();
public void RegisterCommandHandler<TCommand>(ICommandHandler<TCommand> handler) where TCommand: ICommand
{
Type commandType = typeof(TCommand);
if (handlerMap.ContainsKey(commandType))
throw new HandlerAlreadyRegisteredException(commandType);
/* BAD: Narrowing cast */
handlerMap.Add(commandType, command => handler.Handle((TCommand) command));
}
public Task Dispatch<TCommand>(TCommand command) where TCommand : ICommand
{
Type commandType = typeof(TCommand);
if (!handlerMap.TryGetValue(commandType, out Func<ICommand, Task> handler))
throw new HandlerNotRegisteredException(commandType);
return handler(command);
}
}
public class Application {
private CommandBus bus;
private S1 service1;
private S2 service2;
...
private void InitializeBus() {
bus.RegisterCommandHandler(new ConcreteCommandHandler(service1))
...
}
public void ExecuteCommand<TCommand>(TCommand command) where TCommand : ICommand {
bus.Dispatch(command);
}
...
}
Best Answer
Before answering your question, one should firstly know the goal developers are trying to achieve with the command pattern in the first place. More often than not, the pattern's purpose is to decouple modules from each other and to provide a sensible abstraction for execution of intents (commands) to the system.
In order to understand how commands fit into an application, let's integrate the command pattern by refactoring a hypothetical application which allows users to be registered. I apologise for the Java language, I haven't programmed in C# for a long time. In this very simple application we have a service and a controller:
On the service layer, there's a design issue. The method takes 4 string arguments in a very specific order. This couples the caller of the method to the service and refactoring the method by adding a new optional argument might could end up being difficult if you called the
registerUser
from multiple places.In order to reduce the number of arguments of the service method, let's introduce a special DTO object, acting as a data messenger between the two layers, and for the same of convenience, name it
RegisterUserCommand
. The Object would have the following structure:which will lead to design change of the service method, now looking like this:
and the controllers method would change to:
This fixes the unnecessary argument positional coupling, making it easier to introduce optional arguments to the user registration method by simply adding a new attribute to the command objects. In places where you don't work with the optional argument, no changes are necessary at all, in other places you may utilise the newly added property.
However the current design still contains coupling between the controller and the service. In this case, I wouldn't say it's a huge problem but since we're discussing a full command pattern integration, we'll refactor the code a bit further.
Introduction of a command bus
Using the command pattern without having some variant of a command bus is pretty meaningless. But what is this command bus? In short, it's mostly a glorified service locator. Some advanced implementations of command buses allow you to use plugins, middle-wares, effectively giving you the ability to extend the functionality of the command bus execution process without having to know it's internals.
Command busses generally consist of two main parts:
Both of those two parts need to be configured by you. The 1. part requires you to somehow provide service instances to the command bus, and the 2. part requires you defining the mapping.
A very basic command bus implementation (really basic, no support for plugins etc.) could look like this:
This command bus allows you to register a command handler to a registry (fulfilling the 1. part) and to provide mapping for a given command to a certain handler (fulfilling the 2. part). Besides that it allows you to execute a command with an expected result class.
As a part of implementation, you may have noticed that that we've also introduced two interfaces,
Command
andCommandHandler
. Those are necessary in compiled languages, but could be theoretically omitted in dynamic languages such as PHP or Python (as long as you'd follow certain guidelines defined by the command bus' implementation - mainly the method of the execution method of a handler).Simple integration of our command bus
First we have to make our
RegisterUserCommand
implement theCommand interface
:then we make the
UserService
implement theCommandHandler
interface, i.e. we need to add an appropriateexecute
method implementation. For the sake of simplicity, let's change our currentregisterUser
method to theexecute
method.These changes have been basically made so that the command and the service may be used in our command bus context. Right now you may have noticed a caveat. A class may only implement a generic interface only once, which forces you to basically have one command handler for each command. While you might find this troublesome at the beginning, in the long run this is actually very nice, since you'll end up with many tiny command handlers, each having only a single responsibility - handling a specific command instance.
A very basic integration of our command bus on the controller level could then look like the following (without actually making too much sense at the moment):
Here we have manually created an instance of the command bus, registered a command handler and a command to handler mapping, basically forcing the command bus acting as a proxy. In reality, in big application there would usually be at most a few instances of a few separate command busses (or perhaps even only a single instance), which would come pre-configured, already containing all registered handlers and mappings, and such configured command bus would be injected, turning your controller into the following:
Thanks to this, you are no longer coupled to the
UserService
and the mapping is done on an infrastructure level (through configuration). This can be beneficial as it reduces number of dependencies of services, should a single service/controller/command handler invoke multiple commands.The configuration could be e.g. a YAML, looking e.g. like this:
The
CommandBus
would obviously need to provide a mechanism to correctly parse it.Some advantages of a command bus
Some disadvantages of a command bus
Other potential (dis)advantages may come up during discussion of command bus integration into your own project. Whether the approach to use a command bus is fitting for a given project or not depends on many variables and has to be talked through with the system's architect.