Inyectando lógica de procesamiento de datos en clase

10

Quiero encontrar una forma más elegante y apreciada de inyectar procesadores en CommandProcessorDispatcherclase. O puede ser otra solución (el objetivo es separar cada lógica de procesamiento de comandos a una clase independiente). Tal vez algún patrón de diseño puede ser útil aquí.

public interface ICommand { }
public class StartCommand : ICommand { }
public class StopCommand : ICommand { }

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

public class StartCommandProcessor : ICommandProcessor<StartCommand>
{
    public void Process(StartCommand command) { }
}

public class StopCommandProcessor : ICommandProcessor<StopCommand>
{
    public void Process(StopCommand command) { }
}

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

public class CommandProcessorDispatcher : ICommandProcessorDispatcher
{
    public CommandProcessorDispatcher(Dictionary<Type, Action<ICommand>> processors)
    {
        _processors = processors;
    }

    private readonly Dictionary<Type, Action<ICommand>> _processors;

    public void Process(ICommand command)
    {
        _processors[command.GetType()](command);
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        var dict = new Dictionary<Type, Action<ICommand>>
        {
            { typeof(StartCommand), x => new StartCommandProcessor().Process((StartCommand)x) },
            { typeof(StopCommand), x => new StopCommandProcessor().Process((StopCommand)x) },
        };

        var dispatcher= new CommandProcessorDispatcher(dict);
    }
}
Abucheo
fuente
¿Le echó un vistazo a MediatR ? Básicamente hace lo que necesitas, y algo más.
Philippe
Esto es realmente antiguo, por lo que cualquier pregunta aclaratoria probablemente no será respondida. ¿Tiene esto algo que ver con WPF o UAP? ICommandse define con esas tecnologías y se usa ampliamente, por lo que es difícil separar eso de mi mente. ¿Cuál es el objetivo final? Quizás el concepto subyacente podría usar algún refinamiento.
Berin Loritsch

Respuestas:

1

Creo que puede simplificar un poco las cosas, sin demasiada repetitiva, mientras disfruta de algún tipo de seguridad.

Un ejemplo artificial:

public interface ICommand
{
}

#region Friends
public abstract class CommandProcessor
{
    internal abstract void Process(ICommand command);

    public abstract Type CommandType { get; }
}

public abstract class CommandProcessor<TCommand> : CommandProcessor
    where TCommand : ICommand
{
    internal override void Process(ICommand command)
    {
        Process((TCommand)command);
    }

    protected abstract void Process(TCommand command);

    public override Type CommandType { get { return typeof(TCommand); } }
}

public class CommandProcessorDispatcher
{
    public CommandProcessorDispatcher(IEnumerable<CommandProcessor> processors)
    {
        Processors = processors;
    }

    public void Process(ICommand command)
    {
        var found =
            Processors.
            FirstOrDefault
            (
                processor => processor.CommandType == command.GetType()
            );
        if (found == null)
        {
            throw new InvalidOperationException("no suitable processor found");
        }
        found.Process(command);
    }

    // (may just be protected only, depending on requirements)
    public IEnumerable<CommandProcessor> Processors { get; protected set; }
}
#endregion

public class StartCommand : ICommand
{
    public override string ToString()
    {
        return StartId.ToString();
    }

    public int StartId { get; set; }
}

public class StopCommand : ICommand
{
    public override string ToString()
    {
        return StopId.ToString();
    }

    public int StopId { get; set; }
}

public class StartCommandProcessor : CommandProcessor<StartCommand>
{
    protected override void Process(StartCommand command)
    {
        Console.WriteLine("START : " + command);
    }
}

public class EndCommandProcessor : CommandProcessor<StopCommand>
{
    protected override void Process(StopCommand command)
    {
        Console.WriteLine("STOP : " + command);
    }
}

class Program
{
    public static void Main(string[] args)
    {
        var dispatcher =
            new CommandProcessorDispatcher
            (
                new CommandProcessor[]
                {
                    new StartCommandProcessor(),
                    new EndCommandProcessor()
                }
            );

        var start = new StartCommand { StartId = 123 };
        var stop = new StopCommand { StopId = 456 };

        dispatcher.Process(start);
        dispatcher.Process(stop);

        Console.ReadKey();
    }
}

Esto supone menos suposiciones sobre cómo se almacenan los procesadores de comandos, y no requiere delegados de acción, lo que no pude ver el propósito en su ejemplo; finalmente, creo que es preferible tener el downcast (desde ICommand a una de sus implementaciones ) se producen solo en una ubicación.

YSharp
fuente