Как развернуть реализацию IoC Framework

Я изучаю IoC, Injection Dependency и т.д. и наслаждаюсь процессом. Преимущества развязки и программирования для интерфейсов для меня, без проблем.

Однако мне действительно не нравится привязываться к определенной структуре, например Unity или Autofac или Windsor, потому что я все еще участвую и еще не решил, что лучше для моих целей.

Итак, как я могу обернуть что-то вроде Unity, чтобы я мог легко поменять местами в Windsor позже? (или что-то еще). И вы не смеете сказать, что используйте другой, чтобы ввести первый:)

Спасибо!

R.

P.s. Я пометил Unity как это мое личное предпочтение (я просто lurve Entlib).

Ответы

Ответ 1

Вы можете попробовать сделать абстракцию из контейнера, объявив IContainer с помощью Resolve и Register. Я сделал это пару раз. Затем вы должны реализовать Контейнер: IContainer и инкапсулировать реальный контейнер IoC своей абстракцией. Я попробовал это с Unity и Castle Windsor.

Но, в конце концов, я понял, что это действительно чрезмерная инженерия. Затем я понял, что пытался абстрагироваться от абстракции, но для того, чтобы построить еще одну абстракцию. Это может быть хорошо, чтобы изучить концепцию, но это была настоящая боль в шее в реальном проекте. Я бы настоятельно рекомендовал отказаться от абстракции от контейнера IoC. Если вы правильно используете принцип DI, это будет довольно легко изменить ваш контейнер в любом случае.

Код выглядит сложным, например

//I did this mess with Service Locator
var t = ContainerService.Instance.Resolve<IMyType>();
//others could go further with same Service Locator
var t = IoCFactory.Instance.CurrentContainer.Resolve<IMyType>();

//better way, use --> IoC and DI <--
//when a program starts, or a new instance of the context created
var t = Container.Resolve<IMyType>() //this lives at the bottom of the stack
//and then you just pass IMyType to the constructor of other types    
//you don't need to call Resolve again in the logical cycle

См. этот пост от Ayende.

Да, они абстрагировали Inversion of Control Container. Я думаю, что если вам нужно это сделать, довольно ясно, что вы действительно не понимаете, что такое IoC.

Ответ 2

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

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

Ответ 3

Контейнер DI должен ссылаться только на Корень композиции. Все остальные модули не должны иметь ссылки на контейнер.

-Mark Seemann (автор Инъекция зависимостей в .NET)

Другими словами, вам нужно изменить только один класс, если вы меняете контейнеры DI.

Встраивание конструктора - это, как правило, правильный путь, как говорили другие. Вы можете вводить интерфейсы factory или Func<T>, если вам нужно создавать объекты "на лету".

Я также предлагаю избегать конфигурации XML, когда это возможно.

Ответ 4

Как уже говорили некоторые другие люди, предпочитайте инъекцию конструктора. Это решит многие из ваших проблем.

Если ваши классы имеют прямые зависимости от самого контейнера IoC, он имеет тенденцию быть вариантом использования шаблона локатора службы (anti-). В этом конкретном случае изолируйте типы, которые разрешаются с помощью локатора служб, и отрисуйте это динамическое разрешение с интерфейсом factory. Так, например, замените это:

public class Foo
{
    private MyIoCContainer _container;

    public Foo(MyIoCContainer container)
    {
        this._container = container;
    }


    public void DoSomething()
    {
        // have to do this at runtime for whatever reason
        var myObj = this._container.Resolve<ISomeType>();

        myObj.DoSomething();
        myObj.DoSomethingElse();
    }
}

с этим:

public class Foo
{
    private IObjFactory _provider;

    public Foo(IObjFactory _provider)
    {
        this._provider = provider;
    }


    public void DoSomething()
    {
        var myObj = _provider.GetObj();

        myObj.DoSomething();
        myObj.DoSomethingElse();
    }
}

public interface IObjFactory
{
    ISomeType GetObj();
}

Теперь у вас есть IObjFactory, который может инкапсулировать динамический, исполняемый характер создания объектов, реализующих ISomeType. Если вы создаете много разных типов объектов из локатора контейнера/службы, то у вас должно быть как минимум несколько интерфейсов *Factory (в соответствии с Разделение интерфейса Принцип).