Re: Dependency Injection объясните?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 11.01.09 21:03
Оценка: 34 (7)
Здравствуйте, Аноним, Вы писали:

А> В общем, хочу разобраться с этим вопросом, но никак не могу догнать что это такое и зачем оно нужно. Читал статью на MSDN-не, но мало что понял. Можете рассказать простыми словами что это за зверь и в чем преимущества его использвоания?


Смотри
Есть класс A и зависит от от класса B (использует его).
Например так:

public class A
{
  B b = new B();
  
  void foo()
  {
    //используем b
  }
}

public class B
{
}

В этом коде существует несколько проблем.
1)Невозможно тестировать класс A в отрыве от B. Если B работает с БД, то для тестов A вам понадобится база.
2)Временем жизни объекта B управляет А, нельзя например использовать один и тот же объект B в разных местах.

Чтобы победить это нам сначало надо выделить интерфейс B и назвать его IB.
public interface B
{
}

public class A
{
  IB b = new B();
  
  void foo()
  {
    //используем b
  }
}

public class B:IB
{
}

Но ни одну из наших проблем это не решило.

Теперь можно применить паттерн Service Locator. Суть этого паттерна состоит в том что имеем фабричный метод который по идентификатору возвращает нужную реализацию интерфейса. В .NET есть IServiceProvider с одном методом GetService, которому параметром передается тип.

Предположим что мы сделали хорошую реализацию IServiceProvider и у нас получился такой код
public interface B
{
}

public class A
{  
  IB b = (IB)serviceLocator.GetService(typeof(IB)); //serviceLocator - реалзиация IServiceProvider, которая по типу IB возвращает B
  
  void foo()
  {
    //используем b
  }
}

public class B:IB
{
}


Теперь временем жизни B управляет serviceLocator, A не знает о классе B. Можно через serviceLocator подпихнуть любую реализацию IB.
Этот подход называет Dependency Lookup — поиск зависимостей, это один из вариантов подхода IoC.
Но у нас появилась зависимость от serviceLocator, теперь хоть тестировать A без класса B возможно, но это потребует настройки локатора (возможно совсем нетривиальной).

Если подойти с другой стороны, то можно сделать так стобы класс A не искал зависимости сам, а получал их извне. например через конструктор, свойстви или метод.
public interface B
{
}

public class A
{  
  IB b;
  
  //Например так
  public A(IB b)
  {
    this.b = b;
  }
  
  //Или так
  public IB B { {get {return b;} set {b = value;}}

  //Или так
  public void SetB(IB b)
  {
    this.b = b;
  }
  
  void foo()
  {
    //используем b
  }
}

public class B:IB
{
}


Тогда вызывающий код дожен выгядеть примерно так
...
var a = new A(new B());
...

Теперь у нас A не зависит ни от чего, а все зависимости можно передать например через конструктор, что значительно облегчает тестирование A.
Этот подход называется Dependency Injection — инъекция зависимостей, это другой вариант IoC.
Но в большем масштабе проблемы не решает, только перемещает её на уровень выше, то есть все зависимости перемещаются в вызывающий код.

Если такой подход применить во всей программе, то в итоге метод main (или другая точка входа) будет выглядеть так:
new Program(new A(new B(new C(), new D()), new E()......)

И это мы еще не рассматривали управление временем жизни объектов.

На помощь нам приходят IoC контейнеры.
Сами IoC-контейнеры похожи на ServiceLocator, только делать чуть больше работы. При запросе объекта какого-то типа у контейнера он решает объект какого типа вернуть. Для какждого типа, зарегистрированного в IoC-контейнере, есть карта зависимостей, то есть описание какого типа параметры надо передавать в конструктор, каким свойствам надо присваивать и какие методы вызывать чтобы инъектировать зависимости в объект. Карта зависимостей задается внешне или получается с помощью рефлексии.
Кроме того контейнер содержит ассоциации для какого запрошенного идентификатора объект какого типа надо вернуть. В качестве идентификатора чаще всего используется тип. Для каждой зависимости запрошенного объекта, контейнер создает дургой объект у которого тоже могут быть зависимости, для них эта операция вызывается рекурсивно.
В принципе контейнер не обязан каждый раз создавать объект, он может управлять его временем жизни.

Хороший IoC контейнер должен возможности чтобы код выше можно было переписать так:
container.Resolve<Program>();

А также потребуется где-то задать параметры контейнеру, чтобы он по запросу объекта типа IB возвращал объект типа B, для IA возвращал A и так далее, а также задать параметры упрявления временем жизни объетов.

Кроме того IoC-контейнер можно использовать как Service Locator, но такого надо избегать.


Для .NET существует множество контейнеров. От MS — Unity, даже версия для Silverlight уже появилась, autofac, Castle Windsor, StructureMap и Spring.NET.
Spring.NET — клон java библиотеки со всеми вытекающими. Куча конфигов в XML, огромное число классов в библиотеке, слабое использование возможностей .NET, лично меня от него воротит после использования достаточно легковесного unity.

Кроме того MS ведет разработку библиотеки MEF. В ней применяются принципы IoC, но для более крупных компонент, а также возможности менять набор компонент в Runtime. MEF будет в составе .NET 4.0
http://files.rsdn.org/67312/MVP_Horizontal_Mini.png
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.