SlideShare a Scribd company logo
Андрей Кулешов
Деловые решения
Про что мы говорим?
 В первую очередь – про подход к взаимодействую
  между классами и объектами в мире объектно-
  ориентированного программирования
 Во вторую очередь – про существующие шаблоны
  реализации этого подхода
 И только в третью очередь – про сторонние
  компоненты - IoC-контейнеры
Спагетти-код и лазанья-код
 Спагетти-код – это код, представляющий собой одну
  большую хаотичную мешанину классов, отвечающих за
  все функции вашего приложения
 Лазанья-код - это код, представляющий собой
  несколько отдельных маленьких хаотичных мешанин
  классов – слоёв, взаимодействующих друг с другом по
  строго определенным контрактом, не зависящим от
  реализации.
 Классическая модель – всем известная уровень
  данных, уровень бизнес-логики, уровень
  пользовательского интерфейса. Но их может быть
  больше.
Формирование слоёв
 Слой – группа классов, имеющих схожее
  назначение
 Взаимодействие между слоями идёт через «швы»
 Взаимодействие через швы должны идти только
  через контракты (интерфейсы или абстрактные
  классы), а не конкретные реализации
 И этим вы сможете добиться, что маленький
  локальный хаос внутри конкретного слоя
  останется таковым, а не разрастется на всё
  приложение
Что такое «зависимость» («dependency»)

 Что-то сильно внешнее по отношению к нашему классу,
 медленное, тяжелое в настройке, а также просто ещё не
 готовое.. В первую очередь это:
 - файловая система;
 - база данных
 - сеть;
 - сервис, который коллега послезавтра обязательно
 допишет;
 - система старта ракетных двигателей.

 Ваш класс должен зависеть от абстракций, а не от
 конкретных реализаций
Классическая работа с
зависимостями:
 Когда вы напрямую создаёте экземпляр объекта в
 вашем классе - вы контролируете его
 IDbConnection connection =
     new SqlServerConnection(“source=localhost”);
 Контролируете, когда он создается, какие параметры
  принимает, когда он больше не нужен и его надо
  удалять
 Контролируете, что создается именно этот тип объекта,
  а не схожий по функиональности
 То есть всерьез декларируете, что класс, который
  считает сумму заказов в вашей корзине покупок,
  должен знать о балансированной ферме Sql Server, и
  том, какой таймаут допустим при работе с ней
Демо
Классический контроль
Inversion of Control
Инвертированный контроль:
 При использование IoC – ваш класс больше не
 имеет контроля над зависимостями
 public class Basket{
     public Basket(IDbConnection connection){
           //save connection...
     }
 }
 Об этом заботится кто-то снаружи этого класса
 Кто-то, кто знает больше и лучше. Кто на этом
  специализируется
 А ваш класс перестаёт зависеть от конкретной
  реализации
Что злобные враги
рассказывают вам об DI
 DI – это «локатор сервисов»
  нагло врут
 DI нужен только для тестирования
  нагло врут
 DI нужен только для позднего связывания
  нагло врут
 Для архитектуры в стиле DI нужен IoC контейнер
  нагло врут
Как всё начиналось
 Одним из первых паттернов, способствующих
  уменьшению связности между классами, был
  локатор сервисом (Service Locator)
  До сих пор он у многих ассоциируется с понятием
  Dependency Injection
 Однако сейчас он считается скорее анти-
  паттерном, не рекомендуемым к применению
 Основная проблема – неявные требования при
  работе с классами, использующими локаторы
  сервисов
Демо
Локатор сервисов
Как внедрять зависимости?
Основное требование – объект всегда должен быть в рабочем
состоянии.
Если зависимость неявная – значит, она должна быть
необязательной.
Ни при каких обстоятельствах не должен происходить
NullReferenceException
Четыре стандартных подхода:
 Вставка через конструктор (constructor injection)
 Вставка через свойства (property injection)
 Вставка через параметры методов (methods injection)
 Использование общедоступного внешнего контекста
  (ambient context)
Constructor Injection
 Используется для всех зависимостей, без которых
    классу не обойтись
public class GreetingsManager
{
    private readonly IWriter _writer;

     public GreetingsManager(IWriter writer)
     {
         if(writer == null)
             throw new ArgumentNullException("writer",
                       "Writer was not provided");
         _writer = writer;
     }
        ...
        ...
}
Демо
Constructor Injection
Property Injection
 Используется для всех              public class GreetingsManager
  опциональных                       {
  зависимостей                           private IWriter _writer = null;
 Или для зависимостей, у                public IWriter Writer
  которых есть локальная                 {
  реализация по умолчанию                    get
                                             {
 То есть когда в 90% случаев                    if(_writer == null)
  подойдет стандартная, но                       {
  иногда нужно иметь                                 _writer = new ConsoleWriter();
  возможность заменить…                          }
 Возможно, иногда также                         return _writer;
  подойдет NullObject –                      }
  паттерн, то есть объект-                   set
   заглушка, не выполняющая                  {
   действий                                      if(_writer != null)
                                                     throw new
(пример не совсем чистый – в                            InvalidOperationException(
примерах кода GreetingsManager и                        "You can't change writer
ConsoleWriter находятся в разных                        implementation after usage");
сборках. Локальная реализация
должна принадлежать той же сборке)               _writer = value;
                                             }
                                         }
Демо
Property Injection
Method Injection
 Имеет смысл использовать, если зависимость меняется от
  метода к методу
 Также удобно использовать при написании собственного
  фреймворка, когда мы хотим передать получателю
  (например, стороннему плагину) некий контекст, хранящий
  информацию о вызывающей стороне
 public string RunPlugin(SomeValue value, ISomeContext context)
 {
     if (context == null)
     {
         throw new ArgumentNullException("context");
     }
     return context.Name;
 }
Демо
Method Injection
Ambient Context
 Некий контекст, который с большей долей
  вероятности может понадобиться (а может и не
  понадобится) в большинстве разрабатываемых
  классов
 Чтобы не передавать зависимость в каждый из
  разрабатываемых классов – применяем
  общедоступный Ambient Context
 Пример использования – текущее время (но с
  возможностью подмены значения)
Демо
Ambient Context
Абстрактные фабрики
 Удобно использовать для инстанциирования
  короткоживущих объектов
 Удобно использовать для инстанциирования
  объектов, когда тип создаваемых объектов зависит
  от параметров, известных только во время
  выполнения
 Удобно использовать, когда создание зависимости
  стоит дорого, а нужно не при каждом
  использовании класса
Декораторы
 Цепочка из декоратор позволяет нам в полной мере
  реализовать Open/Closed принцип – создавать
  классы, закрытые для модификаций, но доступные
  для расширения
 Только для расширения мы будем применять не
  наследование от этого же класса, а наследование от
  абстракции
 По сути, декоратор – это умный прокси.
Так кто же создает всю
иерархию?
 В идеале – одно место в приложении, где
  формируется весь граф зависимостей (паттерн
  Composition Root)
 В идеале – одно на всё приложение место, где мы
  запрашиваем объект – вершину пирамиды
  зависимостей
 Современные фреймворки создаются с учетом
  этого требования
  Так, в жизненном цикле запроса к ASP.NET MVC
  таким местом является ControllerFactory
Демо
Composition Root своими руками
Управление временем жизни
Lifestyle       Описание
Singletone      Единственный экземпляр, предоставляемый всем
                потребителям
Transient       Новый экземпляр каждому потребителю
PerThread       Один экземпляр внутри потока
Pooled          Определенное количество экземпляров, предоставляемое
                по мере освобождения
PerWebRequest   Один экземпляр, предоставляемый всем потребителям в
                пределах одного запроса к Web-серверу
PerGraph        Один экземпляр на граф объектов
Scoped          Один экземпляр внутри явно выделенного участка кода
Lazy            Transient, но инициализируемый не при разрешении
                зависимости, а при первом использовании
IoC – контейнеры
 Делают всё то же самое, что мы делаем руками
 Но большая часть их багов была уже поймана
  другими людьми
 Одно из главных требований к IoC контейнеру –
  99% вашего кода не должно подозревать, что вы его
  используете
 Мне неизвестные причины, которые
  аргументировали бы НЕ использовать IoC
  контейнер
IoC-контейнеры – из чего выбрать?
Ну, например…
 AutoFac
 Castle Windsor
 Dynamo.Ioc
 Funq
 Hiro
 LightCore
 LightInject
 LinFu
 Munq
 Ninject
 Petite
 Simple Injector
 Spring.NET
 StructureMap
 TinyIoc
 Unity
Castle Windsor
 Один из старейших IoC-контейнеров для .NET
 Текущая версия – 3.0.4001
 Поддерживает конфигурирование из кода (FluentAPI),
    конфигурирование из XML и конфигурирование согласно
    конвенциям
   Отличные возможности для отладки – полная
    диагностическая информация о зарегистрированных
    классах и интерфейсах
   Концепция установщиков (Installer), позволяющая
    разносить регистрационный код для компонентов
   Поддержка абстракций логирования
   Поддержка Interception
   Поддержка авто-реализации фабрик
Демо
Регистрация и разрешение
зависимостей
Перехват (Interception) вызовов
 Один из подходов к реализации cross-cutting
  concerns (вещей, которые используются во всех
  слоях вашего приложения – таких как логирование,
  проверка безопасности, кэширование и т.п.)
  Разновидность аспектного программирования
 Позволяет выполнять действия до или после
  вызовов методов у зависимостей, а также
  обрабатывать результат выполнения
 Сходен с Декоратором, но отсутствует
  необходимость дублировать код для каждого
  интерфейса и метода в интерфейсе
Демо
Interception в Castle Windsor
Фабрики в Castle Windsor
 Interface-based Factories – на основе интерфейса
  Castle Windsor сам генерирует фабрику
 Delegate-based Factories – можно зарегистрировать
  делегат типа Func<TResult>, который будет играть
  роль фабричного метода
Демо
Фабрики в Castle Windsor
Отдельные темы для обсуждения
 Constructor Over-Injection
 public HomeController(IBookRepository rep,
      ICurrentUser user,
      ICurrencyFormatter formatter,
      IDateTimeProvider dateTimeProvider,
      IBookReturnmentPolicy policy,
      IBookOrderService         bookOrderService,
      IBookSearchService bookSearchService,
      IScheduler scheduler,
      IEmailService emailService,
      ISmsSender smsSender){
      …
 }

 Работа с IDisposable-зависимостями
 Круговая зависимость
Реальная жизнь
 На примере сайт GetDev.NET
  ASP.NET MVC 3
  Castle Windsor
 Так и не удалось добиться единого Composition Root –
  CustomRoleProvider создается средой исполнения и не
  имеет возможности вставки зависимостей
  Используем Service Locator ;)
 Для доступа к данным – абстракция над Linq to Sql
 interface IUnitOfWork : IDisposable{
        ITable<User> Users{ get; set; }
        …
 }
  Создаётся абстрактной фабрикой
 Декоратор вокруг IAccountService – кэширование ролей
  пользователя
Вопросы?
          Внимательно слушаю! 
                  Андрей Кулешов
                  «Деловые решения»
                  Директор
akuleshov@solforbiz.com            akuleshov.tula
http://www.solforbiz.com


Специально для http://GetDev.NET
Интересное чтение
 Dependency Injection in .NET by Mark Seemann
    http://manning.com/seemann/
   Castle Windsor homepage
    http://stw.castleproject.org/Windsor.MainPage.ashx
   Krzysztof Koźmic's blog
    http://kozmic.pl/
   Сравнение производительности IoC-контейнеров
    http://www.palmmedia.de/Blog/2011/8/30/ioc-container-
    benchmark-performance-comparison
   Сравнение функциональности некоторых IoC-контейнеров
    http://code.google.com/p/net-ioc-frameworks/
Интересное видео
 IoC Container usage: Patterns and anti-pattern (Krzysztof
  Koźmic)
  http://kozmic.pl/presentations/
 Inversion of Control/Dependency Injection Pattern
  (Hammet Verissimo & Michael Puleio)
  http://channel9.msdn.com/posts/PP-Symposium-2010-
  Inversion-of-ControlDependency-Injection-Pattern-
  Hammet-Verissimo--Michael-Puleio
 Channel 9
  http://channel9.msdn.com/
  Текстовое поле “Search” –> “Dependency Injection” ||
  “Inversion of Control” -> Enter

More Related Content

Dependency injection

  • 2. Про что мы говорим?  В первую очередь – про подход к взаимодействую между классами и объектами в мире объектно- ориентированного программирования  Во вторую очередь – про существующие шаблоны реализации этого подхода  И только в третью очередь – про сторонние компоненты - IoC-контейнеры
  • 3. Спагетти-код и лазанья-код  Спагетти-код – это код, представляющий собой одну большую хаотичную мешанину классов, отвечающих за все функции вашего приложения  Лазанья-код - это код, представляющий собой несколько отдельных маленьких хаотичных мешанин классов – слоёв, взаимодействующих друг с другом по строго определенным контрактом, не зависящим от реализации.  Классическая модель – всем известная уровень данных, уровень бизнес-логики, уровень пользовательского интерфейса. Но их может быть больше.
  • 4. Формирование слоёв  Слой – группа классов, имеющих схожее назначение  Взаимодействие между слоями идёт через «швы»  Взаимодействие через швы должны идти только через контракты (интерфейсы или абстрактные классы), а не конкретные реализации  И этим вы сможете добиться, что маленький локальный хаос внутри конкретного слоя останется таковым, а не разрастется на всё приложение
  • 5. Что такое «зависимость» («dependency»)  Что-то сильно внешнее по отношению к нашему классу, медленное, тяжелое в настройке, а также просто ещё не готовое.. В первую очередь это: - файловая система; - база данных - сеть; - сервис, который коллега послезавтра обязательно допи��ет; - система старта ракетных двигателей.  Ваш класс должен зависеть от абстракций, а не от конкретных реализаций
  • 6. Классическая работа с зависимостями:  Когда вы напрямую создаёте экземпляр объекта в вашем классе - вы контролируете его IDbConnection connection = new SqlServerConnection(“source=localhost”);  Контролируете, когда он создается, какие параметры принимает, когда он больше не нужен и его надо удалять  Контролируете, что создается именно этот тип объекта, а не схожий по функиональности  То есть всерьез декларируете, что класс, который считает сумму заказов в вашей корзине покупок, должен знать о балансированной ферме Sql Server, и том, какой таймаут допустим при работе с ней
  • 8. Inversion of Control Инвертированный контроль:  При использование IoC – ваш класс больше не имеет контроля над зависимостями public class Basket{ public Basket(IDbConnection connection){ //save connection... } }  Об этом заботится кто-то снаружи этого класса  Кто-то, кто знает больше и лучше. Кто на этом специализируется  А ваш класс перестаёт зависеть от конкретной реализации
  • 9. Что злобные враги рассказывают вам об DI  DI – это «локатор сервисов» нагло врут  DI нужен только для тестирования нагло врут  DI нужен только для позднего связывания нагло врут  Для архитектуры в стиле DI нужен IoC контейнер нагло врут
  • 10. Как всё начиналось  Одним из первых паттернов, способствующих уменьшению связности между классами, был локатор сервисом (Service Locator) До сих пор он у многих ассоциируется с понятием Dependency Injection  Однако сейчас он считается скорее анти- паттерном, не рекомендуемым к применению  Основная проблема – неявные требования при работе с классами, использующими локаторы сервисов
  • 12. Как внедрять зависимости? Основное требование – объект всегда должен быть в рабочем состоянии. Если зависимость неявная – значит, она должна быть необязательной. Ни при каких обстоятельствах не должен происходить NullReferenceException Четыре стандартных подхода:  Вставка через конструктор (constructor injection)  Вставка через свойства (property injection)  Вставка через параметры методов (methods injection)  Использование общедоступного внешнего контекста (ambient context)
  • 13. Constructor Injection  Используется для всех зависимостей, без которых классу не обойтись public class GreetingsManager { private readonly IWriter _writer; public GreetingsManager(IWriter writer) { if(writer == null) throw new ArgumentNullException("writer", "Writer was not provided"); _writer = writer; } ... ... }
  • 15. Property Injection  Используется для всех public class GreetingsManager опциональных { зависимостей private IWriter _writer = null;  Или для зависимостей, у public IWriter Writer которых есть локальная { реализация по умолчанию get {  То есть когда в 90% случаев if(_writer == null) подойдет стандартная, но { иногда нужно иметь _writer = new ConsoleWriter(); возможность заменить… }  Возможно, иногда также return _writer; подойдет NullObject – } паттерн, то есть объект- set заглушка, не выполняющая { действий if(_writer != null) throw new (пример не совсем чистый – в InvalidOperationException( примерах кода GreetingsManager и "You can't change writer ConsoleWriter находятся в разных implementation after usage"); сборках. Локальная реализация должна принадлежать той же сборке) _writer = value; } }
  • 17. Method Injection  Имеет смысл использовать, если зависимость меняется от метода к методу  Также удобно использовать при написании собственного фреймворка, когда мы хотим передать получателю (например, стороннему плагину) некий контекст, хранящий информацию о вызывающей стороне public string RunPlugin(SomeValue value, ISomeContext context) { if (context == null) { throw new ArgumentNullException("context"); } return context.Name; }
  • 19. Ambient Context  Некий контекст, который с большей долей вероятности может понадобиться (а может и не понадобится) в большинстве разрабатываемых классов  Чтобы не передавать зависимость в каждый из разрабатываемых классов – применяем общедоступный Ambient Context  Пример использования – текущее время (но с возможностью подмены значения)
  • 21. Абстрактные фабрики  Удобно использовать для инстанциирования короткоживущих объектов  Удобно использовать для инстанциирования объектов, когда тип создаваемых объектов зависит от параметров, известных только во время выполнения  Удобно использовать, когда создание зависимости стоит дорого, а нужно не при каждом использовании класса
  • 22. Декораторы  Цепочка из декоратор позволяет нам в полной мере реализовать Open/Closed принцип – создавать классы, закрытые для модификаций, но доступные для расширения  Только для расширения мы будем применять не наследование от этого же класса, а наследование от абстракции  По сути, декоратор – это умный прокси.
  • 23. ��ак кто же создает всю иерархию?  В идеале – одно место в приложении, где формируется весь граф зависимостей (паттерн Composition Root)  В идеале – одно на всё приложение место, где мы запрашиваем объект – вершину пирамиды зависимостей  Современные фреймворки создаются с учетом этого требования Так, в жизненном цикле запроса к ASP.NET MVC таким местом является ControllerFactory
  • 25. Управление временем жизни Lifestyle Описание Singletone Единственный экземпляр, предоставляемый всем потребителям Transient Новый экземпляр каждому потребителю PerThread Один экземпляр внутри потока Pooled Определенное количество экземпляров, предоставляемое по мере освобождения PerWebRequest Один экземпляр, предоставляемый всем потребителям в пределах одного запроса к Web-серверу PerGraph Один экземпляр на граф объектов Scoped Один экземпляр внутри явно выделенного участка кода Lazy Transient, но инициализируемый не при разрешении зависимости, а при первом использовании
  • 26. IoC – контейнеры  Делают всё то же самое, что мы делаем руками  Но большая часть их багов была уже поймана другими людьми  Одно из главных требований к IoC контейнеру – 99% вашего кода не должно подозревать, что вы его используете  Мне неизвестные причины, которые аргументировали бы НЕ использовать IoC контейнер
  • 27. IoC-контейнеры – из чего выбрать? Ну, например…  AutoFac  Castle Windsor  Dynamo.Ioc  Funq  Hiro  LightCore  LightInject  LinFu  Munq  Ninject  Petite  Simple Injector  Spring.NET  StructureMap  TinyIoc  Unity
  • 28. Castle Windsor  Один из старейших IoC-контейнеров для .NET  Текущая версия – 3.0.4001  Поддерживает конфигурирование из кода (FluentAPI), конфигурирование из XML и конфигурирование согласно конвенциям  Отличные возможности для отладки – полная диагностическая информация о зарегистрированных классах и интерфейсах  Концепция установщиков (Installer), позволяющая разносить регистрационный код для компонентов  Поддержка абстракций логирования  Поддержка Interception  Поддержка авто-реализации фабрик
  • 30. Перехват (Interception) вызовов  Один из подходов к реализации cross-cutting concerns (вещей, которые используются во всех слоях вашего приложения – таких как логирование, проверка безопасности, кэширование и т.п.) Разновидность аспектного программирования  Позволяет выполнять действия до или после вызовов методов у зависимостей, а также обрабатывать результат выполнения  Сходен с Декоратором, но отсутствует необходимость дублировать код для каждого интерфейса и метода в интерфейсе
  • 32. Фабрики в Castle Windsor  Interface-based Factories – на основе интерфейса Castle Windsor сам генерирует фабрику  Delegate-based Factories – можно зарегистрировать делегат типа Func<TResult>, который будет играть роль фабричного метода
  • 34. Отдельные темы для обсуждения  Constructor Over-Injection public HomeController(IBookRepository rep, ICurrentUser user, ICurrencyFormatter formatter, IDateTimeProvider dateTimeProvider, IBookReturnmentPolicy policy, IBookOrderService bookOrderService, IBookSearchService bookSearchService, IScheduler scheduler, IEmailService emailService, ISmsSender smsSender){ … }  Работа с IDisposable-зависимостями  Круговая зависимость
  • 35. Реальная жизнь  На примере сайт GetDev.NET ASP.NET MVC 3 Castle Windsor  Так и не удалось добиться единого Composition Root – CustomRoleProvider создается средой исполнения и не имеет возможности вставки зависимостей Используем Service Locator ;)  Для доступа к данным – абстракция над Linq to Sql interface IUnitOfWork : IDisposable{ ITable<User> Users{ get; set; } … } Создаётся абстрактной фабрикой  Декоратор вокруг IAccountService – кэширование ролей пользователя
  • 36. Вопросы? Внимательно слушаю!  Андрей Кулешов «Деловые решения» Директор akuleshov@solforbiz.com akuleshov.tula http://www.solforbiz.com Специально для http://GetDev.NET
  • 37. Интересное чтение  Dependency Injection in .NET by Mark Seemann http://manning.com/seemann/  Castle Windsor homepage http://stw.castleproject.org/Windsor.MainPage.ashx  Krzysztof Koźmic's blog http://kozmic.pl/  Сравнение производительности IoC-контейнеров http://www.palmmedia.de/Blog/2011/8/30/ioc-container- benchmark-performance-comparison  Сравнение функциональности некоторых IoC-контейнеров http://code.google.com/p/net-ioc-frameworks/
  • 38. Интересное видео  IoC Container usage: Patterns and anti-pattern (Krzysztof Koźmic) http://kozmic.pl/presentations/  Inversion of Control/Dependency Injection Pattern (Hammet Verissimo & Michael Puleio) http://channel9.msdn.com/posts/PP-Symposium-2010- Inversion-of-ControlDependency-Injection-Pattern- Hammet-Verissimo--Michael-Puleio  Channel 9 http://channel9.msdn.com/ Текстовое поле “Search” –> “Dependency Injection” || “Inversion of Control” -> Enter