2. Алексей Качаев
Senior/Lead PHP Developer at
Cogniance
4+ года опыта c PHP
3+ года использования Zend
Framework
Активный участник
zendframework.ru/forum
ZF2 contributor
3. О чем мы будем говорить
1. Что такое зависимости
2. IoC в теории и на практике
3. Реализация DI в Zend Framework с использованием
Symfony DI Container
4. Антипаттерны, или что мешает управлять
зависимостями
5. IoC в Zend Framework 2.0
7. Что такое зависимости: и...
Задача на разминку: как тестируя ProfileController
заменить DbConnection на фейковый?
8. Что здесь плохого?
1. Жесткость и хрупкость (изменения => поломка)
архитектуры (по Роберту Мартину)
2. "Нетестируемость" системы
3. Отсутствие прозрачности связей (сложность)
4. Код не самого нижнего уровня иерархии практически
невозможно реиспользовать (монолитность)
10. IoC в теории
1. Принцип Inversion of Control:
Модули верхних уровней не должны зависить от модулей
нижних уровней.
Легко сказать!
2. Реализация на практике:
Ответственности за зависимости переходят к классам
верхнего уровня.
11. IoC на практике
У Фаулера:
http://martinfowler.com/articles/injection.html
Два основных паттерна:
1. Service Locator
2. Dependency Injection (Внедрение или Инъекция
Зависимостей)
http://ru.wikipedia.org/wiki/Dependency_Injection
(разница в том, на кого перекладывается ответственность
за управление зависимостями)
18. Идем дальше: игра в пятнашки
"Двигаем" все зависимости "наверх" в единый Контейнер:
19. Используем контейнер
Контролер создаем не прямо, а через берем из
контейнера:
Чего еще хотелось бы:
- build контейнера из конфигурационного файла
- shared-объекты контейнера, для возможности следить
за уникальностью объекта в рамках контейнера (при
необходимости)
20. Что дает DI?
1. Любой класс в иерархии зависимостей можно отдельно
протестировать
2. Код легко перенести в другое окружение и/или
приложение
3. Один шаг в сторону принципа единой ответственности
класса
21. Сравнение двух подходов с
практической точки зрения
Service Locator
+ очень просто внедрить даже в существующий (даже
крупный проект) - точечность изменений
+ легко заложить дефолтные процедуры получения
объекта
- сложно переопределять объекты
DI
+ более управляемый при сильном разрастании
+ обеспечивает большую прозрачности иерархий
- практически не поддерживает точечного внедрения
Возможно использование смешаного подхода
24. Возможности Symfony DI
1. Удобное представление контейнера через
конфигурацию (xml, yaml)
2. Гибкие возможности конфигуратора
3. Dump собранного контейнера в php-код для увеличения
производительности
4. Shared-объекты контейнера
5. Хранение параметров
27. DI в Zend
Варианты:
- контейнер для Application
- контейнер как ресурс
- контейнер как часть Bootstrap
28. Как мы поступим?
Прим. Очень много зависимостей, на которые мы пока не
смотрим (helpers, plugins, View, request/response и т.д.)
1. Контейнер будет внедрен в Bootstrap вместо
стандартного Zend_Registry
2. В контролере, контейнер можно будет получить через
специальный Helper - ServiceContainer
Это идеологически Service Locator.
3. PreDispatch-обработка инъекций (ServiceContainer
helper)
Sources
https://github.com/kachayev/zfconf-speech
30. Service locator helper - использование
Плюсы
+ простота имплементации
+ легкость "точечного" внедрения
Минусы
- "частичный" DI - контроллер не является
самодостаточным и связан с helper-ом
31. Dependency Injection при
диспечирезации
1. В helper-e добавляем функцию preDispatch() - будет
вызвана FrontController-ом перед диспечеризацией
контроллера
2. Получаем в ней объект контроллера
3. Через Zend_Reflection_Class анализируем свойста и
методы, на предмет наличия @Inject аннотации
4. Инъектируем зависимости (через setter или через
рефлекцию)
33. DI - обратить внимание
Негативный момент DI - "привязанность" к inject-
ированию. Во избежании проблем лучше пользоваться
Exception-ами вроде:
Безопаснее - RuntimeException()
34. DI - lazy loading
Иногда бывают ситуации, когда нам нужно получить
некий объект с "тяжелыми" объектами в иерархии
зависимостей.
UserService->AuthProvider->AuthAdapter->DbConnection
Но DbConnection может не пригодится. Решение -
фабрика.
35. DI - unit-тестирование
1. Любой объект может быть собран на любом уровне
иерархии зависимостей, а значит может быть
протестирован отдельно.
2. Сборка тестируемых объектов может проводится как
контейнером (используем тестовый конфиг), так и в
"ручном" режиме (используя принцип DI-ready в
архитектуре каждого объекта).
37. Антипаттерны
Не-injectable:
1. Singleton
- заменяется на shared-объект в рамках контейнера
2. Static class
- отпадает необходимость, так как есть возможность
инъектировать объект
3. Cross-reference
- объкты ссылаются друг на друга. например
Bootstrap->FrontController->Bootstrap
40. IoC в Zend Framework
2007 год
Zend_Di component proposal (Federico Cargnelutti)
http://framework.zend.com/wiki/display/ZFPROP/Zend_Di+-
+Federico+Cargnelutti
Непринято из-за комплексности
2008 год
Zend_Container component proposal (Bradley Holt)
http://framework.zend.com/wiki/display/ZFPROP/Zend_Container+-
+Bradley+Holt
41. IoC в Zend Framework 2.0
Matthew Weier O'Phinney
"Zend Framework 2 Patterns"
Roadmap...
- ...
- Inversion of Control
1. Service Locator
2. Dependency Injector
(в официальном ZF 2.0 Roadmap их нет)
42. IoC в Zend Framework 2.0
10 Марта, 2011
Proposal for ServiceLocator and DependencyInjector
(http://framework.zend.com/wiki/display/ZFDEV2/Proposal+for+ServiceLocator+and+Dep
endencyInjector)
Пример использования новых компонентов
https://github.com/weierophinney/zf-examples/tree/projects%2Fzf2.di/zf2-di
Вызвало достаточно бурное обсуждение в рассылке
контрибьюторов.
Замечание: лицензия ZF не позволяет использовать
существующие решения.
43. IoC в Zend Framework 2.0
Приведенные в Proposal примеры достаточно
искусственны и тривиальны.
Типа такого:
44. IoC в Zend Framework 2.0
Что не дает возможности оценить необходимость и
выигрыш от смешивания подходов в некий DI based
Service Locator (похоже на java-ский Avalon Framework):