Skip to content
Vikyath Shirva

Dependency Injection at a high level

- Vikyath Shirva

If you're hearing this term for the first time in object-oriented design, it might feel confusing. What is it and why do we need it?

The main intention of this concept is to decouple the dependency relationships between objects. In any object-oriented design, we have a lot of objects that are important to the functioning of the application. There can exist some meaningful relationships between these objects, based on how you design it or how the business logic should work. We establish these relationships with dependencies.

Here's an example: in order for a man to eat food that is kept on a plate, given that the man exists, there must be some food on the plate. This is a dependency. This is how you picture or model some real-world things. So here's the relationship: there must be food on the plate for the man sitting in front of it to eat. The man's eating behavior is dictated by the existence of food on the plate — that is what a dependency is.

Now, in an object-oriented context, we always try to achieve as much generality as possible — in other words, good levels of abstraction for quality code. What I mean by this: let's assume the man eats different types of food in different ways. Imagine we have two types of food — a bowl of noodles and a packet of biscuits.

Potions Class

Now this is my application class, and here we have a particular way this man eats the noodles and biscuits. Each type has its own eat method. What this means is that the man eats noodles in a different way, so you create a new object of Noodles and run its eat method when eating noodles. When the man eats biscuits, you create a new object of Biscuits and run its eat method.

Potions Class

Now, what we have here is a particular implementation, and we notice that in this application class we've tied the specific food type inside it. If we think forward — what if this application class needs to change? What if the man needs to eat a new type of food? We'd obviously need to come back into the application class and write something like this:

Potions Class

Now this isn't a problem since it's just a single food type. But what if this keeps happening — you keep needing to add new types every now and then? So what if you could create something that would automatically run the eat method for any type of food?

That is a great place to dive in for achieving good abstraction. Let's try this using polymorphism. How do we go about it?

We can achieve this using some kind of parent class or something specific like an Interface. So what we do is use this Interface to run the eat method, and when the application is running, we send it different children of this parent class — at that time, the child's method gets executed. Here's what I mean:

Potions Class

Now, the food types Noodles and Biscuit inherit the FoodType's eat method. So what good does this inheritance do? When I specify the Noodles type to the interface, the Noodles eat method runs. Similarly, if I specify Biscuit type to the interface, the Biscuit's eat method runs. Perfect, right? We've achieved some kind of generality here.

Potions Class

So what are we doing differently here? Right now we can just use foodType to run its eat method, and through the different implementation type (or food types like Noodles or Biscuit), it knows which method to override (which eat method to run based on the type of food).

Now to go even further with abstraction and get rid of the way we're specifying the types to the interface, here's what we'll do:

Potions Class

What we did here is create a method that just takes in the FoodType. All this does is: if the foodType is Noodles, it executes the eat method of Noodles, and so on for other types. What's going on is that myEatMethod just knows that some kind of foodType will be passed in and it has an eat method. How that eat method behaves is not its concern — as you can see, it sits unbothered. Just send it a foodType and it'll run the specific eat method.

But here's the new problem: even though the method looks generic, it still needs that FoodType to be created as a specific type and then sent into this method. Which means at some point in your code, in this class or elsewhere, you'd need to create something like this:

Potions Class

We'd need this new instantiation of a specific FoodType. We'd need to change this instantiation whenever the type changes, but we wouldn't need to change myEatMethod since it's already generic. We still have this instantiation somewhere that we need to take care of.

So what's happening is that every time we have a new instantiation, it's happening in our application class.

Potions Class

What we can try to achieve next is this:

Potions Class

Instead of a specific Noodles object, we could have a generic FoodType object where some other class gives us the specific food types.

Potions Class

Here's how we'll achieve this. We create our Eating class:

Potions Class

What we have here is a member variable of type FoodType that we make private, and we create a setter for it. What this method does is: no matter what foodType you pass in, it's going to set the class's foodType to the one that's sent in, and you can use the eatFood method to execute its specific eat method. Notice that it doesn't do any instantiation of a particular type — no "new" keyword being used.

This is to say that the Eating class assumes it's going to receive an already-instantiated foodType. This is an example of setter injection — one of the ways to do Dependency Injection. Another common approach, and the one generally recommended in Spring, is constructor injection, where the dependency is passed through the constructor instead of a setter.

What we've achieved here is a good level of abstraction. We've removed the dependency on a specific food type from the Eating class. If you pass a Noodles foodType, it sets the foodType to Noodles and you can run the eatFood method to execute Noodles' specific eat method.

So in some other class, this is what's going to happen:

Potions Class

The advantage we've achieved here is that we went from this to this:

Potions Class

We've created a new generic Eating class that is now free from any specific FoodType. Any other class can send in its foodType and the Eating class will know how to eat. The whole idea is that we don't ever need to modify the Eating class for any new types of food — it's generic now. Just pass the type and eat. We can now say that the Eating class is generic, and its dependency (the FoodType) is being injected by some other class. It's not hardcoded into the class — we've decoupled the dependency.

Now you might have a question about this part:

Potions Class

Yes, something needs to handle this work. Even though we achieved a dependency-free Eating class, in the context of the Spring framework, this "other class" is taken care of by Spring itself. You don't have to write it — just configure Spring (using annotations like @Component, @Autowired, or @Bean) so that the dependencies you want are injected into the right objects.

So, this was an explanation of what Dependency Injection is about. I'll put up another post on how Dependency Injection works at a lower level, so see you then.

© 2026 by Vikyath Shirva. All rights reserved.