There is a wicked truth behind the use of an Inversion of Control container. An unspoken programming tragedy takes place every day while passing unnoticed. Firm in my intentions to stop this shame, I decided to stand up and start writing this series of articles that will tell about the problem, the principles and the possible solutions.
I started to notice the symptoms of this “blasphemy” (against the code gods 🙂 ) a long time ago, but back then I couldn’t pin down the reason for them. Nevertheless, the problem was pretty clear: the IoC container solution was scarily used too often as an alternative to the Singleton pattern to inject dependencies. With the code growing and the project evolving, many classes started to take the form of a blob of functions, with a common pitfall: dependencies, dependencies everywhere.

What means and what is wrong with using an IoC container as a mere substitute for a Singleton, is something that I am going to describe the best I can with this series of articles. I realised that IoC containers cannot be used successfully without understanding the inversion of control principle and that’s why I started to look for a safer solution that could be adopted even by inexperienced coders. Before taking a look at this solution, let’s start taking some steps back and explain what Dependency Injection actually is:

What Dependency Injection is

Dependency Injection isn’t anything fancy. A dependency is just an interface used by the methods of a class. Usually, dependencies are solved in two ways: injecting them through constructor parameters or passing them with singletons. Singletons break encapsulation in the sense that, as a global variable, they can be used without a scope. Singletons awkwardly hide your dependencies: there is nothing in the class interface showing that the dependency is used internally. Singletons strongly couple your implementations, resulting eventually in too long and painful code refactoring. For these reasons, it’s preferable to inject dependencies manually:

This example shows how to inject dependencies via the constructor. Now, would you pass 10 or more parameters by constructor? I surely wouldn’t, if nothing else, for how painful and inconvenient it would be. The same reasoning should apply when an IoC container is used. Just because it’s more convenient to use, it doesn’t mean you can take advantage of it. Injecting a ton of dependencies means that the code is not well layered/structured and does not use sound communication patterns. The result is just a mess with object coupling again. To be honest, if the design of the code would really follow the SOLID[1] principles, this problem wouldn’t occur, since the number of dependencies injected is directly linked to the number of responsibilities a class has got. A single responsibility should lead to very few dependencies injected. However, without a proper paradigm to follow, we all know that coders tend to break the Open/Close principle and add behaviours to existing classes instead to adopt a modular and extendible design. That’s when IoC containers start to be dangerous, since they actually help this process, making it less painful.

Where do the injected dependencies come from? If the dependencies are injected by constructor, they obviously come from the scope where the object that needs the dependencies injected has been created. In its turn, the class that is injecting the dependencies into the new object could need also dependencies injected, which therefore are passed from another class in the parent scope. This chain of dependency passages creates a waterfall of injections and the relationship between these objects is called Object Dependency Graph[1].

Albeit, where does this waterfall start from? It starts from the place where the dependencies are created. This place is called Composition Root. Root because is where the context is initialised, composition because is where all the dependencies are created and injected and, therefore, the initial relations composed.

Now you can see what the real problem of the Unity framework is: the absence of a Composition Root. Unity doesn’t have a “main” class where the relations between objects can be composed. This is why the only way to create relationships with the bare Unity framework is using Singletons or static classes/methods.

Why are relationships between objects needed? Mainly to let them communicate with each other. All forms of communications involve dependency injection. The only pattern that allows communication without not static dependency injection is the pattern called Event Bus[2] in the Java environment. The Event bus allows communications through events held by a Singleton, hence the Event Bus is one of the many anti-patterns out there. Note that you could think to create something similar to an Event Bus without using a singleton (therefore injecting it). That’s an example of what I define to use injection as a mere substitute for a Singleton.

Object Communication and Dependency Injection

Communication can couple or not couple objects, but in all cases injection is involved. There are several ways to let objects communicate:

  • Interface injection: usually the interface A is injected in B, B is coupled with A [e.g.: Inside a B method A is used, B.something() { A.something());]
  • Events: usually B is injected in A, A is coupled with B [e.g.:Inside A, B is injected to expose the event, B.onSomething += A.Something]
  • Commands: B and A are uncoupled, B could call a command that calls a method of A. Commands are great to encapsulate business logic that could potentially change often. A Command Factory is usually injected in B.
  • Mediators: usually B and A do not know each other, but know their mediator. B and A pass themselves into the mediator and the mediator wires the communication between them (i.e.: through events or interfaces). Alternatively, B and A are passed to the mediator outside B and A themselves, totally removing the dependency on the Mediator itself. This is my favourite flavour and the closest to dependency-less possible.
  • Various other patterns like Observers, Event Queue[3] and so on.

How to pick up the correct one? If we don’t have guidelines it looks like one or another is the same. That’s why sometimes we end up using, randomly, one of these. Remember the first two patterns are the worst because they couple interfaces that could change over time.

We can anyway introduce the first sound practice of our guideline for our code design: our solution must minimize the number of dependencies.

Of course, the second sound practice is about following the concept of Single Responsibility Principles[4] (and Interface Segregation Principle[5]). One of the 5 principles of SOLID (ISP is another fundamental one), but the only one that must be actually taken as a rule. Your class must have one responsibility only. Communicating could be considered a responsibility, therefore it’s better to delegate it.

How we are going to achieve SRP and solve the dependencies blob problem is something I am going to explain in the next articles of this series.


[1] SOLID (object-oriented design)

[2] Event Bus

[3] Event Queue

[4] Single Responsibility Principle

[5] Interface Segregation Principle 

I strongly suggest to read all my articles on the topic:

5 2 votes
Article Rating
Notify of

1 Comment
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
André "Intentor" Martins
André "Intentor" Martins
7 years ago

Fantastic article, Sebastiano!

And “our solution must minimize the number of dependencies” should be turned into a mantra!