处理器工厂与依赖注入设计

时间:2012-03-07 22:00:08

标签: c# design-patterns dependency-injection factory-pattern

我的项目已包含一些处理程序的工厂(代码非常简化):

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];
    }
}

处理程序列表是硬编码的。工厂位于主程序集中,但应用程序可以在运行时使用其他程序集进行扩展(主应用程序搜索它们并在开始时加载)。附加组件定制应用程序

必须有可能从主程序集中“替换”一些处理程序(提供另一个逻辑)。因此,例如,当加载其他程序集时,工厂将按名称“Second”而不是CustomSecondHandler返回SecondHandler。我能够重写工厂,但更改不得影响已经使用它的代码。

我考虑过将字典初始化移动到可以在自定义工厂中重写的某个方法RegisterHandlers。并添加工厂的依赖注入,因此附加组件提供从现有工厂派生的工厂自己的实现。如果未加载其他程序集,则使用默认工厂。但我觉得这种方法有问题。

请提出你的想法。我需要一个干净的解决方案,只需要最少的代码编写,以提供可替代现有处理程序的自定义处理程序。

谢谢。

1 个答案:

答案 0 :(得分:5)

如何为这些处理程序扫描程序集?如果您使用反射,则可以使用custom attributes。考虑:

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

我假设您有办法为加载的处理程序生成正确的字典键以及创建处理程序实例 - 跳过这些部分(namehandler变量初始化),这里是程序集扫描的样子:

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);
    }
}

如果您的工厂类中的注册方法以某种方式防止将处理程序添加到已存在的键两次,那么您可能必须摆脱它(或公开可以替换处理程序的注册方法;例如ReplaceHandlerForceRegistration)。

这将让您轻松控制何时类型应覆盖/替换现有处理程序,或者只是注册为新处理程序。

请注意,您甚至可以更进一步;使用应该替换的处理程序名称声明您的属性 - [ForcesHandlerRegistrationOverride("Second")]