Контейнеры IoC / DI, фабрики и создание типов среды выполнения - PullRequest
7 голосов
/ 09 марта 2012

Я недавно узнал об основах DI Guice и Ninject и хотел использовать их в некоторых моих новых проектах.

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

Рассмотрим этот пример:

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

Хотя легко связать View главного окна с Presenter / ViewModel, а затем связать его с логикой домена, я не понимаю, как чисто (в смысле IoC) достичь следующие задачи:

  • Динамически создавать конкретный элемент управления пользовательского интерфейса (например, IGreenBoxView, IRedImageView <- <code>JConcreteGreenBoxView, JConcreteRedImageView) без использования какого-либо шаблона поиска службы (например, повторный запрос от IoC )
    • В зависимости от этого создайте новую модель, презентатор и экземпляр представления
  • Аналогично, создайте новое конкретное диалоговое окно, например, JOptionPane во время выполнения

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

Итак - как мне сделать это правильно?

Ответы [ 2 ]

4 голосов
/ 09 марта 2012

Если вы можете повторно использовать элементы управления, тогда вы можете делать инъекции в конструктор там, где вы их используете. В противном случае вы должны ввести фабрику:

public interface IControlFactory 
{
     IGreenBoxView CreateGreenBoxView();
     IRedImageView CreateRedImageView();
}

и введите его туда, где вам нужно создать этот элемент управления.

Реализация идет к конфигурации контейнера. Там вы можете внедрить контейнер в реализацию. Некоторые контейнеры позволяют реализовать эту фабрику автоматически. например, в Ninject:

Bind<IControlFactory>().ToFactory();

См. https://github.com/ninject/ninject.extensions.factory/wiki

0 голосов
/ 11 июля 2013

Для тех, кто хочет делать подобные вещи с Unity (вместо Ninject), я создал расширение, которое позволяет вам создавать фабрики даже без объявления интерфейсов: UnityMappingFactory @ GitHub

Вы просто добавляете сопоставления там, где вы регистрируете классы во время обычного процесса начальной загрузки ...

//make sure to register the output...
container.RegisterType<IImageWidgetViewModel, ImageWidgetViewModel>();
container.RegisterType<ITextWidgetViewModel, TextWidgetViewModel>();

//define the mapping between different class hierarchies...
container.RegisterFactory<IWidget, IWidgetViewModel>()
.AddMap<IImageWidget, IImageWidgetViewModel>()
.AddMap<ITextWidget, ITextWidgetViewModel>();

Затем вы просто объявляете интерфейс фабрики сопоставления в конструкторе для CI и используете его Create () метод ...

public ImageWidgetViewModel(IImageWidget widget, IAnotherDependency d) { }

public TextWidgetViewModel(ITextWidget widget) { }

public ContainerViewModel(object data, IFactory<IWidget, IWidgetViewModel> factory)
{
    IList<IWidgetViewModel> children = new List<IWidgetViewModel>();
    foreach (IWidget w in data.Widgets)
        children.Add(factory.Create(w));
}

В качестве дополнительного бонуса любые дополнительные зависимости в конструкторе отображаемых классов также будут разрешаться при создании объекта. (Также опубликовано в ответе на этот пост Stackoverflow )

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