Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the new operator to create objects/dependencies vs. using DI containers

What's so bad about manually wiring up dependencies via constructor injection and what's so great about using a DI container (container.Resolve<T>()) as against that?

I mean, apart from the fact that you have to type a few more keystrokes every time you need to create your dependent object and supply it with all its dependencies.

In fact, having a central registry like a DI Container provides can be a heck of a lot more confusing and take away control from you.

I've had this question and another one (below) for some time, but I guess Mark Seemann's article titled Service Locator is an anti-pattern got me to finally type this question here.

So, my other question is:

What if there's a dependency that itself needs some dynamic (say user supplied) input for its construction? Say, like:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

class Dependent
{
    IDependency _dependency;

    void DoSomething()
    {
      var container = new MyFavoriteContainerFromTheMarket();

      Console.Write("Hey, user. How are you feeling?");
      var userInput = Console.ReadLine();

      // Do DI containers have overloads for Resolve instead of for Register
      // that accept parameters to be passed to constructors?
      // In other words, what if I don't know the constructor parameter
      // value at the time of registration of the dependency but only
      // get to know it just before I have to resolve/instantiate the
      // dependency? Do popular DI containers of today have overloads
      // for that? I assume they must
      _dependency = container.Resolve<IDependency>(userInput);
    }
}

Overall, doesn't it feel like DI Containers take away control from you? Wouldn't it just be okay to manually wire up dependencies in some cases?

Sure, I understand sometimes it is easy and saves you typing and creates for a cleaner, more terse code snippet, but having it all mixed up -- some dependencies managed by a container and some you have on your own that you manually supply -- all makes it so confusing and forces you to remember which one is which, and thus makes maintenance more difficult, wouldn't you agree?

Update

Oh, wait. I just realized my whole example was convoluted. I had meant to call the Resolve from client code in the main(). The code I should have originally posted with this question should have been like this:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

class Dependent
{
    IDependency _dependency;

    public Dependent(IDependency dependency)
    {
      _dependency = dependency;
    }

    public void DoSomething()
    {
    }
}

class Program
{
  public static void Main(string[] args)
  {
      var container = new MyFavoriteContainerFromTheMarket();

      container.Register<IDependency>(new Dependency(/* I want this to come later */));

      container.Register<Dependent>(new Dependent(), 
       new ConstructorInjection<IDependency>());

      Console.Write("Hey, user. How are you feeling?");
      var userInput = Console.ReadLine();

      // It appears I can't do this as it makes perfect sense
      // to have the whole tree of dependencies known at the time
      // of registration.
      var dependent = container.Resolve<Dependent>(userInput);

      dependent.DoSomething();
  }
}

I don't know what that update makes of this previous discussion then. It just looks like some idiot (me) not having though out the question in his head before asking. Asking the question itself forced the right answer, which @Maarten confirmed and was trying to help me with.

Sorry, @Maarten. I wasted your time. :-)

like image 466
Water Cooler v2 Avatar asked May 14 '14 10:05

Water Cooler v2


People also ask

What are the 3 types of DI?

Types of DI There are three main styles of dependency injection, according to Fowler: Constructor Injection (also known as Type 3), Setter Injection (also known as Type 2), and Interface Injection (also known as Type 1).

What are advantages of using DI?

A basic benefit of dependency injection is decreased coupling between classes and their dependencies. By removing a client's knowledge of how its dependencies are implemented, programs become more reusable, testable and maintainable.

What is the purpose of using DI?

Dependency Injection (DI) is a design pattern used to implement IoC. It allows the creation of dependent objects outside of a class and provides those objects to a class through different ways. Using DI, we move the creation and binding of the dependent objects outside of the class that depends on them.

Is IoC the same as DI?

Inversion of Control(IoC) is also known as Dependency injection (DI). The Spring container uses Dependency Injection (DI) to manage the components that build up an application and these objects are called Spring Beans. Spring implements DI by either an XML configuration file or annotations.


1 Answers

You shouldn't try to resolve an instance by calling the container. You should use constructor injection to inject your dependencies. Mark Seeman refers to this as Don't call the container, let it call you.

To solve your problem of creating an instance of an injected type with a run-time value, use abstract factories.

So your example can be solved like this:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

interface IDependencyFactory {
    IDependency Create(string userInput);
}

class DependencyFactory: IDependencyFactory {
    IDependency Create(string userInput) {
        return new Dependency(userInput);
    }
}

class Dependent {
    public Dependent(IDependencyFactory factory) {
        // guard clause omitted
        _factory = factory
    }

    private readonly IDependencyFactory _factory;

    void DoSomething() {
        // Do not call the container, let it call you.
        // So no container usage here.
        //var container = ...

        Console.Write("Hey, user. How are you feeling?");
        var userInput = Console.ReadLine();

        var dependency = _factory.Create(userInput);
        // There you go!
    }
}
like image 76
Maarten Avatar answered Oct 13 '22 09:10

Maarten