Лучший способ начать работу с Ninject - начать с малого.Ищите new
.
Где-то в середине вашего приложения вы создаете класс внутри другого класса.Это означает, что вы создаете зависимость .Внедрение зависимостей означает, что передает эти зависимости, обычно через конструктор, вместо встраивания их.
Скажем, у вас есть такой класс, который используется для автоматического созданияконкретный тип заметки в Word.(Это похоже на проект, который я недавно делал на работе.)
class NoteCreator
{
public NoteHost Create()
{
var docCreator = new WordDocumentCreator();
docCreator.CreateNewDocument();
[etc.]
WordDocumentCreator
- это класс, который обрабатывает особенности создания нового документа в Microsoft Word (создать экземпляр Word,так далее.).Мой класс, NoteCreator
, зависит от WordDocumentCreator
, чтобы выполнить свою работу.
Беда в том, что если когда-нибудь мы решим перейти к превосходному текстовому процессору, я должен найтивсе места, где создается экземпляр WordDocumentCreator
, и вместо этого меняйте его на экземпляр WordPerfectDocumentCreator
.
Теперь представьте, что я изменил свой класс так:
class NoteCreator
{
WordDocumentCreator docCreator;
public NoteCreator(WordDocumentCreator docCreator) // constructor injection
{
this.docCreator = docCreator;
}
public NoteHost Create()
{
docCreator.CreateNewDocument();
[etc.]
Мой код еще не найдентак сильно изменилось;все, что я сделал в методе Create
, удалил строку с new
.Но сейчас я делаю инъекцию своей зависимости.Давайте сделаем еще одно небольшое изменение:
class NoteCreator
{
IDocumentCreator docCreator;
public NoteCreator(IDocumentCreator docCreator) // change to interface
{
this.docCreator = docCreator;
}
public NoteHost Create()
{
docCreator.CreateNewDocument();
[etc.]
Вместо ввода бетона WordDocumentCreator
, я извлек интерфейс IDocumentCreator
с помощью CreateNewDocument
метод.Теперь я могу передать любой класс, который реализует этот интерфейс, и все, что NoteCreator
должен сделать, это вызвать метод, о котором он знает.
Теперь сложная часть ,Теперь у меня должна быть ошибка компиляции в моем приложении, потому что где-то я создавал NoteCreator
с конструктором без параметров, которого больше не существует .Теперь мне нужно вытащить эту зависимость .Другими словами, я выполняю тот же процесс, что и выше , но теперь я применяю его к классу, который создает новый NoteCreator
.Когда вы начнете извлекать зависимости, вы обнаружите, что они имеют тенденцию «пузыриться» до корня вашего приложения, которое является only местом, где у вас должна быть ссылка на ваш контейнер DI (например, Ninject).
Другая вещь, которую мне нужно сделать, это настроить Ninject .Важнейшим элементом является класс, который выглядит следующим образом:
class MyAppModule : NinjectModule
{
public override void Load()
{
Bind<IDocumentCreator>()
.To<WordDocumentCreator>();
Это говорит Ninject, что когда я пытаюсь создать класс, который где-то внизу требует IDocumentCreator
, он должен создать WordDocumentCreator
и используйте это.Процесс, через который проходит Ninject, выглядит примерно так:
- Создание приложения
MainWindow
.Его конструктор требует NoteCreator
. - ОК, поэтому создайте NoteCreator.Но его конструктор требует
IDocumentCreator
. - Моя конфигурация говорит, что для
IDocumentCreator
я должен использовать WordDocumentCreator
.Итак, создайте WordDocumentCreator
. - Теперь я могу передать
WordDocumentCreator
NoteCreator. - И теперь я могу передать
NoteCreator
MainWindow
.
Красота этой системы тройная.
Во-первых, если вам не удастся что-то настроить, вы сразу узнаете, потому что ваши объекты создаются сразу после запуска приложения.Ninject выдаст вам полезное сообщение об ошибке, в котором будет сказано, что ваш IDocumentCreator
(например) не может быть разрешен.
Во-вторых, если позднее управление предписывает пользователю превосходный текстовый процессор, все, что вам нужно сделать, это
- Напишите
WordPerfectDocumentCreator
, который реализует IDocumentCreator
. - Измените
MyAppModule
выше, связывая IDocumentCreator
с WordPerfectDocumentCreator
вместо.
В-третьих, если я хочу проверить свой NoteCreator
, мне не нужно передавать real WordDocumentCreator
(или что-то еще, что я использую).Я могу передать в поддельные один.Таким образом, я могу написать тест, который предполагает, что my IDocumentCreator
работает правильно, и проверяет только движущиеся части в самом NoteCreator
.Моя фальшивка IDocumentCreator
ничего не сделает, только вернет правильный ответ, и мой тест убедится, что NoteCreator
делает правильные вещи.
Для получения дополнительной информации о том, как структурировать ваши приложения таким образом, взгляните на недавнюю книгу Марка Симанна, Внедрение зависимостей в .NET . К сожалению, он не охватывает Ninject, но охватывает ряд других структур DI и говорит о том, как структурировать ваше приложение так, как я описал выше.
Также взгляните на Эффективно работающий с устаревшим кодом , автор Michael Feathers. Он говорит о стороне тестирования, описанной выше: как разбивать интерфейсы и передавать фальшивку с целью изоляции поведения и проверки его.