What Dependency Injection Is? And Why?

Background

Dependency injection is a powerful design pattern that used in various popular frameworks and libraries. Spring framework in JVM eco-system, for example, is one of the most popular frameworks, that built heavily around dependency injection.

In this article, inversion of control, dependency injection, and other related concepts will be explained, with their beneifts and drawbacks, by a coding situation.

Situation

You are creating a component which would do some logging to help user troubleshoot. When planning this feature, you are thinking how to handle logs generated.

There are few dozens ways to handle logs including print to console, append to files, pipe to elastic search, etc. Implement all of them is not feasible.

Solution

Inversion of Control

Since you don't do it, you allow the component user to write their own way to process log. This is the concept of inversion of control. In other words, you give the control over logging to the consumer.

For this to work, you need to add a new layer of abstraction for consumer's code to response to logging, while this implies extra codes, but this is much better than catching up all the logging possibilities out there.

Inheritance

You make your class abstract and force user to override the logging functions. This utilizes template pattern, that is, you define specific steps, i.e. one logging step, and it is up to user to decide how to implement it.

However, inheritance is restrictive. For example,

Composite over Inheritance is a principle in object oriented programming addressing the inflexibility caused by inheritance, but this is out of the scope of this article.

Dependency Injection

To avoid limitations caused by inheritance, you decide to take logging out to another abstract class and your component will then depend on the concrete implementation provided by user. This pattern is called dependency injection, that you allow user to inject dependency you required by their own version. In this way, each objects are focused in single responsibility, so the code is more manageable, and reusable.

As the code base grows, more dependencies are introduced, and these relationships become more complex. Although libraries and frameworks, such as Spring for JVM, can help to manage these dependency graphs, sometimes graph problems, e.g. circular dependency, still need to be handled by hand.

Event bus

Another way is to use an event bus, so your component will post a log event to a bus, and the listener, again, implemented by user, will react to that event, like append to log file.

Using event bus, logging and your component are decoupled, and they only need to depend on the event and the specific logging event. Also, adding extra log handling is relatively easy.

However, this comes with some drawbacks.

Closing

Here, the concepts, pros and cons of inversion of control, dependency injection and event bus are covered. I hope this will help you can understand them, and pick proper patterns to write more good code. Happy coding 🙂

If you like this article, remember to show your support by buy me a book.