Что контейнер МОК фактически делает для меня здесь? - PullRequest
5 голосов
/ 09 августа 2011

Итак, я полностью рефакторился для внедрения в конструктор, и теперь у меня есть класс начальной загрузки, который выглядит примерно так:

var container = new UnityContainer();
container.RegisterType<Type1, Impl1>();
container.RegisterType<Type2, Impl2>();
container.RegisterType<Type3, Impl3>();
container.RegisterType<Type4, Impl4>();

var type4Impl = container.Resolve((typeof)Type4) as Type4;
type4Impl.Run();

Я смотрел на него секунду, прежде чем понял, что Unity действительно не делает ничего особенного для меня. Если не брать в расчет сигары, вышеприведенное можно записать так:

Type1 type1Impl = Impl1();
Type2 type2Impl = Impl2();
Type3 type3Impl = Impl3(type1Impl, type2Impl);
Type4 type4Impl = Impl4(type1Impl, type3Impl);
type4Impl.Run();

Рефакторинг с инжекцией в конструктор великолепен и действительно открывает возможность тестирования кода. Однако я сомневаюсь в полезности Unity здесь. Я понимаю, что могу использовать платформу ограниченным образом (то есть не вводить контейнер где-либо, конфигурировать в коде, а не в XML, не использовать возможности управления временем жизни), но мне не удается понять, как это на самом деле помогает в этом примере , Я прочитал более одного комментария с мнением, что DI лучше просто использовать в качестве шаблона, без контейнера. Это хороший пример такой ситуации? Какие еще преимущества дает это решение, которое я упускаю?

Ответы [ 7 ]

6 голосов
/ 09 августа 2011

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

Если вы обнаружите, что обращаетесь к контейнеру, когда извлекаете объект, то вы действительно следуете шаблону Service Locator.

3 голосов
/ 09 августа 2011

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

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

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

1 голос
/ 10 августа 2011

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

Однако есть некоторые вещи, которые IoC может упростить:

  • Ленивая инициализация. Autofac и некоторые другие (не уверены в Unity) могут обнаружить конструктор, который принимает Func<IMyDependency> и, при регистрации для IDependency, автоматически сгенерирует соответствующий метод фабрики. Это уменьшает фронтальную загрузку, часто требуемую в системе DI, где множество больших объектов, таких как репозитории, должны быть инициализированы и переданы в объект верхнего уровня.
  • Сокрытие зависимостей. Скажем, класс A должен создавать экземпляр класса B, а B - C, но A не должен знать о C. Может быть, даже класс Z, создавший A, не может даже знать о C. Это то, для чего были созданы IoC; бросьте A, B и C в контейнер, встряхните его и разрешите полностью гидратированный B, чтобы дать A, или фабричный метод, который может быть введен в A (автоматически) и который A может использовать для создания всех ссылок B оно хочет.
  • Простое «синглтонирование». Вместо того чтобы создавать и использовать статический синглтон, IoC можно настроить создание и возврат одного и только одного экземпляра любой зарегистрированной зависимости, независимо от того, сколько раз запрашивается эта зависимость. Это позволяет разработчику превращать любой обычный класс экземпляра в одноэлементный для использования в контейнере без необходимости изменения кода самого класса.
1 голос
/ 09 августа 2011

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

public interface IFoo { }
public interface IBar { IFoo FooImpl { get; set; } }
public interface IBaz { IBar BarImpl { get; set; } }
public interface IBat { IBaz BazImpl { get; set; } }

По мере того, как ваш объектный граф растет и зависимости вкладываются все дальше и дальше вниз по графику, вам придется предоставить все дерево:

var bat = new Bat{
        BazImpl = new BazImpl() {
                    BarImpl = new BarImpl() {
                        FooImpl = new FooImpl()
                        }
                    }
                };

Однако, если вы правильно используете контейнер, все это разрешение зависит от того, что вы зарегистрировали:

var bat = container.Resolve<IBat>()
1 голос
/ 09 августа 2011

См. мой пост здесь для подробного ответа на этот вопрос.

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

1 голос
/ 09 августа 2011

Разница в том, что вы делаете внедрение зависимостей вместо того, чтобы Unity выполнял внедрение зависимостей.В вашем примере вы должны знать, какие типы должны быть созданы, связывая ваш код с этими типами.Теперь вам нужно знать в своем коде, что Impl1 должен создаваться всякий раз, когда вам нужен Type1.

0 голосов
/ 09 августа 2011

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

Значение использования DI-фреймворка очень быстро возрастает по мере усложнения графа зависимостей.

...