Фабрика обработчиков с инжекцией зависимостей
Мой проект уже содержит фабрику некоторых обработчиков (код очень упрощен):
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
по имени "Второй" вместо SecondHandler
когда дополнительная сборка загружена. Я могу переписать фабрику, но изменения не должны влиять на код, который его уже использует.
Я думал о переносе инициализации словаря в какой-то метод RegisterHandlers
это можно переопределить в пользовательской фабрике. И добавить внедрение зависимостей фабрики, чтобы дополнительная сборка обеспечивала собственную реализацию фабрики, полученную из существующей. А если дополнительная сборка не загружена, то используется фабрика по умолчанию. Но я чувствую, что что-то не так с таким подходом.
Пожалуйста, предложите свои идеи. Мне нужно чистое решение с минимальным написанием кода для предоставления пользовательских обработчиков, которые заменят существующие.
Спасибо.
1 ответ
Как вы сканируете свои сборки для этих обработчиков? Если вы используете отражение, вы можете использовать пользовательские атрибуты. Рассматривать:
[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")]
,