В качестве IoC-контейнера используется Castle.Windsor.
Корневой элемент приложения - планировщик Scheduler, который выполняет в определенной последовательности различные задания. Задания реализуют интерфейс IWorker, поэтому я решил внедрить(inject) List
Конфигурация контейнера Castle.Windsor:
var container = new WindsorContainer();
// Для внедрения List
container.Kernel.Resolver.AddSubResolver(new ListResolver(container.Kernel, true));
// Регистрация Scheduler
container.Register(CastleRegistration.Component.For
Scheduler
public class Scheduler : IScheduler
{
public IList
public Scheduler(IList
Writer
public class Writer : IWorker
{
public string Source { get; set; }
public Writer()
{
}
public Writer(string source)
{
Source = source;
}
}
Cleaner
public class Cleaner : IWorker
{
public Cleaner()
{
}
}
UPDATE:
Дополнительный вопрос:
Содержимое Xml-файлов переехали в одну из таблиц БД. В Scheduler (корневой компонент) нужно внедрить по Writerу с параметром на каждую запись в таблице. Из полезного совета в ответе(Andrew Prigorshnev), следует, что обращаться к БД будет правильным после настройки контейнера и получения из него корневого компонента.
С точки зрения архитектуры, правильным ли будет внедрить в Scheduler компоненты: WriterFactoryи Repository(слой доступа к БД), после чего получить экземпляр Scheduler, затем в нем с помощью Repository получить список параметров, а потом сделать на каждый параметр по Writerу с помощью WriterFactory?
Ответ
Насколько я понимаю в наиболее общем виде вопрос состоит в следующем: как правильно внедрить список зависимостей с помощью контейнера Castle Windsor?
Ответ следующий. Сам подход с внедрением списка через конструктор вполне корректный и Castle Windsor умеет работать с такими сценариями. Чтобы внедрить список зависимостей надо сделать следующее:
Зарегистрировать в контейнере резолвер списков. Вы это уже сделали в строчке container.Kernel.Resolver.AddSubResolver(new ListResolver(container.Kernel, true));
Зарегистрировать в контейнере несколько компонентов, которые должны быть внедрены в виде списка. При этом если вы внедряете список типа IList
Таким образом правильная реализация вашей задачи должна выглядеть вот так:
var container = new WindsorContainer();
// Для внедрения List
container.Kernel.Resolver.AddSubResolver(new ListResolver(container.Kernel, true));
// Регистрация Scheduler
container.Register(Component.For
// Writers
container.Register(Component
.For
container.Register(Component
.For
container.Register(Component
.For
// Cleaner
container.Register(Component.For
// Забираем Scheduler
var scheduler = container.Resolve
И еще несколько замечаний, касающихся конкретно вашей реализации:
Вы хотите передавать в воркеры xml, но это плохая идея. Тогда вам придется прочитать его из файла прямо в корне компоновки, в том месте где вы настраиваете контейнер. Но чтение из файла это отдельная задача, никак не связанная с разрешением зависимостей, поэтому делать ее в корне компоновки не стоит. Пусть этим занимается один из ваших компонентов. В связи с этим, в примере кода выше вы можете увидеть что во Writer'ы передается путь к файлу, а не его содержимое.
Если файлы, которые вы обрабатывате меняются редко и их имена и количество известны на этапе написания кода, пример приведенный выше может вас полностью устроить. Но если это не так, надо сделать еще немного дополнительной работы, и как вы правильно предположили, надо использовать фабрики. Возможное решение здесь следующее. Надо добавить новую абстракцию - поставщик воркеров, который будет представлять собой фабрику, создающую воркеры:
public interface IWorkersProvider
{
IEnumerable
Поставщик Cleaner'ов будет просто возвращать один Cleaner. А поставщик Writer'ов может получать как аргумент конструктора путь к папке, в которой надо искать файлы, и затем в методе GetWorkersсоздавать по одному воркеру на каждый найденный файл:
public class WritersProvider : IWorkersProvider
{
public WritersProvider(string folderPath)
{
}
public IEnumerable
При такой архитектуре ваш Sheduler должен получать вместо списка воркеров, список поставщиков воркеров:
public class Scheduler : IScheduler
{
public Scheduler(IList
Регистрация такой конфигурации в Castle Windsor делается с помощью тех же методов, что и регистрация в первом примере, поэтому не буду здесь ее описывать.
UPDATE: Ответ на дополнительный вопрос
Лучше сделать так:
В Scheduler внедрить список поставщиков воркеров (IEnumerable
Идея здесь вот в чем. Каждый класс должен выполнять одну единственную задачу. Задача Scheduler-а взять готовый список воркеров и запустить их. Задача поставщиков воркеров - создать воркеры на основе какого-то правила (из xml-файлов, таблиц бд и т.д.). Разделив обязанности таким образом, вы получите код, который легко модифицировать. Если вы захотите поменять что-то в логике запуска воркеров, вы прийдете и поменяете только один класс - Scheduler, при этом вы будете уверены, что вы никак не сломали логику создания воркеров, потому что эту работу выполняют совсем другие классы.
Потенциально у вас может быть множество поставщиков воркеров, работающих по разным правилам. Один поставщик может всегда возвращать один и тот же жестко закодированный воркер, второй создавать по воркеру на каждый xml-файл в какой-нибудь директории, третий создавать воркеры на основе таблицы в базе данных, четвертый получать список воркеров, которые надо создать, у удаленного сервиса и т.д. Более того, в процессе развития проекта у поставщика воркеров может меняться правило, по которому он создает воркеры (у вас это уже произошло - вместо xml-файлов у вас теперь таблица в бд.)
Если поставщику воркеров для выполнения его работы нужен доступ к бд, внедряйте Repository прямо в поставщик. Если потом это правило изменится, прийдете и поменяете только конкретный поставщик, не трогая другие поставщики и не трогая Scheduler
Вариант с фабрикой похуже, но тоже годится. Но если будете внедрять фабрику воркеров то Repository внедряйте уже в нее, а не в Scheduler
Фабрика хуже, потому что, когда количество воркеров вырастет, вы все равно будете вынуждены разнести создание воркеров разных типов по отдельным классам, иначе фабрика будет слишком большой и запутанной. В итоге вы все равно создадите несколько фабрик (что полность аналогично моему варианту со списком поставщиков воркеров, отличие только в названии, фабрика и поставщик в данном случае одно и то же).
Комментариев нет:
Отправить комментарий