Фабрика обработчиков с инжекцией зависимостей - PullRequest
3 голосов
/ 08 марта 2012

Мой проект уже содержит фабрику некоторых обработчиков (код очень упрощен):

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

Список обработчиков жестко запрограммирован.Фабрика находится в основной сборке, но приложение может быть расширено во время выполнения за счет дополнительных сборок (основное приложение ищет их и загружает при запуске).Дополнительные сборки подгоняют приложение.

Должна быть возможность «заменить» некоторые обработчики из основной сборки на новые (предоставьте другую логику).Так, например, фабрика вернет CustomSecondHandler с именем «Second» вместо SecondHandler при загрузке дополнительной сборки.Я могу переписать фабрику, но изменения не должны влиять на код, который ее уже использует.

Я думал о переносе инициализации словаря в некоторый метод RegisterHandlers, который можно переопределить в пользовательской фабрике.И добавить внедрение зависимостей фабрики, чтобы дополнительная сборка обеспечивала собственную реализацию фабрики, полученную из существующей.А если дополнительная сборка не загружена, то используется фабрика по умолчанию.Но я чувствую, что что-то не так с этим подходом.

Пожалуйста, предложите свои идеи.Мне нужно чистое решение с минимальным написанием кода для предоставления пользовательских обработчиков, которые заменят существующие.

Спасибо.

1 Ответ

5 голосов
/ 08 марта 2012

Как вы сканируете ваши сборки для этих обработчиков? Если вы используете отражение, вы можете использовать пользовательских атрибутов . Рассмотрим:

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

Я предполагаю, что у вас есть способ сгенерировать правильный словарный ключ для загруженного обработчика, а также создать экземпляр обработчика - пропуская эти части (name и handler инициализация переменных), вот как может выглядеть сканирование сборки:

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

Если ваш метод регистрации в вашем фабричном классе каким-то образом препятствует добавлению обработчика к уже существующему ключу дважды, вам, вероятно, придется избавиться от этого (или предоставить метод регистрации, который может заменить обработчик ; например, ReplaceHandler или ForceRegistration).

Это позволит вам легко контролировать, когда ваши типы должны переопределять / заменять существующие обработчики или просто регистрироваться как новые.

Обратите внимание, что вы можете сделать еще один шаг вперед; объявите свой атрибут с именем обработчика, который предполагается заменить - [ForcesHandlerRegistrationOverride("Second")].

...