Вы хорошо понимаете DI. И да, Symfony Controller действительно реализует ContainerAwareInterface, и, как вы сказали, выполняет роль локатора сервисов. Но локатор сервисов - это не антипаттерн. Каждый узор имеет свое правильное и неправильное использование.
Более того, Symfony никоим образом не принуждает вас использовать свой Контроллер. Ваш контроллер может быть службой. Черт, это может быть даже функция!
Вот одна из причин, почему Контроллеры реализованы как локаторы сервисов: Производительность.
Давайте откажемся от аналогии с автомобилем и сосредоточимся на реальном случае, с которым вы столкнетесь в 99% проектов: вам нужен CRUD для ресурса. Допустим, вы создаете приложение Todo и вам нужен контроллер RESTfulish для обработки операций CRUD для ресурса Task.
Меньше всего, что вам нужно, - это способ прочитать все задачи и способ добавить новую задачу, для этого вам потребуются два действия: индекс (обычно также называется список) и store (обычно также называется create).
Обычный поток в Symfony будет таким, в псевдокоде:
indexAction -> getDoctrine -> getTaskRepository -> getAllTasks
storeAction -> getFormFactory -> createForm -> bindRequestDataToForm -> getDoctrine -> saveData
Если Контроллер был локатором службы
Индекс Действие
Когда выполняется действие index, только служба, которая будет разрешена из контейнера, будет ManagerRegistry (в данном случае Doctrine). Затем мы попросим его предоставить нам репозиторий задач, и мы будем выполнять с ним свою операцию.
Магазин Действия
Когда выполняется действие store, мы проделаем немного больше работы: попросим контейнер предоставить нам FormFactory, проделаем с ним некоторые операции, а затем попросим его предоставить нам Доктрина и тоже поработайте с ней.
Итак, итоги: при выполнении действия индекса только одна служба должна быть создана контейнером служб, при выполнении обновления должны быть созданы две.
Если бы контроллер был обычным сервисом
Посмотрим, что нужно нашему контроллеру. Из приведенного выше раздела видно, что ему нужны FormFactory и Doctrine.
Теперь, когда вы просто хотите вызвать действие index для чтения всех задач из хранилища данных, ваш контроллер должен будет получить экземпляр контейнера. Прежде чем его можно будет создать, контейнер должен создать экземпляр своих зависимостей: FormFactory и Doctrine. Затем создайте экземпляр контроллера, вводя в него эти два.
Итак, вы вызываете действие index, которому вообще не требуется FormFactory, но у вас все еще есть накладные расходы на его создание, потому что оно необходимо для действия, которое не будет вызываться. вообще в том запросе.
Ленивые службы
Чтобы уменьшить эти накладные расходы, существует вещь, которая называется ленивым сервисом. Он работает, фактически вставляя прокси вашей службы в контроллер. Итак, что касается контроллера, у него есть FormFactory. Он не знает, что это не настоящая FormFactory, а поддельный объект, который будет делегировать вызовы реальному коду FormFactory, когда вы вызываете на нем какой-либо метод.
Подведение итогов
Контроллер не обязательно должен быть локатором услуг, но может быть. Создание локатора сервисов может быть немного более производительным и более легким для начальной загрузки, но скрывает зависимости. Кроме того, его немного сложнее протестировать, так как вам нужно будет имитировать контейнер зависимостей. Хотите ли вы сделать свои контроллеры службами, функциями или локаторами служб - это ваш выбор, и Symfony не будет заставлять вас использовать какой-либо из этих способов.
По моему опыту, расширение контроллера Symfony по умолчанию и использование контроллеров в качестве локаторов служб - это нормально, если вы не пишете в них свою бизнес-логику, а вместо этого делегируете все это работа с услугами, которые вы получаете из контейнера. Таким образом, очень маловероятно, что у вас будут ошибки в коде контроллера (поскольку методы обычно состоят из 2–3 строк кода), и вы сможете обойтись без их тестирования.
person
Igor Pantović
schedule
10.10.2014