Я использую Castle Windsor для домашнего проекта, над которым работаю. Я начинаю замечать, что мне нужно вызывать контейнер IoC в разных местах моего кода для создания новых объектов. Эта зависимость от контейнера затрудняет поддержку моего кода.
Есть два решения, которые я использовал для решения этой проблемы.
Я попытался создать абстрактные фабрики в качестве оберток вокруг контейнера, которые я мог бы внедрить в части моего приложения, которые должны создавать объекты. Это работает, но имеет некоторые недостатки, потому что Castle с трудом вводит собственный контейнер в качестве зависимости. Так что мне приходится делать это вручную, такого рода поражение всей цели контейнера IoC.
Я использовал основной класс applicationcontroller, чтобы обернуть контейнер IoC и работать как центральная фабрика / репозиторий. Это было довольно успешно, но этот класс становится слишком большим и действует как центральный объект-бог, почти все остальные объекты имеют ссылку на него.
Оба решения вроде работают, но у обоих есть свои недостатки. Так что мне любопытно, были ли у других людей такая же проблема и они нашли лучшие решения.
редактировать Проблема не в объекте A, который зависит от объекта B. Здесь я обычно просто использую внедрение конструктора, и все работает. Иногда у меня есть объекты типа A, которым необходимо создать переменное количество других объектов типа B за время их существования. Я не знаю, как это сделать.
@Blair Conrad: До сих пор проблемы с обслуживанием не были серьезными. У меня были некоторые классы, зависящие от объекта-контейнера, вызывающего container.Resolve <>. И я не хочу, чтобы мой код зависел от того, что я считаю инфраструктурой. Я все еще пытаюсь что-то сделать, поэтому я заметил, что мне пришлось изменить много кода при переключении с Ninject на Castle для этого проекта.
@flowers: Хм. Мне нравится твое кулачное решение. Он сочетает в себе то, что работает в обоих решениях, которые я пробовал. Думаю, я все еще слишком много думал об объектах и недостаточно об интерфейсах / обязанностях. Я пробовал специально построенные фабрики, но я хотел бы, чтобы они использовали контейнер за кулисами для создания объектов, и я так и не узнал, как я могу преобразовать контейнер в объекты чистым способом.


Основное преимущество внедрения зависимостей, по крайней мере в моих приложениях, - это возможность писать код, не зависящий от контекста. С этой точки зрения ваше второе решение, похоже, действительно сводит на нет те преимущества, которые DI мог бы вам дать. Если «объект-бог» предоставляет разные интерфейсы для каждого класса, который на него ссылается, это может быть не слишком плохо. Но если вы зашли так далеко, я не понимаю, почему вы не дошли до обруча.
Пример: у вашего объекта God есть метод getFoo () и метод getBar (). Объекту A нужен Foo, объекту B - Bar. Если A нужен только один Foo, Foo следует ввести непосредственно в A, а A вообще не должен знать о Боге. Но если A нужно продолжать создавать Foos, ссылка на Бога в значительной степени неизбежна. Но вы можете защитить себя от ущерба, причиняемого, передавая Бога, сужая тип ссылки на Бога. Если вы заставите Бога реализовать FooFactory и дадите A ссылку на FooFactory, реализованную Богом, вы все равно можете написать код в A контекстно-нейтральным способом. Это улучшает возможности повторного использования кода и увеличивает вашу уверенность в том, что изменение Бога не вызовет неожиданных побочных эффектов. Например, при удалении getBar () из God вы можете быть уверены, что класс A не сломается.
НО ... если вы все равно собираетесь иметь все эти интерфейсы, вам, вероятно, лучше написать специализированные фабричные классы и связать все свои объекты вместе, включая фабрики, внутри контейнера, а не обертывать контейнер вообще. Контейнер все еще может настраивать фабрики.
Хотя я ценю явность «специализированных фабрик» и даже использую их сам, это похоже на запах кода в моих собственных проектах, потому что общедоступный интерфейс (маленький «i») постоянно меняется с новой фабрикой и / или новым методом GetX. для каждой реализации. После прочтения Пришло время разрядки контейнеров IoC Джереми Миллера я подозреваю, что дженерики и инъекция самого контейнера - это правильный путь.
Я бы обернул Ninject, StructureMap или Windsor в какой-то интерфейс IServiceLocator, подобный тому, который предлагается в статье Джереми. Затем создайте контейнерную фабрику, которая просто возвращает IServiceLocator в любом месте вашего кода, даже в циклах, как вы изначально предлагали.
IServiceLocator container = ContainerFactory.GetContainer();
while( keepLooping )
{
IExample example = container.GetInstance<IExample>();
keepLooping = example.DoWork();
}
Ваша фабрика контейнеров всегда может вернуть тот же вид, вы можете поменять местами фреймворки IoC, что угодно.
Пожалуйста, никогда не используйте статические классы, такие как IoC.Container.Resolve или ContainerFactory.GetContainer!
Это делает код более сложным, труднее тестировать, поддерживать, повторно использовать и читать.
Обычно у любого отдельного компонента или службы есть только одна единственная точка внедрения - это конструктор (с необязательными свойствами). И вообще ваши компоненты или классы обслуживания никогда не должны знать о существовании такой вещи, как контейнер.
Если ваши компоненты действительно нуждаются в динамическом разрешении внутри (т.е. разрешении политики обработки исключений или рабочего процесса на основе имени), то я рекомендую рассмотреть предоставление возможностей IoC через узкоспециализированных поставщиков
Спасибо за ссылку. Мне приходится читать его более внимательно, когда у меня есть время, но я думаю, что в конечном итоге я попробовал что-то подобное, затем отказался от этого и использовал абстрактную фабрику для создания динамических объектов вместо использования контейнера IoC. Я использовал контейнер только для создания самой фабрики.
@Rinat, используя исходный сценарий OP: вы находитесь в A, и вам нужно n экземпляров B. Без ссылки на контейнер, как вы получите свои четверки?
Ссылка на интернет-архив: web.archive.org/web/20080921233716/http://rabdullin.com/…
Как продолжение @flipdoubt
Если вы в конечном итоге используете шаблон типа локатора служб, вы можете проверить http://www.codeplex.com/CommonServiceLocator. Он имеет некоторые привязки, доступные для нескольких популярных фреймворков IoC (windsor, structuremap), которые могут быть полезны.
Удачи.
Спасибо! Я проверю это. Я обнаружил, что создать собственный локатор сервисов очень просто, но я могу почерпнуть идеи из того, как они это делали!
Я бы рекомендовал в этом случае использовать строго типизированные фабрики, как вы упомянули, которые вводятся. Эти фабрики могут обернуть контейнер, но могут разрешить передачу в дополнительном контексте и дополнительную обработку. Например, Create на OrderFactory может принимать контекстные параметры.
Наличие статических зависимостей от универсального локатора сервисов - плохая идея, поскольку вы теряете намерение и контекст. Когда IoC создает экземпляр, он может предоставить правильные зависимости на основе множества факторов, таких как профиль, контекст и т. д., Поскольку он имеет общую картину.
CommonServiceLocator не предназначен для этой цели, хотя может возникнуть соблазн его использовать. Основная цель CommonServiceLocator - для приложений / фреймворков, которые хотят быть совместимыми с контейнерами IoC. Однако приложения, которые используют, должны оптимально вызывать локатор только один раз, чтобы построить иерархию компонентов и их зависимостей. Его никогда не следует напрямую вызывать снова. Если бы у нас был способ добиться этого, мы бы это сделали. В Prism (http://www.microsoft.com/compositewpf) мы представили IContainerFacade для создания модулей. Это локатор услуг, хотя и низкого уровня. Оглядываясь назад, мы, вероятно, должны были создать ModuleFactory или что-то в этом роде и использовать IContianerFacade, чтобы заполучить его, а затем использовать эти модули разрешения вместо прямого перехода к фасаду. Ретроспективный взгляд - 20/20. Это достаточно низкий уровень, хотя на самом деле это не влияет на вещи.
На CSL мы боролись с наименованием, потому что это могло привести к путанице. В конце концов, мы остановились на CSL, потому что технически интерфейс не позволял вам выполнять DI.
Я бы порекомендовал посмотреть мини-сериал Ника Блюмхардта по этому поводу.
Это была отличная статья, которая помогла мне прояснить эту проблему!
Ссылка понравилась. Но я бы также предпочел резюме высокого уровня, а не просто ссылку.
Больше не доступно -> это последний действительный снимок web.archive.org/web/20100209011441/http://blogs.msdn.com/…
Это действительно общая проблема. Встроенный в Windsor Типизированный заводской объект даст вам преимущества использования фабрики без упомянутых недостатков.
Мне любопытно, и ответы могут помочь нам ответить. Какие проблемы с обслуживанием у вас были?