5

I am a big fan of dependency injection, but something bothers me and I was wondering whether someone could give me an explanation:

It is not possible to create two services that depends on each other, because we will get a "Circular Reference" exception. I think everyone using symfony2 has met this error.

While I understand very well this error, I meet it sometimes because with a lot of services, comes complexity... and, maybe also because designing/sizing services is not easy.

Therefore, I was wondering about the root cause of this error:

  1. It is to protect us regarding a potential serious design error? If this is the case, could you give me an example of what serious can happen if two dependent services could "live together"?
  2. It is purely technical? I.e. because it is not possible to call both constructors. If this is the root reason, why not solve it by forcing service constructors to be empty and have an init method?

I.e.:

class MyService1{

   private $service2;

   public function __construct(){ //empty constructor
      ...
   }

   protected function init(MyService2 $service2, ...){
      $this->service2 = $service2;
   }

}

class MyService2{

   private $service1;

   public function __construct(){ //empty constructor
      ...
   }

   protected function init(MyService1 $service1, ...){
      $this->service1 = $service1;
   }

}

And then instantiate both services:

$service1 = new MyService1();
$service2 = new MyService2();
$service1->init($service2);
$service2->init($service1);

I'm pretty sure there is something I don't have understood in depth. So could someone explain me why we are prevented to create circular references in the container?

Thank you

Vincent Pazeller
  • 1,448
  • 18
  • 28
  • Because your service1->init method would receive a service that is not fully initialized. Bad things will happen when you try to use a partially initialized service. – Cerad Dec 14 '15 at 12:57
  • I'm sorry, I am not convinced by this argument. Because in the init method, you would be in the same state than in the constructor... Symfony2 developers could create an abstract Service class that forces to implement that method... I don't think this is particularly dangerous. – Vincent Pazeller Dec 14 '15 at 13:30
  • So what happens if a depends on b which depends on c which in turn depends on both a and b? I suspect that after you work with di a bit more you might have a change of opinion. If not then submit a patch. The Symfony group encourages outside help. – Cerad Dec 14 '15 at 13:54
  • I don't think this is very difficult to implement the general case. But I think there is another reason this has not been done. And that's probably more a "Best practice" reason than a technical one. And that's why I am asking here... to have a better understanding... – Vincent Pazeller Dec 14 '15 at 13:58

1 Answers1

3

Ideally your services would adhere to the dependency inversion principle.

That is, high-level components would depend on low-level components but not the other way round. Additionally for higher cohesion, this dependency would be via an abstraction or interface.

See this question from the Programmers SE site.

enter image description here

Community
  • 1
  • 1
Adam Elsodaney
  • 7,722
  • 6
  • 39
  • 65
  • 1
    I think I have understood this. But I think that even if the Component A' constructor's prototype is __construct(ComponentBInterface $componentB), the Symfony2 service config will inject ComponentB implementation. There's no problem in your diagram because it is simple. Let me give a concrete example: I have a BasketHelper who contains methods related to orders and a CertificateHelper (which handles the certificate product). There are some methods which are a bit in between (i.e. related both to the orders and certificates)... Then, I guess I should create a Third service that depends on both? – Vincent Pazeller Dec 14 '15 at 13:41