Я пытаюсь внедрить внедрение зависимостей в относительно большие приложения, но у меня нет опыта в этом. Я изучил концепцию и несколько доступных реализаций IoC и инжекторов зависимостей, таких как Unity и Ninject. Однако есть одна вещь, которая ускользает от меня. Как мне организовать создание экземпляра в моем приложении?
Я думаю о том, что могу создать несколько конкретных фабрик, которые будут содержать логику создания объектов для нескольких типов классов. В основном это статический класс с методом, вызывающим метод Ninject Get () статического экземпляра ядра в этом классе.
Будет ли это правильный подход к внедрению внедрения зависимостей в моём приложении или я должен реализовать его по другому принципу?
c#
.net
dependency-injection
inversion-of-control
ninject
user3223738
источник
источник
Ответы:
Пока не думайте об инструменте, который собираетесь использовать. Вы можете сделать DI без контейнера IoC.
Первый пункт: у Марка Симанна есть очень хорошая книга о DI в .Net
Второе: состав корень. Убедитесь, что все настройки выполнены в точке входа в проект. Остальная часть вашего кода должна знать об инъекциях, а не о каком-либо используемом инструменте.
Третье: наиболее вероятным вариантом является использование Constructor Injection (есть случаи, когда вы этого не хотите, но не так много).
Четвертое: изучить использование лямбда-фабрик и других подобных функций, чтобы избежать создания ненужных интерфейсов / классов с единственной целью внедрения.
источник
Ваш вопрос состоит из двух частей: как правильно реализовать DI и как реорганизовать большое приложение для использования DI.
На первую часть хорошо ответил @Miyamoto Akira (особенно рекомендация прочитать книгу Марка Симанна «Внедрение зависимостей в .net». Блог Marks также является хорошим бесплатным ресурсом.
Вторая часть намного сложнее.
Хорошим первым шагом было бы просто переместить все экземпляры в конструкторы классов - не вводить зависимости, а просто убедиться, что вы вызываете только
new
конструктор.Это выделит все нарушения SRP, которые вы совершили, так что вы можете начать разбивать класс на более мелких соавторов.
Следующей проблемой, которую вы найдете, будут классы, которые полагаются на параметры времени выполнения для построения. Обычно это можно исправить, создав простые фабрики, часто с помощью
Func<param,type>
инициализации их в конструкторе и вызова их в методах.Следующим шагом будет создание интерфейсов для ваших зависимостей и добавление второго конструктора в ваши классы, кроме этих интерфейсов. Ваш конструктор без параметров обновит конкретные экземпляры и передаст их новому конструктору. Это обычно называется «B * Stard Injection» или «Dor Mans DI».
Это даст вам возможность провести некоторое модульное тестирование, и, если это было главной целью рефакторинга, возможно, вы остановитесь там. Новый код будет написан с помощью инжектора конструктора, но ваш старый код может продолжать работать так, как написано, но все же будет тестируемым.
Конечно, вы можете пойти дальше. Если вы намереваетесь использовать контейнер IOC, то следующим шагом может быть замена всех прямых вызовов
new
ваших конструкторов без параметров статическими вызовами контейнера IOC, по существу (ab) с использованием его в качестве локатора службы.Это приведет к большему количеству случаев параметров конструктора во время выполнения, чтобы работать с ними, как и раньше.
Как только это будет сделано, вы можете начать удалять конструкторы без параметров и рефакторинг для чистого DI.
В конечном итоге это будет большая работа, поэтому убедитесь, что вы решили, почему вы хотите это сделать, и расставьте приоритеты в тех частях кода, которые больше всего выиграют от рефакторинга.
источник
Во-первых, я хочу упомянуть, что вы значительно усложняете это путем рефакторинга существующего проекта, а не запускаете новый проект.
Вы сказали, что это большое приложение, поэтому для начала выберите небольшой компонент. Предпочтительно компонент «листовой узел», который не используется ничем другим. Я не знаю, каково состояние автоматического тестирования в этом приложении, но вы будете нарушать все модульные тесты для этого компонента. Так что будьте готовы к этому. Шаг 0 - написание интеграционных тестов для компонента, который вы будете изменять, если они еще не существуют. В качестве последнего средства (нет тестовой инфраструктуры; нет поддержки для ее написания), подумайте о серии ручных тестов, которые вы можете выполнить, чтобы убедиться, что этот компонент работает.
Самый простой способ заявить о своей цели для рефакторинга DI состоит в том, что вы хотите удалить все экземпляры оператора 'new' из этого компонента. Они обычно делятся на две категории:
Инвариантная переменная-член: это переменные, которые устанавливаются один раз (как правило, в конструкторе) и не переназначаются на время существования объекта. Для этого вы можете вставить экземпляр объекта в конструктор. Как правило, вы не несете ответственности за уничтожение этих предметов (я не хочу говорить здесь никогда, но вы действительно не должны нести эту ответственность).
Переменная члена-переменной / переменная метода: это переменные, которые будут собирать мусор в определенный момент времени жизни объекта. Для этого вам нужно внедрить фабрику в ваш класс, чтобы обеспечить эти экземпляры. Вы несете ответственность за утилизацию предметов, созданных на фабрике.
Ваш контейнер IoC (как бы это ни звучало) будет нести ответственность за создание экземпляров этих объектов и реализацию ваших заводских интерфейсов. Независимо от того, что использует компонент, который вы изменили, необходимо знать о контейнере IoC, чтобы он мог получить ваш компонент.
Выполнив все вышеперечисленное, вы сможете получить все преимущества, которые вы надеетесь получить от DI в выбранном вами компоненте. Сейчас самое время добавить / исправить эти юнит-тесты. Если существовали модульные тесты, вам нужно будет решить, хотите ли вы соединить их вместе, вводя реальные объекты или писать новые модульные тесты, используя макеты.
«Просто» повторите вышеупомянутое для каждого компонента вашего приложения, перемещая ссылку на контейнер IoC вверх по мере продвижения, пока только основные не должны знать об этом.
источник
Правильный подход заключается в использовании инжектора конструктора, если вы используете
тогда вы в конечном итоге с локатором службы, чем внедрение зависимости.
источник
Вы говорите, что хотите использовать это, но не говорите, почему.
DI - это не что иное, как механизм создания конкреций из интерфейсов.
Это само по себе происходит от DIP . Если ваш код уже написан в этом стиле, и у вас есть единственное место, где создаются конкреции, DI больше ничего не приносит участнику. Добавление кода структуры DI здесь просто раздувает и запутывает вашу кодовую базу.
Предполагая, что вы действительно хотите его использовать, вы обычно настраиваете фабрику / сборщик / контейнер (или что-то еще) в начале приложения, чтобы он был четко виден.
NB. Очень легко накатить свой собственный, если вы хотите, а не фиксировать Ninject / StructureMap или что-то еще. Однако, если у вас разумная текучесть кадров, он может смазать колеса, чтобы использовать признанные рамки, или, по крайней мере, написать это в таком стиле, чтобы это не было слишком сложным для обучения.
источник
На самом деле, «правильный» способ - НЕ использовать фабрику вообще, если нет абсолютно никакого другого выбора (как в модульном тестировании и некоторых моделях - для производственного кода вы НЕ используете фабрику)! Это на самом деле анти-паттерн и его следует избегать любой ценой. Весь смысл в контейнере DI состоит в том, чтобы позволить гаджету сделать всю работу за вас.
Как было сказано выше в предыдущем посте, вы хотите, чтобы ваш гаджет IoC взял на себя ответственность за создание различных зависимых объектов в вашем приложении. Это означает, что ваш DI-гаджет может создавать различные экземпляры и управлять ими. В этом весь смысл DI - ваши объекты НИКОГДА не должны знать, как создавать и / или управлять объектами, от которых они зависят. В противном случае нарушается ослабление сцепления.
Преобразование существующего приложения во все DI является огромным шагом, но, исключая очевидные трудности в этом, вы также захотите (просто чтобы сделать вашу жизнь немного проще) изучить инструмент DI, который будет автоматически выполнять большую часть ваших привязок. (ядро чего-то вроде Ninject - это
"kernel.Bind<someInterface>().To<someConcreteClass>()"
вызовы, которые вы делаете для сопоставления объявлений интерфейсов с теми конкретными классами, которые вы хотите использовать для реализации этих интерфейсов. Это те вызовы «Bind», которые позволяют гаджету DI перехватывать вызовы конструктора и предоставлять необходимые экземпляры зависимых объектов. Типичный конструктор (показанный здесь псевдокод) для некоторого класса может быть:Обратите внимание, что нигде в этом коде не было никакого кода, который создавал / управлял / выпускал экземпляр SomeConcreteClassA или SomeOtherConcreteClassB. На самом деле ни на один конкретный класс даже не ссылались. Итак ... где случилось волшебство?
В начальной части вашего приложения произошло следующее (опять же, это псевдокод, но он довольно близок к реальному (Ninject) ...):
Этот небольшой фрагмент кода говорит гаджету Ninject искать конструкторы, сканировать их, искать экземпляры интерфейсов, для которых он был настроен (это вызовы «Bind»), а затем создавать и заменять экземпляр конкретного класса везде, где это необходимо. на экземпляр ссылаются.
Есть хороший инструмент, который очень хорошо дополняет Ninject, который называется Ninject.Extensions.Conventions (еще один пакет NuGet), который сделает большую часть этой работы за вас. Не для того, чтобы отвлечься от отличного опыта обучения, через который вы будете проходить, создавая его самостоятельно, но для начала, это может быть инструментом для исследования.
Если память служит, Unity (формально от Microsoft, теперь проект с открытым исходным кодом) имеет вызов метода или два, которые делают то же самое, другие инструменты имеют аналогичные помощники.
Какой бы путь вы ни выбрали, обязательно прочитайте книгу Марка Симанна для большей части вашего обучения DI, однако, следует отметить, что даже «Великие» в мире разработки программного обеспечения (такие как Марк) могут совершать явные ошибки - Марк забыл все о Ninject в своей книге, так что вот еще один ресурс, написанный только для Ninject. У меня есть это и его хорошее чтение: Освоение Ninject для инъекций зависимости
источник
Не существует «правильного пути», но есть несколько простых принципов, которым нужно следовать:
Вот и все. Конечно, это принципы, а не законы, но если вы будете следовать им, вы можете быть уверены, что делаете DI (пожалуйста, исправьте меня, если я ошибаюсь).
Итак, как создавать объекты во время выполнения без «нового» и без знания контейнера DI?
В случае NInject существует расширение фабрики, которое обеспечивает создание фабрик. Конечно, созданные фабрики все еще имеют внутреннюю ссылку на ядро, но это не доступно из вашего приложения.
источник