IoC - динамическая композиция экземпляров объекта

Есть ли способ использовать IoC, MEF [Imports] или другое решение DI для составления зависимостей «на лету» во время создания объекта, а не во время компоновки?

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

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

Возможно, я перепутал некоторую терминологию, но моя цель - избежать написания фреймворка для «подключения всех событий» к экземпляру объекта и использовать что-то вроде MEF для [Export] обработчиков (зависимостей), которые придерживаются очень определенный интерфейс и [ImportMany] их в экземпляр объекта, чтобы мои экспортные данные вызывались, если сборки присутствуют при запуске приложения. Так что, возможно, все объекты все еще могут быть составлены при запуске приложения, но я хочу, чтобы система находила и вызывала их все по мере создания и уничтожения объекта.


person Joshua Starner    schedule 10.06.2010    source источник


Ответы (1)


Обычно способ создания экземпляров на лету в среде DI / IoC заключается в использовании абстрактных фабрик. Фабрика - это единственный тип класса, которому разрешено напрямую взаимодействовать с контейнером для разрешения зависимостей при создании экземпляра нового объекта.

public interface IWidgetFactory
{
    IWidget CreateWidget(...);
}

public class MyIocWidgetFactory : IWidgetFactory
{
    private IoCContainer container;

    public MyIocWidgetFactory(IoCContainer container)
    {
        if (container == null)
            throw new ArgumentNullException("container");
        this.container = container;
    }

    public IWidget CreateWidget(...)
    {
        // Assumes that the container is configured to create transient objects
        // for IWidget, not a singleton.
        return container.Resolve<IWidget>();
    }
}

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

На этом этапе вы просто передаете IWidgetFactory тому, что нужно для создания виджета во время выполнения. Вот и все - вы сохранили DI.

person Aaronaught    schedule 10.06.2010
comment
Некоторые DI-контейнеры также поддерживают создание фабрик. Вы можете ввести, например, Func ‹IWidget› -делегат. Затем DI-контейнер создает фабрику для экземпляров IWidget. Например, в Autofac: nblumhardt.com/2010/01/the-relationship-zoo < / а> - person Gamlor; 14.06.2010
comment
Мне любопытно, нужно ли вам внедрить эту фабрику в класс, который изначально хотел создать объект? Или это нужно создать? - person stuartmclark; 06.02.2013
comment
@stuartmclark: вы вводите фабрику. Поэтому это IWidgetFactory, а не какой-то конкретный тип. Некоторые контейнеры IoC действительно способны автоматически генерировать фабрики, поэтому вам даже не нужны указанные выше типы, вы можете просто ввести Func<IWidget> или Lazy<IWidget>. - person Aaronaught; 07.02.2013
comment
Ах да, я не ожидал создания всех этих фабрик! Вы знаете, есть ли у Unity такая возможность? Или мне стоит посмотреть на другие контейнеры IoC? Спасибо - person stuartmclark; 07.02.2013
comment
@stuartmclark: Прошло много времени с тех пор, как я приблизился к Unity. Замок Виндзор делает это хорошо, и у Ninject есть расширение для этого; это единственные два, о которых я могу сказать что-нибудь определенное. - person Aaronaught; 08.02.2013
comment
Узнал, как это сделать в единстве: IUnityContainer result = new UnityContainer (); result.RegisterType ‹Func ‹IDriver›› (новый InjectionFactory ((i = ›new Func ‹IDriver› (() =› result.Resolve ‹IDriver› ())))); Где IDriver - это класс, который вы хотите создать. - person stuartmclark; 10.02.2013