Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Урок 2. Dependency Injection






Цель урока: Изучение DI (Dependency Injection). Пример на Ninject и Unity (Autofac, Winsor).

Во многих случаях, один и тот же экземпляр класса используется в вашем приложении в разных модулях. Простым способом реализации является применение шаблона Одиночка (Singleton).

Но рассмотрим эту ситуацию с другой стороны. Так как данный объект создается при первом обращении к нему, мы не можем контролировать его время жизни. При модульном тестировании (unit-test) нет необходимости использовать этот объект (или это может быть невозможно). Чтобы избежать этого, мы не напрямую вызываем объект, а через интерфейс. И реальный экземпляр класса, и экземпляр-заглушка для тестирования будут реализовывать этот интерфейс. А логику создания мы поручаем DI-контейнеру.

Например, до использования сервиса.

Опишем пару классов, интерфейс IWeapon с методом Kill, два класса реализации Bazuka и Sword, и класс Warrior, который пользуется оружием:

public interface IWeapon

{

void Kill();

}

public class Bazuka: IWeapon

{

public void Kill()

{

Console.WriteLine(" BIG BADABUM! ");

}

}

public class Sword: IWeapon

{

public void Kill()

{

Console.WriteLine(" Chuk-chuck");

}

}

public class Warrior

{

readonly IWeapon Weapon;

 

public Warrior(IWeapon weapon)

{

this.Weapon = weapon;

}

 

public void Kill()

{

Weapon.Kill();

}

}

Используем это:

class Program

{

static void Main(string[] args)

{

Warrior warrior = new Warrior(new Bazuka());

warrior.Kill();

Console.ReadLine();

}

}

Читаем между строк. Создаем воина и даем ему базуку, он идет и убивает. В консоли получаем:

BIG BADABUM!

Заметим, что у нас нет проверки на null в строке

 

Weapon.Kill();

 

Что здесь некоректно? Воин не знает, есть ли у него оружие, и выдачей оружия занимается не отдельный модуль, а главная программа.

Суть DI – поручить выдачу оружия другому модулю.

 

Подключаем Ninject:

 

Install-Package Ninject

 

Создаем модуль, который занимается выдачей оружия:

 

public class WeaponNinjectModule: NinjectModule

{

public override void Load()

{

this.Bind< IWeapon> ().To< Sword> ();

}

}

 

Что буквально значит: «если попросят оружие – то выдайте мечи».

 

Создаем «сервис-локатор» и пользуемся оружием:

class Program

{

public static IKernel AppKernel;

 

static void Main(string[] args)

{

AppKernel = new StandardKernel(new WeaponNinjectModule());

 

var warrior = AppKernel.Get< Warrior> ();

 

warrior.Kill();

 

Console.ReadLine();

}

}

 

Как видно, объект warrior мы создаем не с помощью конструкции new, а через AppKernel.Get< > (). При создании AppKernel, мы передаем в качестве конструктора модуль, отвечающий за выдачу оружия (в данном случае это меч). Любой объект, который мы пытаемся получить через AppKernel.Get, будет (по мере возможности) проинициализирован, если существуют модули, которые знают, как это делать.

 

Другой момент применения, когда объект Warrior не берет с собой оружие каждый раз, а при не обнаружении оного обращается к сервису локатору и получает его:

 

public class OtherWarrior

{

private IWeapon _weapon;

 

public IWeapon Weapon

{

get

{

if (_weapon == null)

{

_weapon = Program.AppKernel.Get< IWeapon> ();

}

return _weapon;

}

}

 

public void Kill()

{

Weapon.Kill();

}

}

 

Исполняем:

var otherWarrior = new OtherWarrior();

otherWarrior.Kill();

 

Наш воин получает оружие по прямым поставкам – супер!

 

В Ninject есть еще одна очень хорошая деталь. Если свойство (public property) помечено [Inject], то при создании класса через AppKernel.Get< > () – поле инициализуется сервисом-локатором:

 

public class AnotherWarrior

{

[Inject]

public IWeapon Weapon { get; set; }

 

public void Kill()

{

Weapon.Kill();

}

}

 

var anotherWarrior = AppKernel.Get< AnotherWarrior> ();

anotherWarrior.Kill();

 

Unity

Абсолютно всё то же:

· Установка

Install-Package Unity

· Инициализация сервиса локатора (Container)

Container = new UnityContainer();

· Регистрация типа

Container.RegisterType(typeof(IWeapon), typeof(Bazuka));

· Получение объекта и использование:

var warrior = Container.Resolve< Warrior> ();

warrior.Kill();

· Кроме того, у Unity есть класс-одиночка (Singleton) ServiceLocator, который регистрирует контейнер и позволяет получить доступ к сервисам из любого места.

var serviceProvider = new UnityServiceLocator(Container);

ServiceLocator.SetLocatorProvider(() => serviceProvider);

 

· Хитрый OtherWarrior теперь так получает оружие:

 

public class OtherWarrior

{

private IWeapon _weapon;

 

public IWeapon Weapon

{

get

{

if (_weapon == null)

{

_weapon = ServiceLocator.Current.GetInstance< IWeapon> ();

}

return _weapon;

}

}

 

public void Kill()

{

Weapon.Kill();

}

}

 






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.