Круговая зависимость. Инъекция объектов, которые напрямую зависят друг от друга

Я использовал контейнер Dice PHP DI довольно долгое время, и он кажется лучшим с точки зрения простоты инъекционных зависимостей.

Из документации на кости :

class A { public $b; public function __construct(B $b) { $this->b = $b; } } class B { } $dice = new \Dice\Dice; $a = $dice->create('A'); var_dump($a->b); //B object 

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

Пример:

 class A { public $b; public function __construct(B $b) { $this->b = $b; } } class B { public $a; public function __construct(A $a) { $this->a = $a; } } 

Автор Dice говорит, что нет способа построить объект из классов A или B. В виде:

  • Объекту 'A' требуется, чтобы существовал объект B, прежде чем он может быть создан
  • Но объект 'B' требует существования объекта 'A', прежде чем он может быть создан

Автор говорит, что это ограничение касается всех контейнеров DI !


Вопрос:

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

Как упоминалось в вашем сообщении на Dice github ( https://github.com/TomBZombie/Dice/issues/7 ), единственный способ разрешить без удаления круговой зависимости – это рефакторинг одного из классов для использования инъекции установщика:

 class A { public $b; public function __construct(B $b) { $this->b = $b; } } class B { public $a; public function setA(A $a) { $this->a = $a; } } 

Это позволяет создавать объекты:

 $b = new B(); $a = new A($b); $b->setA($a); 

С исходным кодом:

 class A { public $b; public function __construct(B $b) { $this->b = $b; } } class B { public $a; public function __construct(A $a) { $this->a = $a; } } 

Вы не можете построить его и столкнуться с той же проблемой, что и контейнер:

 $b = new B(new A(new B(new A(new B(.............)))) 

Проблема с использованием контейнера вокруг этой проблемы с использованием взлома, такого как ReflectionClass :: newInstanceWithoutConstructor, заключается в том, что ваши объекты теперь зависят от логики создания, которая использует этот метод. Вы по существу соединяете код с контейнером, который является плохим дизайном, поскольку ваш код теперь не переносится и не может использоваться без контейнера для выполнения объекта.

У вас есть круговая зависимость, которую очень трудно решить. Первое, что нужно сделать, это попытаться избавиться от этой циклической зависимости , рефакторинг ваших классов и их взаимодействие.

Если вы действительно не можете этого сделать, есть решения. Я скопирую мой ответ из моделей саморегуляции. Максимальный уровень вложенности функции x в Laravel 4 :

  • Сетчатая инъекция

Вместо того, чтобы вводить зависимость в конструкторе, вы можете ввести его в сеттер, который будет вызываться после создания объекта. В псевдокоде это будет выглядеть так:

 $userRepo = new UserRepository(); $cartRepo = new CartRepository($userRepo); $userRepo->setCartRepo($userRepo); 
  • Ленивая инъекция

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

Вот объяснение того, как ленивая инъекция работает, если вы заинтересованы: http://php-di.org/doc/lazy-injection.html