Question

When I was looking this question, a question comes my mind.

Think about using an interface like :

public interface ICommandProcessor<T> where T : ICommand
{
    void Process(T command);
}

When you think about just interfaces, is there any advantage over direct using? Like :

public interface ICommandProcessor
{
    void Process(ICommand command);
}

Edit:

@Olivier's answer hits a good point.

It is an advantage if you want to create a command processor for a specialized command type

If specialized command type is needed, it is good. But I am not sure to force all command classes to use generics because of maybe they would need specialized command type. So, when think both things :

public interface ICommandProcessor<in T> where T : ICommand
{
    void Process(T command);
}

public interface ICommandProcessor : ICommandProcessor<ICommand>
{

}

public class SampleClass : ICommandProcessor
{
    public void Process(ICommand command)
    {
        //..
    }
}

public class AnotherSampleClass : ICommandProcessor<IAnotherCommand>
{
    public void Process(IAnotherCommand command)
    {
        //..
    }
}

Can we say it is the best design for ICommandProcessor? By this way, classes can implement ICommandProcessor or ICommandProcessor<T> if needs to create special one, I think.

Was it helpful?

Solution

It is an advantage if you want to create a command processor for a specialized command type. E.g. you have an interface

public interface IUndoableCommand : ICommand
{
    void Undo();
}

Now you can use it like this:

public class UndoableCommandProcessor : ICommandProcessor<IUndoableCommand>
{
    public void Process(IUndoableCommand command)
    {
        //TODO: Execute command. Push command to undo stack.
    }
}

UndoableCommandProcessor.Process() will not accept a simple ICommand.


The generic variant allows you to create specialized command processors restricted to process only specialized commands.

The non-generic variant allows you to create specialized commands and pass them to the processor, but the command processor must always be able to process the general ICommand.


A disadvantage of generic types is, that they are not assignment compatible when specialized with different generic type parameters. So T<A> and T<B> are not assignment compatible, even if A and B are. (Exception, see: Creating Variant Generic Interfaces (C#)).

Therefore, it is often useful for generic types to have a non-generic base type.

public interface ICommandProcessor
{
    void Process(ICommand command);
}

public interface ICommandProcessor<T> : ICommandProcessor
    where T : ICommand
{
    void Process(T command);
}

So, this is the exact contrary of your approach public interface ICommandProcessor : ICommandProcessor<ICommand>.

An implementation would hide the non-generic Process method by implementing it explicitly. This makes it accessible only when called directly through the ICommandProcessor interface.

If we have

public interface ICommand { }
public interface ICommandA : ICommand { }
public interface ICommandB : ICommand { }

Then we can create a specialized implementation like this:

public class CommandProcessorA : ICommandProcessor<ICommandA>
{
    void ICommandProcessor.Process(ICommand command) // Explicit implementation
    {
    }

    public void Process(ICommandA command) // Implicit implementation
    {
    }
}

We declare public class CommandProcessorB : ICommandProcessor<ICommandB> the same way. Now we can store different command processors like this

var processors = new List<ICommandProcessor> {
    new CommandProcessorA(),
    new CommandProcessorB()
};

This does not work:

// DOES NOT COMPILE!
var processors = new List<ICommandProcessor<ICommand>> {
    new CommandProcessorA(),
    new CommandProcessorB()
};

We get

Error CS1950 The best overloaded Add method 'List<ICommandProcessor<ICommand>>.Add(ICommandProcessor<ICommand>)' for the collection initializer has some invalid arguments ...

OTHER TIPS

Building further on Olivers answer. Its also alot easier to get a factory going with a generic interface version. You just do

public class CommandRunner
{
   public CommandRunner(IServiceProvider serviceProvider) 
   {
       _serviceProvider = serviceProvider;
   }

   public void Execute<TCOmmand>(TCommand cmd) where TCommand : ICommand
   {
      _serviceProvider.GetService<ICommandHandler<TCommand>>().Execute(cmd);
   }
}

You can only tell what the best design is after you established your usage scenario.

ICommandProcessor would work with any command whereas ICommandProcessor<T> would only work with a specific type of command. The first will give you more flexibility (it is more generic than the generic interface, go figure). The latter gives you stronger type safety.

What is more important to your application? Do you want to toss around commands that your main logic does not need to know the details about? Or do you discriminate between command types in your main logic?

It won't hurt to declare both interfaces. After all, it only starts to matter once a class implements either. And you can always implement both but that may not be helpful to the user of your class. Choosing one sends a message about the intended usage scenario. That is what you should consider: what is a command to me (or to my application). The choice between the two interfaces should follow.

Licensed under: CC-BY-SA with attribution
scroll top