MVC и инъекции зависимостей, вынуждены использовать singleton Controller?

Я работаю над созданием фреймворка PHP, который ведет себя в соответствии с принципами MVC и использует инъекцию зависимостей. Я думаю, что у меня есть блок переднего контроллера; существует рабочий маршрутизатор, который создает экземпляр контроллера и вызывает соответствующее действие на основе запрошенного URI.

Далее – инъекция зависимостей. Я хочу реализовать контейнер, который разрешает зависимости с помощью отражения. При этом я думаю, что у меня проблемы с моими контроллерами.

Существует ряд тем, что я называю «системными зависимостями», которые должны быть доступны для производных классов контроллеров. Я еще не создал все эти зависимости, но кажется разумным, что контроллеры имеют доступ к таким сервисам, как InputProvider (для инкапсуляции параметров get / post или аргументов командной строки) и, возможно, зависимости от Output. В идеале я бы использовал контейнер Container для встраивания этих зависимостей в конструктор контроллера, но в этом я столкнулся с проблемами.

Если я использую инъекцию конструктора для системных зависимостей в контроллере, то я заставляю производные контроллеры управлять зависимостями базового контроллера, если они реализуют сам конструктор. Это, по-видимому, не самый удобный. Другой вариант заключается в использовании инжектора setter для системных зависимостей, но тогда производные контроллеры не будут иметь доступа к этим системным зависимостям, если они нуждаются в них в своем конструкторе.

Единственное решение, которое я вижу, которое предлагает лучшее из обоих миров, – это сделать мои контроллеры одиночными. У них должен быть частный конструктор, поэтому я могу безопасно использовать инъекцию setter, не беспокоясь о конструкторах производных классов. Вместо этого существует переопределяемый метод initialize () (предполагая, что я каким-то образом запускаю инъекцию метода), который в основном выполняет роль конструктора (как, например, инициализатор для производного контроллера). Таким образом, инъекция конструктора заменяется методом инжекции методом initialize (), где все системные зависимости будут доступны, не требуя, чтобы производный контроллер управлял ими.

Но тогда быстрый поиск в Google кажется единодушным, что сингл-контроллеры – это плохая практика. Я очень не уверен, как действовать дальше. Я, вероятно, переоцениваю это, но, не желая, чтобы мое приложение было надежным и поддерживаемым в будущем, я также рассматриваю его как небольшое упражнение в применении лучших практик, поэтому я хотел бы делать «правильно».
Я считаю, что лучшей практикой в ​​этом случае будет передача ответственности за управление требуемыми системными зависимостями с производным контроллером. Вероятно, зависимость должна быть создана только в том случае, если на самом деле у этого контроллера есть необходимость. Зачем вводить InputProvider в базовый контроллер, если возможно, что производный контроллер никогда не собирается его использовать? Но в то же время я продолжаю возвращаться к удобству для пользователя, и как приятно просто всегда иметь доступный член $this->input , например, в рамках CodeIgniter.

Я высоко ценю любые и все вклады в мои диллемы. Я также извиняюсь за стену текста, но я не мог придумать каких-либо примеров кода, которые упростили бы объяснение, потому что все это настолько абстрактно для меня прямо сейчас!

С уважением,
Серьезно разорванный человек

Возможны несколько решений:

  • запретить контроллерам использовать __construct() : сделать его закрытым публичным окончанием и заставить контроллеры переопределить что-то вроде init() и вызвать его из конструктора. Тогда конструктор будет вводить все зависимости (отражение? Другой материал?), Чтобы все они были готовы в init() .

  • вы можете использовать существующую библиотеку DI, такую ​​как PHP-DI (отказ от ответственности: я над этим работаю), которая позволит вам определять зависимости, но иметь их в конструкторе (по волшебству, да).

Что-то вроде того:

 <?php use DI\Annotations\Inject; class FooController { /** * @Inject * @var Bar */ private $bar; public function __construct() { // The dependency is already injected $this->bar->sayHello(); } public function setBar(Bar $bar) { return $this->bar = $bar; } } 

Например, так я работаю с Zend Framework 1. Я не могу использовать конструкторы, поэтому я добавляю в свойства. Вот проект интеграции ZF1 .