Общий сервисный реестр - PullRequest
3 голосов
/ 04 июня 2011

В течение долгого времени нам посчастливилось иметь Common Service Locator (CSL) для разрешения служб из неизвестного источника.Тем не менее, никогда прежде не было никакого независимого от контейнера решения для регистрации этих услуг.Мы всегда сталкивались с проблемой необходимости связывать наш составной код с конкретным контейнером IoC, и, более того, мы стремимся использовать этот контейнер во всем нашем приложении.

Я чувствую, что, возможно, пытаюсь достичьздесь невозможно, но есть ли у кого-нибудь идеи о том, как создать Common Service Registry (CSR)?

Моя первоначальная идея состояла в том, чтобы использовать MEF для разрешения различных IContainerIntegrator s (один класс для каждой контейнерной технологии).которые в свою очередь используют MEF для разрешения различных IXXXContainerBinding с (один интерфейс для каждой технологии).Затем пользователи могут разрабатывать приложения на основе привязок контейнера и просто помещать свои привязки в каталог BIN, чтобы создать архитектуру подключаемых модулей.Если бы они хотели использовать новую контейнерную технологию, то им просто нужно было бы разработать новый класс IContainerIntegrator и сопровождающий IXXXContainerBinding, который они затем использовали бы для написания своих собственных привязок.При запуске приложения CSR объединяет все экземпляры контейнера в одну CSL, используя класс ServiceLocatorAggregator.

Я получил это, но столкнулся со следующими проблемами:

  • Любая привязка, которая выполняет вызовы к текущему (неполному) контейнеру, является нестабильной, поскольку предыдущие регистрации могут быть переопределены последующими привязками.Это включает в себя привязки, которые должны разрешать объекты для принятия решения о регистрации (то есть «связать это, если таковое имеется»).
  • Могут существовать две привязки, которые предоставляют один и тот же набор услуг.Однако мы хотим «переплетить» эти регистрации.Например, «я хочу, чтобы службы A и C связывались с X, а службы B и D - с привязкой Y».
  • Контейнеры разрешают службы сами, когда автоматически устанавливаются зависимости запрошенных служб.Например, «container.Bind .To ()» автоматически внедрит реализацию с услугами, разрешенными из «контейнера», а не из агрегированного локатора служб.

Пожалуйста, кричите на меня, еслиЗдесь я полностью упустил момент, но не является ли это самым отделенным решением для управления зависимостями?Разве это не было бы хорошо?Я собираюсь начать большой корпоративный проект с архитектурой плагинов.Я не хочу фиксировать конкретный контейнер IoC.

(ps. Этот вопрос касается не зависящей от контейнера композиции, которая поддерживает обнаружение. Пожалуйста, не вступайте в дискуссию по SL против DI. SL используется в композиции,вот почему я так много на нее ссылаюсь)

Ответы [ 2 ]

9 голосов
/ 05 июня 2011

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

Использование КонструкторИнъекция во всем приложении.Гарантирует, что ни один из ваших прикладных уровней не должен ссылаться на какой-либо контейнер вообще .Затем создайте весь граф приложения в корне приложения .

Вам не нужно использовать , чтобы использовать DI-контейнер для этого, но если вы решите использоватьКонтейнер DI, вы должны изолировать его от Корень композиции .Это означает, что если вы позже решите перейти на другой контейнер, вам нужно всего лишь изменить корень композиции.Тем не менее, DI для бедняков также является опцией.

1 голос
/ 04 июня 2011

Существует целый ряд приемов, которые вы можете использовать, чтобы избежать необходимости зависеть от конкретного контейнера IoC в большей части вашего кода, простейшим из которых является использование инъекции в конструктор. Если вы не можете использовать шаблон локатора службы, просто создайте свой собственный класс локатора службы, который будет содержать ядро ​​контейнера IoC, которое вы планируете использовать.

Тем не менее, целью контейнера IoC является достижение «инверсии управления»: то есть перемещение элемента управления с нижних уровней на верхние. Это означает, что вам нужно иметь точку рядом с «вершиной» (или «корнем») вашего приложения, которая фактически знает обо всех реализациях служб, которые оно будет использовать, а также о вашей конкретной реализации IoC. Это должно быть ограничено несколькими классами. Обычно «корень контекста» приложения - это место, где вы инициализируете свой контейнер IoC и локатор служб. Должен быть определенный модуль или группа модулей, которые позаботятся о настройке всех ваших привязок.

Если вы хотите разрешить использование плагинов, вам нужно создать специальный API для их использования и соответствия. Простое разрешение другим пакетам определять новые привязки IoC - это путь к катастрофе, поскольку вы не знаете, насколько хорошо эти разные пакеты будут играть вместе.

ASP.NET MVC 3 является хорошим примером этого. У них есть определенные локаторы фабрики сервисов, которые вы переопределяете в методе Global Application_Start. Чтобы реализовать одну из этих фабрик, вы должны соблюдать API, который они вам предоставляют. Но вы можете создать реализацию, которая использует любой контейнер IoC, который вы хотите, или ни одного вообще. Вы не меняете «привязки» вообще. Вы просто указываете платформе, что для текущего приложения вы хотите, чтобы оно использовало «эту фабрику» для создания контроллеров или поставщиков метаданных модели вместо использования фабрики по умолчанию.

Чтобы использовать другой пример, более подходящий для вашего конкретного примера, давайте рассмотрим случай ISearchProvider. У вас может быть встроенный LuceneProvider, и, возможно, один из ваших плагинов может предоставить GoogleProvider. Каких из этих провайдеров вы хотите использовать? Означает ли простое присутствие GoogleProviderPlugin, что LuceneProvider больше не доступен? Должны ли поиски как-то объединять результаты обоих этих провайдеров? Может ли пользователь выбрать одного или нескольких поставщиков из пользовательского интерфейса?

Независимо от ответа на эти вопросы, в конечном итоге вы хотите, чтобы это контролировало ваше приложение, а не плагин. Вместо того, чтобы давать плагину карт-бланш для использования ваших привязок DI, вы хотите сказать плагину: «Я разрешаю вам определять дополнительных поставщиков поиска, и вот как вы можете их зарегистрировать». Они могут быть зарегистрированы различными способами, включая аннотации / атрибуты класса или просто наличие класса, который реализует данный интерфейс. Но важным моментом является то, что существует API, который конкретно определяет, к чему они могут «подключаться», и что вам нужно от любого, кто создает плагин.

Теперь, если GoogleProvider имеет зависимости, которые определены в плагине, этот плагин может разрешать эти зависимости так, как он этого хочет. Надеюсь, он будет использовать какой-то IoC-контейнер, но если он этого не сделает, это не будет проблемой. Вы все еще можете быть агностиком в отношении типа контейнера, который они используют, если таковой имеется.

Если есть определенные службы, которые, как вы ожидаете, SearchProvider потребуется, вы можете включить эти службы или фабрики для этих служб в состав API инициализации для вашего плагина. Таким образом, ваш плагин может получить доступ к этим сервисам, не зная о контейнере IoC вашего приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...