Разрешение обработчиков с использованием Factory через контейнер IOC
-
21-12-2019 - |
Вопрос
У меня есть задача рефакторить монолитное приложение MVC (1 действие контроллера с более чем 1000 строками) во что-то более управляемое.
Цель программы — парсить коллекции разных типов одну за другой в определенном порядке.
Вот пример предлагаемого «лучшего» решения...
public interface IFruit
{
}
public class Banana : IFruit
{
}
public class Apple : IFruit
{
}
public interface IFruitHandler<in TFruit> where TFruit : IFruit
{
void Handle(IEnumerable<TFruit> fruit);
}
public class BananaHandler : IFruitHandler<Banana>
{
public void Handle(IEnumerable<Banana> fruit)
{
Console.WriteLine("break apart the bunch of bananas");
foreach (var banana in fruit)
{
Console.WriteLine("Peel the banana");
}
}
}
public class AppleHandler : IFruitHandler<Apple>
{
public void Handle(IEnumerable<Apple> fruit)
{
foreach (var apple in fruit)
{
Console.WriteLine("Slice the apple");
}
Console.WriteLine("Throw the apple cores away");
}
}
Как видите, у каждого фрукта есть свой обработчик, и реализация каждого обработчика может незначительно отличаться.Некоторые обработчики делают что-то перед работой с коллекцией, другие делают что-то позже, некоторые делают что-то до И после.
Приложение представляет один объект (через контроллер MVC) со всеми различными коллекциями фруктов в качестве свойств (по сути, модель большого представления).Мне нужен какой-то посредник, который мог бы взять все эти коллекции и передать их соответствующему обработчику.
Это то, что я имел в виду...
public class Mediator : IMediator
{
private readonly Func<Type, IFruitHandler<IFruit>> handlerFactory;
public Mediator(Func<Type, IFruitHandler<IFruit>> handlerFactory)
{
this.handlerFactory = handlerFactory;
}
public void Execute(IEnumerable<IEnumerable<IFruit>> fruitBundles)
{
foreach (var bundle in fruitBundles)
{
if (!bundle.Any())
continue;
var handler = handlerFactory(bundle.First().GetType());
handler.Handle(bundle);
}
}
}
Набор фруктов всегда будет содержать только один фрукт, а наборы будут располагаться в определенном порядке.Подпись для Mediator.Execute достаточно свободна, чтобы один пакет мог содержать разные плоды.Этого не должно произойти.
Вопрос двоякий....
Я могу зарегистрировать все FruitHandlers с помощью OpenGenerics...
container.RegisterManyForOpenGeneric(typeof(IFruitHandler<>),typeof(IFruitHandler<>).Assembly);
но у меня возникли трудности с правильной регистрацией фабрики. Как мне зарегистрировать фабрику с помощью Simple Injector?
Реализация фабрики медиатора/обработчика кажется неудобной, как и интерфейс IFruit (по сути, это всего лишь маркер) — вы бы изменили дизайн?
Решение
Вы можете реализовать Mediator
следующим образом, но у него есть некоторые недостатки:
- оно использует
dynamic
- тот
Mediator
необходимо ссылаться наContainer
(хотя это нормально, если вы определите этот класс в корне композиции)
Я считаю, что вопрос слишком общий, чтобы можно было дать какой-либо конкретный совет о том, как лучше разработать решение.
class Mediator : IMediator
{
private readonly Container container;
public Mediator(Container container)
{
this.container = container;
}
public void Handle(IEnumerable<IEnumerable<IFruit>> fruitBundles)
{
foreach (var bundle in fruitBundles)
{
if (bundle.Any())
{
dynamic instance = bundle.First();
this.Handle(instance, bundle);
}
}
}
private void Handle<T>(T instance, IEnumerable<IFruit> bundle)
where T : IFruit
{
var handler = this.container.GetInstance<IFruitHandler<T>>();
handler.Handle(bundle.Cast<T>());
}
}