Long time lurker, first time poster. I am usually quite good at searching the forums to find a solution to a problem I am having. However, not this time.
I have outlined the following interfaces in C#:
public interface ICommand {}
public interface ICommandDispatcher<TCommand> where TCommand : ICommand {
void Dispatch(TCommand command);
}
public interface ICommandDispatcherFactory<TDispatcher, TCommand>
where TDispatcher : ICommandDispatcher<TCommand>, new()
where TCommand : ICommand
{
TDispatcher Create();
}
So far, so good. I can create my supported commands and implement their respective dispatchers and factories:
// Commands
public class CommandFileBase : ICommand
{
private readonly string _filepath;
public CommandFileBase(string filepath) {
_filepath = filepath;
}
}
public class CommandBackupFile : CommandFileBase
{
public CommandBackupFile(string filepath) : base(filepath){}
}
public class CommandCreateFile : CommandFileBase
{
public CommandCreateFile(string filepath) : base(filepath){}
}
public class CommandDeleteFile : CommandFileBase
{
public CommandDeleteFile(string filepath) : base(filepath){}
}
// Dispatchers
public class CommandBackupFileDispatcher : ICommandDispatcher<CommandBackupFile>
{
public void Dispatch(CommandBackupFile command)
{
//work
}
}
public class CommandCreateFileDispatcher : ICommandDispatcher<CommandCreateFile>
{
public void Dispatch(CommandCreateFile command)
{
//work
}
}
public class CommandDeleteFileDispatcher : ICommandDispatcher<CommandDeleteFile>
{
public void Dispatch(CommandDeleteFile command)
{
//work
}
}
// Factories
public class CommandBackupFileDispatcherFactory
: ICommandDispatcherFactory<CommandBackupFileDispatcher, CommandBackupFile>
{
public CommandBackupFileDispatcher Create()
{
return new CommandBackupFileDispatcher();
}
}
public class CommandCreateFileDispatcherFactory
: ICommandDispatcherFactory<CommandCreateFileDispatcher, CommandCreateFile>
{
public CommandCreateFileDispatcher Create()
{
return new CommandCreateFileDispatcher();
}
}
public class CommandDeleteFileDispatcherFactory
: ICommandDispatcherFactory<CommandDeleteFileDispatcher, CommandDeleteFile>
{
public CommandDeleteFileDispatcher Create()
{
return new CommandDeleteFileDispatcher();
}
}
Now, I would like to create a "container" that maps each supported command to its respective dispatcher factory and wraps the Dispatch calls. I want to map the factories, as some dispatchers can potentially group several commands together. For example, there could be a CommandBackupAndDeleteFile command. So I have devised this ICommandContainer interface:
public interface ICommandContainer<TCommand> where TCommand : ICommand {
void Dispatch(TCommand command);
}
My problem starts when implementing the container class. I want a dictionary that maps the ICommand type to its dispatcher factory but I just cannot get the code correct. I am not sure if I am just missing something, my interface design is faulty, or if I am on the wrong track entirely. I've tried a Dictionary of type, type and a Dictionary of type, object and I cannot seem to get an object returned that can execute the Dispatch() method. I've also tried to use the factory interface as the dictionary value, but of course the compiler did not like that. In other container implementations that I've found, the activator call is cast as the interface the object implements. I'm not sure if that's possible in my case, as I only have the generic Dispatcher type to work with.
public class CommandContainer<TDispatcher, TCommand> : IContainer
where TDispatcher : ICommandDispatcher<TCommand>, new()
where TCommand : ICommand
{
private readonly Dictionary<Type, object> _container;
public CommandContainer()
{
_container = new Dictionary<Type, object>();
_container.Add(typeof(CommandBackupFile), new CommandBackupFileDispatcherFactory());
_container.Add(typeof(CommandCreateFile), new CommandCreateFileDispatcherFactory());
_container.Add(typeof(CommandDeleteFile), new CommandDeleteFileDispatcherFactory());
}
public void Dispatch(TCommand command)
{
Type type = null;
if (_dictionary.TryGetValue(typeof(TCommand), out type))
{
var factory = Activator.CreateInstance(type); // as TDispatcher
//var dispatcher = factory.Create();
//dispatcher.dispatch(command);
}
else
{
throw new InvalidOperationException("unsupported command");
}
}
}
Here's an implementation I tried by merging code with another container found on this forum.
public class TestContainer2
{
Dictionary<Type, Func<object>> registrations = new Dictionary<Type, Func<object>>();
public TestContainer2()
{
Register<CommandBackupFile, CommandBackupFileDispatcher>();
Register<CommandCreateFile, CommandCreateFileDispatcher>();
Register<CommandDeleteFile, CommandDeleteFileDispatcher>();
}
public void Register<TCommand, TDispatcher>()
where TDispatcher : ICommandDispatcher<TCommand>, new()
where TCommand : ICommand
{
registrations.Add(typeof(TCommand), () => GetInstance(typeof(TDispatcher)));
}
public object GetInstance(Type serviceType)
{
Func<object> creator;
if (registrations.TryGetValue(serviceType, out creator)) return creator();
else if (!serviceType.IsAbstract) return CreateInstance(serviceType);
else throw new InvalidOperationException("No registration for " + serviceType);
}
private object CreateInstance(Type implementationType)
{
var ctor = implementationType.GetConstructors().Single();
var parameterTypes = ctor.GetParameters().Select(p => p.ParameterType);
var dependencies = parameterTypes.Select(t => GetInstance(t)).ToArray();
return Activator.CreateInstance(implementationType, dependencies);
}
public void Dispatch<TCommand>(TCommand command)
{
var dispatcher = GetInstance(typeof(TCommand));
//dispatcher.Dispatch(command);
}
}
Any suggestions or feedback would be greatly appreciated. Thank you for your time.
Laevus
Aucun commentaire:
Enregistrer un commentaire