Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency Injection container in constructor

Why putting a container in a constructor is so bad? For example you want to resolve a class B in the constructor of another class (C) because you need the class (B) to be used with the dependencies resolved (you start using class C the way you want it like it were B but with the dependencies resolved).

like image 601
Ivana Stoilova Avatar asked Dec 21 '22 19:12

Ivana Stoilova


1 Answers

Why putting a container in a constructor is so bad?

I suppose you mean to pass the container as a constructor argument. This is actually a variation of the Service Locator pattern, which in this context is considered to be an anti pattern. There are a couple of reasons why you may not want to do this.

First, the users of your class will only know that the class needs a container for resolving its dependencies. This amount of information is equal to no information at all, because you still don't know what the class is going to depend on. Do you want to write a unit test for the class? You have to look inside the class and see what types it is resolving, mock them and initialize the container for every test. This also means that changes on some code will let it compile but may break some tests: this is the case when the new code relies on a class which is not yet registered in the container, for instance.

A secondary effect which is common when using Service Locator is that you can never be sure that you won't be getting an exception at runtime while asking for dependencies. Is every class registered correctly? While some containers offer the possibility to check if every interface is registered, it doesn't mean it is registered to the correct type. For instance, it could happen that a type is registered twice with two different implementations and it is going to be difficult to notice if any piece of code could call the container.

A better solution to this is the Composition Root pattern. This blog post also explains why Service Locator may not be a good idea.


EDIT in light of the new developments:

Apparently you are using a third party library which relies on your classes having a default constructor. Let us assume that you have no way to influence the instantiation of your classes and that you have to let this framework do its job. Be aware that this may be a big assumption, please investigate the third party library for possibilities to do it first. At first glance, frameworks like ASP.NET WebForms and WCF don't give you many chances, but there are ways to ease the pain for these cases.

I meant just to create the container in the constructor, add the respective dependency to the container and resolve the object, which can be done by simply creating instance of the dependency object and use it to create the dependent object.

I may be missing something, but why do you need to register the dependency in the constructor? Couldn't you just resolve it in the constructor but register it somewhere else? That would still be a Service Locator, but you would at least be doing the wrong thing right.

Why doing so in the constructor is a bad idea and doing so elsewhere is fine?

Doing so anywhere but in one place is a bad idea. Why would you spread your container registration all over the place? If you really feel the need to decide what implementation of an interface to use at runtime, use something like a Factory.

So, why is it bad?

  • the client class depends on both implementation and interface, which doesn't make it better than newing the concrete class in the constructor.
  • the client class now also depends on the container, and the issues of Service Locator arise (see above), making now this approach worse than newing the concrete class.

As @Steven said, you lose all advantages of Dependency Injection. The real underlying question is: why do you absolutely want to do DI in this place? What advantages of the approach would you like to use? Based on the answer there could be several solutions. Two examples off the top of my head:

Solution 1: lose the DI for the classes being instantiated by the third party library.

Solution 2: Use a combination of Bastard Injection + Service Locator. Two wrongs could make a right in this case.

public class MyClass
{
    public MyClass()
        : this(Container.Resolve<IDependency>())
    {
    }

    public MyClass(IDependency dep)
    {
    }
}

In this case you are not using a non-local dependency in the constructor, because it is resolved by the Service Locator, so you have no dependencies on the implementation.

like image 133
Filippo Pensalfini Avatar answered May 22 '23 23:05

Filippo Pensalfini