Гибкая архитектура Zend
  Framework приложений с
использованием Dependency
          Injection
Алексей Качаев
Senior/Lead PHP Developer at
Cogniance

4+ года опыта c PHP

3+ года использования Zend
Framework

Активный участник
zendframework.ru/forum

ZF2 contributor
О чем мы будем говорить

1. Что такое зависимости

2. IoC в теории и на практике

3. Реализация DI в Zend Framework с использованием
Symfony DI Container

4. Антипаттерны, или что мешает управлять
зависимостями

5. IoC в Zend Framework 2.0
Что такое зависимости
     (dependency)
Что такое зависимости: начало
Что такое зависимости: далее
Что такое зависимости: и...




Задача на разминку: как тестируя ProfileController
заменить DbConnection на фейковый?
Что здесь плохого?

1. Жесткость и хрупкость (изменения => поломка)
архитектуры (по Роберту Мартину)

2. "Нетестируемость" системы

3. Отсутствие прозрачности связей (сложность)

4. Код не самого нижнего уровня иерархии практически
невозможно реиспользовать (монолитность)
IoC patterns:
теория и практика
IoC в теории

1. Принцип Inversion of Control:

Модули верхних уровней не должны зависить от модулей
нижних уровней.

Легко сказать!

2. Реализация на практике:

Ответственности за зависимости переходят к классам
верхнего уровня.
IoC на практике

У Фаулера:
http://martinfowler.com/articles/injection.html

Два основных паттерна:

1. Service Locator

2. Dependency Injection (Внедрение или Инъекция
Зависимостей)
http://ru.wikipedia.org/wiki/Dependency_Injection

(разница в том, на кого перекладывается ответственность
за управление зависимостями)
Service Locator

Суть: зависимостями управляет специальный объект
ServiceLocator
Service Locator

Далее используем везде, где есть зависимости
Dependency Injection
 Суть: зависимый объект заранее передается через setter
 или конструктор.

Зависимости




                                               Сборка
3 шага к Dependency Injection

Шаг 1. Getter/Setter

Шаг 2. Интерфейс (interface)

Шаг 3. Контейнер
3 шага к Dependency Injection
На один уровень выше
Идем дальше: игра в пятнашки

"Двигаем" все зависимости "наверх" в единый Контейнер:
Используем контейнер
Контролер создаем не прямо, а через берем из
контейнера:




Чего еще хотелось бы:

- build контейнера из конфигурационного файла

- shared-объекты контейнера, для возможности следить
за уникальностью объекта в рамках контейнера (при
необходимости)
Что дает DI?

1. Любой класс в иерархии зависимостей можно отдельно
протестировать

2. Код легко перенести в другое окружение и/или
приложение

3. Один шаг в сторону принципа единой ответственности
класса
Сравнение двух подходов с
практической точки зрения
Service Locator
+ очень просто внедрить даже в существующий (даже
крупный проект) - точечность изменений
+ легко заложить дефолтные процедуры получения
объекта
- сложно переопределять объекты

DI
+ более управляемый при сильном разрастании
+ обеспечивает большую прозрачности иерархий
- практически не поддерживает точечного внедрения

Возможно использование смешаного подхода
ZF 1.* &
Symfony DI Container
PHP DI контейнеры

1. Symfony Dependency Injection
2. Pico Container
3. Yadif_Container
4. Phemto
5. .... (много других)
Возможности Symfony DI

1. Удобное представление контейнера через
конфигурацию (xml, yaml)

2. Гибкие возможности конфигуратора

3. Dump собранного контейнера в php-код для увеличения
производительности

4. Shared-объекты контейнера

5. Хранение параметров
Пример блоков конфигурации
Пример блоков конфигурации
DI в Zend
Варианты:
- контейнер для Application
- контейнер как ресурс
- контейнер как часть Bootstrap
Как мы поступим?
Прим. Очень много зависимостей, на которые мы пока не
смотрим (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
Service locator helper
Service locator helper - использование




Плюсы
+ простота имплементации
+ легкость "точечного" внедрения

Минусы
- "частичный" DI - контроллер не является
самодостаточным и связан с helper-ом
Dependency Injection при
диспечирезации
1. В helper-e добавляем функцию preDispatch() - будет
вызвана FrontController-ом перед диспечеризацией
контроллера

2. Получаем в ней объект контроллера

3. Через Zend_Reflection_Class анализируем свойста и
методы, на предмет наличия @Inject аннотации

4. Инъектируем зависимости (через setter или через
рефлекцию)
Dependency Injection при
диспечирезации




Плюсы + те же
Минусы
- контроллер не является самодостаточным и связан с
циклом диспечирезации
DI - обратить внимание

Негативный момент DI - "привязанность" к inject-
ированию. Во избежании проблем лучше пользоваться
Exception-ами вроде:




Безопаснее - RuntimeException()
DI - lazy loading
Иногда бывают ситуации, когда нам нужно получить
некий объект с "тяжелыми" объектами в иерархии
зависимостей.

UserService->AuthProvider->AuthAdapter->DbConnection

Но DbConnection может не пригодится. Решение -
фабрика.
DI - unit-тестирование

1. Любой объект может быть собран на любом уровне
иерархии зависимостей, а значит может быть
протестирован отдельно.

2. Сборка тестируемых объектов может проводится как
контейнером (используем тестовый конфиг), так и в
"ручном" режиме (используя принцип DI-ready в
архитектуре каждого объекта).
Антипаттерны IoC:
что мешает управлять
   зависимостями
Антипаттерны
Не-injectable:

1. Singleton
- заменяется на shared-объект в рамках контейнера

2. Static class
- отпадает необходимость, так как есть возможность
инъектировать объект

3. Cross-reference
- объкты ссылаются друг на друга. например
Bootstrap->FrontController->Bootstrap
Антипаттерны
Не-логичные:

4. "Блуждающий контейнер"
- зависимости собираются в контейнер, но сам койнейнер
передается (injected) вниз по иерархии
IoC & ZF2
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
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 их нет)
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 не позволяет использовать
существующие решения.
IoC в Zend Framework 2.0

Приведенные в Proposal примеры достаточно
искусственны и тривиальны.

Типа такого:
IoC в Zend Framework 2.0

Что не дает возможности оценить необходимость и
выигрыш от смешивания подходов в некий DI based
Service Locator (похоже на java-ский Avalon Framework):
Спасибо за внимание!



            Вопросы
              ???

ZFConf 2011: Гибкая архитектура Zend Framework приложений с использованием Dependency Injection (Алексей Качаев)

  • 1.
    Гибкая архитектура Zend Framework приложений с использованием Dependency Injection
  • 2.
    Алексей Качаев Senior/Lead PHPDeveloper 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
  • 4.
  • 5.
  • 6.
  • 7.
    Что такое зависимости:и... Задача на разминку: как тестируя ProfileController заменить DbConnection на фейковый?
  • 8.
    Что здесь плохого? 1.Жесткость и хрупкость (изменения => поломка) архитектуры (по Роберту Мартину) 2. "Нетестируемость" системы 3. Отсутствие прозрачности связей (сложность) 4. Код не самого нижнего уровня иерархии практически невозможно реиспользовать (монолитность)
  • 9.
  • 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 (разница в том, на кого перекладывается ответственность за управление зависимостями)
  • 12.
    Service Locator Суть: зависимостямиуправляет специальный объект ServiceLocator
  • 13.
    Service Locator Далее используемвезде, где есть зависимости
  • 14.
    Dependency Injection Суть:зависимый объект заранее передается через setter или конструктор. Зависимости Сборка
  • 15.
    3 шага кDependency Injection Шаг 1. Getter/Setter Шаг 2. Интерфейс (interface) Шаг 3. Контейнер
  • 16.
    3 шага кDependency Injection
  • 17.
  • 18.
    Идем дальше: играв пятнашки "Двигаем" все зависимости "наверх" в единый Контейнер:
  • 19.
    Используем контейнер Контролер создаемне прямо, а через берем из контейнера: Чего еще хотелось бы: - build контейнера из конфигурационного файла - shared-объекты контейнера, для возможности следить за уникальностью объекта в рамках контейнера (при необходимости)
  • 20.
    Что дает DI? 1.Любой класс в иерархии зависимостей можно отдельно протестировать 2. Код легко перенести в другое окружение и/или приложение 3. Один шаг в сторону принципа единой ответственности класса
  • 21.
    Сравнение двух подходовс практической точки зрения Service Locator + очень просто внедрить даже в существующий (даже крупный проект) - точечность изменений + легко заложить дефолтные процедуры получения объекта - сложно переопределять объекты DI + более управляемый при сильном разрастании + обеспечивает большую прозрачности иерархий - практически не поддерживает точечного внедрения Возможно использование смешаного подхода
  • 22.
    ZF 1.* & SymfonyDI Container
  • 23.
    PHP DI контейнеры 1.Symfony Dependency Injection 2. Pico Container 3. Yadif_Container 4. Phemto 5. .... (много других)
  • 24.
    Возможности Symfony DI 1.Удобное представление контейнера через конфигурацию (xml, yaml) 2. Гибкие возможности конфигуратора 3. Dump собранного контейнера в php-код для увеличения производительности 4. Shared-объекты контейнера 5. Хранение параметров
  • 25.
  • 26.
  • 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
  • 29.
  • 30.
    Service locator helper- использование Плюсы + простота имплементации + легкость "точечного" внедрения Минусы - "частичный" DI - контроллер не является самодостаточным и связан с helper-ом
  • 31.
    Dependency Injection при диспечирезации 1.В helper-e добавляем функцию preDispatch() - будет вызвана FrontController-ом перед диспечеризацией контроллера 2. Получаем в ней объект контроллера 3. Через Zend_Reflection_Class анализируем свойста и методы, на предмет наличия @Inject аннотации 4. Инъектируем зависимости (через setter или через рефлекцию)
  • 32.
    Dependency Injection при диспечирезации Плюсы+ те же Минусы - контроллер не является самодостаточным и связан с циклом диспечирезации
  • 33.
    DI - обратитьвнимание Негативный момент DI - "привязанность" к inject- ированию. Во избежании проблем лучше пользоваться Exception-ами вроде: Безопаснее - RuntimeException()
  • 34.
    DI - lazyloading Иногда бывают ситуации, когда нам нужно получить некий объект с "тяжелыми" объектами в иерархии зависимостей. UserService->AuthProvider->AuthAdapter->DbConnection Но DbConnection может не пригодится. Решение - фабрика.
  • 35.
    DI - unit-тестирование 1.Любой объект может быть собран на любом уровне иерархии зависимостей, а значит может быть протестирован отдельно. 2. Сборка тестируемых объектов может проводится как контейнером (используем тестовый конфиг), так и в "ручном" режиме (используя принцип DI-ready в архитектуре каждого объекта).
  • 36.
    Антипаттерны IoC: что мешаетуправлять зависимостями
  • 37.
    Антипаттерны Не-injectable: 1. Singleton - заменяетсяна shared-объект в рамках контейнера 2. Static class - отпадает необходимость, так как есть возможность инъектировать объект 3. Cross-reference - объкты ссылаются друг на друга. например Bootstrap->FrontController->Bootstrap
  • 38.
    Антипаттерны Не-логичные: 4. "Блуждающий контейнер" -зависимости собираются в контейнер, но сам койнейнер передается (injected) вниз по иерархии
  • 39.
  • 40.
    IoC в ZendFramework 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 в ZendFramework 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 в ZendFramework 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 в ZendFramework 2.0 Приведенные в Proposal примеры достаточно искусственны и тривиальны. Типа такого:
  • 44.
    IoC в ZendFramework 2.0 Что не дает возможности оценить необходимость и выигрыш от смешивания подходов в некий DI based Service Locator (похоже на java-ский Avalon Framework):
  • 45.