Search code examples
c#design-patternsdependency-injectionfactory-pattern

Handlers factory with dependency injection design


My project already contains factory of some handlers (the code is very simplified):

class HandlersFactory
{
    private static readonly Dictionary<string, IHandler> registeredHandlers =
        new Dictionary<string, IHandler>
        {
            { "First", new FirstHandler() },
            { "Second", new SecondHandler() },
            { "Third", new ThirdHandler() },
        };

    public bool IsRegisteredHandler(string name)
    {
        return registeredHandlers.ContainsKey(name);
    }

    public IHandler GetHandler(string name)
    {
        if (!IsRegisteredHandler(name))
            return null;

        return registeredHandlers[name];
    }
}

The list of handlers is hard-coded. Factory is located in the main assembly, but application can be extended at runtime with additional assemblies (main application searches for them and load on the start). Additional assemblies customize application.

There must be a possibility to "replace" some handlers from the main assembly with new one (provide another logic). So, for example factory will return CustomSecondHandler by name "Second" instead of SecondHandler when additional assembly is loaded. I'm able to rewrite factory, but changes mustn't affect code that already uses it.

I thought about moving dictionary initialization to some method RegisterHandlers that can be overridden in the custom factory. And add dependency injection of factory, so additional assembly provide own implementation of factory derived from existing one. And if additional assembly isn't loaded then default factory is used. But I feel that something wrong with this approach.

Please, suggest your ideas. I need a clean solution with minimal code writing for providing custom handlers that will replace existing one.

Thank you.


Solution

  • How do you scan your assemblies for those handlers? If you use reflection, you could utilize custom attributes. Consider:

    [ForcesHandlerRegistrationOverride]
    public class CustomSecondHandler : IHandler
    {
        // ...
    }
    

    I assume you have a way to generate proper dictionary key for loaded handler as well as create handler instance - skipping those parts (name and handler variables initialization), here's how assembly scanning might look like:

    var types = loadedAssembly
        .GetTypes()
        .Where(t => type.IsAssignableFrom(typeof(IHandler)));
    
    foreach (var type in types)
    {
        if (factory.IsRegisteredHandler(name))
        {
            // usually you'll do nothing here, but now we check if handler
            // we want to register is marked with custom attribute so that
            // we can override already registered handler
            var canForceOverride = type.GetCustomAttributes(
                typeof(ForcesHandlerRegistrationOverride), true).Length > 0;
            if (canForceOverride)
            {
                factory.RegisterHandler(name, handler);
                // ...or to keep this one safe, add more appropriate method
                // for explicit replacement (see note below)
            }      
        }
        else
        {
            factory.RegisterHandler(name, handler);
        }
    }
    

    If your registration method on your factory class somehow guards against adding handler to an already existing key twice, you'll probably have to get rid of that (or expose registration method which can replace handler; like ReplaceHandler or ForceRegistration).

    This will let you easily control when your types should override/replace existing handlers, or simply register as new ones.

    Note that you can take it even one step further; declare your attribute with a name of handler it is supposed to replace - [ForcesHandlerRegistrationOverride("Second")].