Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace casting with better pattern

I'm working on simple client-server solution where client can send different types of commands to server and get specific results. Commands can have different properties. What I'd like to have is an architecture where a specific command handler could be chosen based on the type of the command it handles. I created basic infrastructure as follows:

public interface ICommand
{
}

public class CommandA: ICommand
{
    public string CustomProperty { get; set; }
}

public class CommandB: ICommand
{
}

Each command has its own CommandHandler which is responsible for handling the command and returning result. They all inherit from CommandHandlerBaseClass:

public interface ICommandHandler
{
    bool CanHandle(ICommand command);
    IReply Handle(ICommand command);
}

public abstract class CommandHandlerBase<TCommand> : ICommandHandler
    where TCommand : class, ICommand
{
    public bool CanHandle(ICommand command)
    {
        return command is TCommand;
    }

    public IReply Handle(ICommand command)
    {
        return Handle(command as TCommand);
    }

    public abstract IReply Handle(TCommand command);
}

// Specific handler
public class CommandAHandler : CommandHandlerBase<CommandA>
{
    public override IReply Handle(CommandA command)
    {
        //handling command and returning result
        return null;
    }
}

I also created a class responsible for choosing suitable handler and returning result:

public interface IReplyCreator
{
    IReply GetReply(ICommand command);
}

public class ReplyCreator : IReplyCreator
{
    private readonly IEnumerable<ICommandHandler> _commandHandlers;

    public ReplyCreator(IEnumerable<ICommandHandler> commandHandlers)
    {
        _commandHandlers = commandHandlers;
    }

    public IReply GetReply(ICommand command)
    {
        var commandHandler = _commandHandlers
            .FirstOrDefault(x => x.CanHandle(command));

        if (commandHandler == null)
            return null;
        return commandHandler.Handle(command);
    }
}

I don't like casting in CommandHandlerBase class, but I can't find any patterns to avoid it. I could create a generic interface as shown below, but how to register and choose a specific handler in ReplyCreator then?

public interface ICommandHandler<TCommand>
    where TCommand : ICommand
{
    bool CanHandle(TCommand command);
    IReply Handle(TCommand command);
}

The commands received in the server are serialized with Json.net as follows:

JsonConvert.SerializeObject(new CommandA(), new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.All
    };)

So I receive a string which eventually need to be deserialized into concrete command and handled by suitable handler. Is there any way of avoiding casts at all in such a scenario? I use StructureMap as my IoC library.

like image 677
syntax_error Avatar asked Oct 21 '22 08:10

syntax_error


1 Answers

Why are you trying to avoid this cast? Any workarounds that come to my mind now, will not be any nicer than this.

I would just avoid using as keyword for this purpose. It will fail silently in the unlikely case when wrong type is passed to the handler. In cases like this, you want exception to be thrown immediately, not somewhere later in your code.

like image 136
SoftwareFactor Avatar answered Oct 23 '22 01:10

SoftwareFactor