Хорошо. Предположим, у вас есть следующие команды:
public class MoveCustomerCommand : ICommand
{
public int CustomerId { get; set; }
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) { }
public event EventHandler CanExecuteChanged;
}
public class KillCustomerCommand : ICommand
{
public int CustomerId { get; set; }
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) { }
public event EventHandler CanExecuteChanged;
}
Теперь рассмотрим следующее предложение архитектуры для обработчиков:
public abstract class CommandHandlerBase
{
protected static readonly Dictionary<Type, CommandHandlerBase> _handlers = new Dictionary<Type, CommandHandlerBase>();
protected abstract void HandleCommand<TCommand>(TCommand command) where TCommand: ICommand;
public static void Handle<TCommand>(TCommand command) where TCommand : ICommand
{
if (_handlers.TryGetValue(typeof(TCommand), out var handler))
{
handler.HandleCommand(command);
}
}
}
public abstract class CommandHandlerBase<TCommandHandlerBase, TCommand> : CommandHandlerBase
where TCommandHandlerBase : CommandHandlerBase<TCommandHandlerBase, TCommand>, new() where TCommand : ICommand
{
public static void Register()
{
var type = typeof(TCommand);
_handlers[type] = new TCommandHandlerBase();
}
protected override void HandleCommand<T>(T command) => Handle((TCommand) (object) command);
public abstract void Handle(TCommand command);
}
По сути, мы делаем централизацию всей обработки в одном базовом классе, и мы предоставляем только точку входа для обработки любого TCommand
(при условии, что зарегистрирован обработчик, вы можете поставить случай по умолчанию или просто аварийно завершить работу, если обработчик не найден).
Реализация может показаться запутанной на первый взгляд, но использование действительно хорошо после: мы определяем только наши классы обработчиков и вызываем Register
. Посмотрим, как они выглядят:
public class MoveCustomerCommandHandler : CommandHandlerBase<MoveCustomerCommandHandler, MoveCustomerCommand>
{
public override void Handle(MoveCustomerCommand command) => Console.WriteLine("Moving the customer");
}
public class KillCustomerCommandHandler : CommandHandlerBase<KillCustomerCommandHandler, KillCustomerCommand>
{
public override void Handle(KillCustomerCommand command) => Console.WriteLine("Killing the customer");
}
И тестирование:
private static void Main(string[] args)
{
MoveCustomerCommandHandler.Register();
KillCustomerCommandHandler.Register();
CommandHandlerBase.Handle(new MoveCustomerCommand());
CommandHandlerBase.Handle(new KillCustomerCommand());
Console.ReadLine();
}
Я думаю, что этот подход более удобен в обслуживании и масштабируемости, не требует рефлексии (из-за снижения производительности), не требует очень больших операторов switch или жестко закодированных решений.
Кроме того, вы можете позже добавить незарегистрированный метод или оставить несколько обработчиков для данной команды, предел - это небо .. =)