Как отделить реализацию IoC Framework - PullRequest
15 голосов
/ 06 января 2012

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

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

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

Спасибо!

R.

Ps Я пометил Unity, так как это мое текущее личное предпочтение (Я просто перевожу Энтлиба).

Ответы [ 5 ]

18 голосов
/ 06 января 2012

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

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

18 голосов
/ 06 января 2012

Вы, конечно, можете попытаться сделать абстракцию из контейнера, объявив IContainer, скажем, Resolve и Register.Я делал это пару раз.Затем вы могли бы реализовать Container: 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.

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

9 голосов
/ 06 января 2012

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

- Марк Зееманн (автор Внедрение зависимостей в .NET )

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

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

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

4 голосов
/ 06 января 2012

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

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

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 (в соответствии с Принципом разделения интерфейсов ).

3 голосов
/ 06 января 2012

Ознакомьтесь с библиотекой Common Service Locator (github). (Ранее расположен на CodePlex ).

...