Видеозапись:
http://getdev.net/Event/dependency-injection
Рассказ о внедрении зависимостей (Dependency Injection), зачем оно нужно, откуда оно пошло и развивалось. Виды Dependency Injection, разница между ними и рекомендации к применению. Расказ про декораторы (decorators). Рассказ про абстрактные фабрики. Рассказ про управление временем жизни. Рассказ про IoC -контейнеры вообще и Castle Windsor в частности.
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
контейнер
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