Dependency Injection for Dummies

Kritika Agarwal
7 min readJul 20, 2021

--

There are 2 reasons really why I am penning down this article.

  1. I failed at a job interview once because I didn’t know what dependency injection means even though I blatantly uttered Spring is used as a framework for dependency injection.
  2. Because everytime I have to reject a candidate who explains the advantages of DI as low coupling but have no idea what coupling/cohesion means, I cringe a lil.

Even though an awful lot of literature exists on what dependency injection is or why people are hell bent on using dependency injection, I’m convinced it’s worth taking a shot to explain these terms in my own words and hope it helps other people create the right mental models around these concepts.

Q) What is DI?

A real life situation of what a dependency looks like is, let’s go a lil back to your childhood when you used to ask money from your parents, or you got pocket money. For everything you needed to buy, the function of receiving money was then dependent on your parents and if we model you (the child) as a class, you’d have to inject your parents as a dependency.

In programming languages we always try to follow a single responsibility principle, that each class should be responsible to do only 1 and no more things. It is due to this, classes are bound to be dependent on each other e.g. if you as a child class were only supposed to focus on studies, there would be a need to add parents as dependency to GetMoney(). Now that we’ve established me as a jerk child, using parents to mint money, let’s move to a hypothetical scenario where you don’t need dependency injection, and let’s see how we model it.

There are really 2 basic rules when it comes to making an application use the concept of Dependency Injection.

  1. All dependencies of a class must be defined.
  2. Whenever you call a class, it is the responsibility of the caller to give all class dependencies.

Let’s see what the above 2 rules mean via an example.

public class Portfolio {
private Stocks stocksAndSmallCase;
private FixedDeposits fd;
private PhysicalAssets goldAndLand;
private CryptoCurrency coins;
private MutualFunds mfs;
private LiquidCash cash;

Portfolio(Stocks sc, FixedDeposits fd, MutualFunds mf, PhysicalAssets. inheritance, CryptoCurrency dogecoin, LiquidCash blackMoney){
this.stocksAndSmallCase = sc;
this.fd=fd;
this.mfs=mf;
this.goldAndLand=inheritance;
this.cash=blackMoney;
this.coins = dogecoin;
}
}

Here, the first few lines are basically defining all dependencies of the class Portfolio. This is the rule #1, i.e. we need to clearly define all dependencies of a class.

As per the second rule, rather than creating objects of all these dependencies by ourselves, we’re basically depending on the caller to create all the dependencies, and pass it to this class. Only once all these dependencies are created, can someone try to create objects of the Portfolio class, since the Portfolio constructor needs all this data pre-populated. P.S. This is not the only way to do DI. The dependencies can be injected via constructors, getter-setter methods or just as member variables. More on this later.

Q) Why DI?

To answer why DI, let’s again take a good look at the rules. The first rule says to define every dependency beforehand. But how does that help us?

Have you ever tried to cook a recipe by watching it on youtube? Care to think why people mention all ingredients beforehand?

Though some people like to swing it the other way, that is getting ingredients or creating dependencies as they need it, but it might bring in complications i.e. the ingredient/dependency might be unavailable when you try and fetch it. The first rule helps in avoiding such circumstances, with a no surprises later policy. Once you’ve clearly outlined your dependencies, you’d know everything you require to create this class. Not to mention testing becomes extremely easy because you can now mock these dependencies and do your thing.

This brings us to the second rule, the caller providing all the dependencies. This rule is to make sure that there is a central place where objects get created which is then passed on in your application at different points. To take a real life scenario, let’s say you wish to eat an icecream. So you go to a restaurant, order ice cream for yourself, and come back. Now suppose your brother feels like having a milkshake, so he also has to drive to the same restaurant, and fetch a shake for himself. If your family is anything like mine, this arouses other family members to have a good meal, and one by one each of them take the keys, drive the car, fetch their order from the restaurant and come back. By now, you must have realised the smart thing to do here was to ask everybody if they want anything while you planned to go to the restaurant, and then bring the order for each member of the family.

Using the second rule, we’re guaranteeing the same thing where each class doesn’t create their own objects(doesn’t drive the car and fetch food themselves) and rather everything is created at a central place and then used in different classes depending on their requirement. The benefit of doing this is less number of tasks to be done by individual classes, and object creation and object lifecycle is now not managed in each class. Less Objects = Less Memory Utilisation. For simplicity, let’s picture that the way dependency injection looks in a real life project is, there is one class which creates all the objects required in the whole application, e.g. it will create all the objects of stocks, mutual funds etc. and while creating a Portfolio class it will provide these objects so that Portfolio class can get initialised with the same.

Q) Advantages?

Other than simplicity of code, and developer’s life, DI way of doing things also has some other advantages.

  1. Can be used to create singleton objects: If your application wants to make sure only 1 object is created for a given class, you can make sure of it by doing the DI way.
  2. Object lifecycle management is handled well throughout the whole application. If you create objects in each class, it takes a lot of time for the garbage collector to make sure these objects aren’t referenced anymore and that they need to clean them.
  3. If you plan to change your object, i.e. rather than using your dad’s stock broker now you want to shift to Zerodha, you can make this change at a single place and rest assured that things will be working fine. The whole jazz about design patterns, and best practices is to make sure when you make a change in one part of the application, it doesn’t break everything else. Suggest to read coupling and cohesion for the same.
  4. Rule #2 also ensure that all the dependency issues are caught at compile time. i.e. when you compile your code and if something breaks in one of the dependent classes, Portfolio class will not be created and you’ll get to know about it in compile time itself, rather than at runtime, while you’re running your application, and try to create/access your portfolio. Trust me, it’s a good thing.

Q) Are spring and DI in a relationship?

Yes! Spring and DI are in a healthy relationship i.e. they don’t need the other to exist but they really support each other well.

Remember somewhere above I mentioned that there is one single class which is responsible for creating all the objects? Well that would be an awful lot of work for a single class to do, and since this pattern is heavily used across projects we actually have a better way of doing it.

So, rather than a single class doing this job, Spring uses a concept called an IOC container. What it means is, rather than an application creating objects and providing it, the control of creating and managing the objects now resides with a framework which we refer to as IOC or DI container. The container is now responsible to create all the objects, and provide it on runtime thereby removing the necessity of a particular class to do all the gruntwork.

Having said that, DI is just a concept, a design technique and as long as you follow the 2 rules, you’re good to introduce DI to your application without using Spring, or any fancy frameworks. Also, spring has ample libraries, frameworks apart from IOC which makes your life much much easier as an application developer and it’s a real catch.

Implementation?

To implement dependency injection, let’s take the help of Spring to create objects and automatically add them. Please checkout link to understand the various ways in which dependencies can be injected.

  1. Go to springboot link, and download a new spring boot project with options chosen as below. Here, choose Spring Web from dependencies.

2. Open this project in your ide (Eclipse, or Intellij or whatever you use) and create a new class Portfolio.

@RestController
public
class Portfolio {
@Autowired
private
Stocks stocks;
@RequestMapping("/")
public String getPortfolio(){
return stocks.addStocks();
}}

3. Create a new class Stock, and add stocks here:

@Service
public
class Stocks {
public String addStocks(){
return "Added 1000 Tesla Shares to your account.";
}}

Now run the Application, and that’s all you need to become a billionaire, in your mind. Imagination is a beautiful place to be in.
Hope you enjoyed this article, stay tuned for more fun tech content. Please comment down on similar buzzwords you’d want me to take a stab at.

--

--

Kritika Agarwal
Kritika Agarwal

Written by Kritika Agarwal

Software Engineer, Mentor, Blogger, Shitposter, hustling for goals to become history. https://linktr.ee/kritika.agarwal

Responses (3)