как писать библиотеки, не заставляя пользователей использовать контейнер IOC библиотеки

Короткий вопрос: Учитывая, что библиотека гарантируетиспользование определенного контейнера IOC для своих внутренних компонентов, когда приложение использует эту библиотеку, учитывая, что приложение гарантируетиспользование контейнера IOC для связывания своих зависимостей, учитывая, что два контейнеры разные, как они могут хорошо сочетаться друг с другом?

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

Вот длинный вопрос:

Этот вопрос, кажется, задавался в разных формах и формах раньше на SO, но я не могу найти ответ, который мне нужен, поэтому я собираюсь попробовать это с гипотетическим _over_simplified_ конкретным примером. Мы хотим написать библиотеку для ведения журнала, которую пользователи смогут включать в виде пакета в свое решение, чтобы получить функциональность ведения журнала из коробки. Открытые интерфейсы, предоставляемые библиотекой, следующие..

открытый интерфейс ILogger {}

открытый интерфейс ITarget {}

Конкретные реализации:

внутренний класс Logger: ILogger { public Logger(ITarget target) {}}

внутренний класс FileTarget : ITarget {}

Требования: если пользователь включает наш пакет и определяет класс со свойством типа ILogger или имеет аргумент ctor типа ILogger затем наша библиотека отвечает за внедрение конкретной реализации этого интерфейса в определяемый пользователем класс.По умолчанию внедренный регистратор будет обращаться к файловой системе для ведения журнала, потому что реализация по умолчанию ITarget, внедренная в реализацию ILogger, — это FileTargetнашей библиотекой.

Если пользователь решит написать класс, реализующий интерфейс ITarget, наша библиотека будет использовать его для внедрения в класс Logger, а не использовать его по умолчанию FileTarget. ] выполнение.

Итак, что я хочу продемонстрировать, так это их двунаправленную зависимость.

  1. Наша библиотека зависит от пользовательских сборок, поскольку ей необходимо сканировать пользовательские сборки, чтобы загрузить любые точки расширения (например, реализацию ITarget) и внедрить их в свои собственные объекты перед любыми реализациями по умолчанию.

  2. Пользовательские сборки зависят от библиотеки, поскольку, если пользователь решит определить класс с интерфейсом ILoggerв качестве зависимости, то этот пользовательский объект должен получить конкретную ссылку на этот интерфейс, предоставляемую во время выполнения наша библиотека.

Простое решение состоит в том, что если пользователь и наша библиотека используют один и тот же контейнер IOC, тогда проблема решена. Но это сильное предположение. Что я хочу сделать, так это

  1. использовать контейнер IOC с библиотекой, которая лучше всего соответствует требованиям библиотеки, в моем случае это Ninject.
  2. Во время выполнения каким-то образом предоставьте пользователю механизм вызова моей библиотеки через какой-либо API, который обеспечит запуск Ninject, сканирование пользовательских сборок и подключение всего с учетом всех точек расширения.

Пока все хорошо, это вполне достижимо, но здесь начинается сложная часть.

  • если пользователь также использует Ninject, то проблема решается автоматически, так как Ninject уже знает, как разрешать интерфейсы, живущие в нашей библиотеке. Но что, если пользователь решит использовать выбранный им IOC-контейнер?

Я почти хочу определить какую-то функциональность дочернего контейнера в библиотеке с таким интерфейсом

public interface IDependencyResolvingModule { T Get(); []T ПолучитьВсе(); }

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

Я хочу, чтобы IOC-контейнер пользователя имел некоторую функциональность: если он не может разрешить зависимость (например, ILogger), он должен подключиться к реализации IDependencyResolvingModuleи запросить зависимость.

Таким образом, наша библиотека может использовать свой выбор контейнера IOC, а пользовательский код получает способ разрешения зависимостей, о которых его контейнер IOC не знает. Разве это решение не сработало бы, если бы контейнеры IOC каким-то образом обеспечивали функциональность для регистрации одноэлементных экземпляров любых IDependencyResolverModules, найденных в сборках в каталоге исполняемой сборки, и когда они не могут разрешить тип, запросите любой из одноэлементные модули?

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

8
задан afif 26 March 2012 в 02:17
поделиться