Explore Amazon Deals

「Spring」Explaining Dependency Injection (DI) and IoC

Hey everyone, today I'm going to share about two concepts that are both troubling and memorable for many developers. To work with Spring and its ecosystem, the first, essential, and only thing you need to do is to thoroughly understand the definition of these two things.

So what are they? Let's delve into the details.

Oh, wait, first, you have to read this article:

Concepts of Tight Coupling and Loosely Coupled

Dependency Injection (DI)

In the document, it says:

"Dependency Injection is a design pattern, ..."

So in simple terms, it's a programming method, a design to make your code more efficient. Before this method came about, you still coded normally, but now, following it will greatly benefit your programming.

So, what does Dependency Injection tell us to do? 🙃 (Just a simple way to put it)

Let me explain it to you through an example like this:

public class Girl {
    private Bikini outfit; // each girl will have a bikini when going out
    public Girl() {
        outfit = new Bikini(); // When you create a girl, you give her a Bikini for example
    }
}

First of all, through this code, you'll notice that when you create a Girl, you'll create a Bikini with her. At this point, Bikini exists, meaning it's a dependency of Girl.

By initializing attributes like this, you unintentionally create a bottleneck in your program. For instance, what if the Girl wants to wear a Dress + T-shirt or nothing at all? Would you change the Bikini class to SkirtWithTshirt or Naked, huh?

Or more dangerously, what if the Bikini outfit is damaged? (The Bikini class code doesn't work?) It will directly affect the Girl.

The issue here lies in the principle:

"Classes should not depend on low-level inheritance but rather on Abstraction."

It might sound a bit complex. Now let's change the code like this:

      
// An interface for dressing up
public interface Outfit {
    public void wear();
}

// A low-level object, implementing Outfits
public class Bikini implements Outfit {
    public void wear() {
        System.out.println("Wearing Bikini");
    }
}

// Now, Girl only depends on Outfit. If you want to change her outfit, you just need to give Outfit a new instance.
public class Girl {
    private Outfit outfit;
    public Girl() {
        outfit = new Bikini();
    }
}

At this point, we've abstracted the Girl's attribute, but in reality, Girl is still tied to a single Bikini. So, to change clothes for the girl, what do you have to do?

You have to further modify the code like this:

      
public class Girl {
    private Outfit outfit;
    public Girl(Outfit anything) {
        this.outfit = anything; // Creating a girl with a customizable outfit
        // Not too much dependency at the time of initialization or code.
    }
}

public class Main {
    public static void main(String[] agrs) {
        Outfit bikini = new Bikini(); // Creating a Bikini object outside the object
        Girl britSpear = new Girl(bikini); // Dress her in it when creating her.
}

With the above code, we've almost completely separated Bikini from Girl. This reduces the dependency between Girl and Bikini, enhancing code customization and flexibility.

Now, Girl will work with Outfit only. And where is Outfit from? We create it and pass it (Inject) into Girl.

Dependency Injection is when Objects should depend on Abstract Classes, and their specific instances will be Injected into the object at runtime.

There are several ways to Inject dependencies into an object:

  • Constructor Injection: This is the example I just gave, injecting the dependency directly into the Constructor for convenience.

  • Setter Injection: Oh, why not 😗 We learned about Setters from previous lessons, which makes sense. Using girl.setOutfit(new Naked()) 😈

  • Interface Injection: Every Class wanting to inject something must implement an Interface containing an inject(xx) function (almost like a replacement for the setter). Then when you want to inject something, call the inject(xx) function. This method is a bit longer and more challenging for newcomers.

Inversion of Control (IoC)

Dependency Injection helps us easily extend code and reduce the interdependency between dependencies. However, at this point, when coding, you'll have to take on the additional responsibility of Injecting dependencies. Imagine a Class with dozens of dependencies, and you have to manually inject each one. It leads to difficulties in coding, code management, and dependency.

      
public static void main(String[] args) {
    Outfit bikini = new Bikini();
    Accessories gucci = new GucciAccessories();
    HairStyle hair = new KoreanHairStyle();
    Girl ngocTrinh = new Girl(bikini, gucci, hair);
}

If there were someone to do this for us, wouldn't it be great?

Now, assuming that we define all the dependencies in the Project beforehand, describe them, and put them in a repository, then hand them over to a framework to manage. Whenever any Class is initialized and needs a dependency, this framework will automatically find it in the repository and inject it into the object for us. Wouldn't that be more convenient?

That's it! That's exactly the principle of Inversion of Control (IoC) - controlling the flow of control within the application not by the application itself but by the underlying framework.

The code then only needs something like this to get an object:

      
@Override
public void run(String... args) throws Exception {
    Girl girl = context.getBean(Girl.class);
}

For Java, some Frameworks support Inversion of Control (IoC) for us. Among them, the prominent ones are:

  • Spring framework
  • Google Guice

The Spring framework is a framework from the early days, created to realize the concept of Inversion of Control (IoC). However, over time, Spring has grown strong, becoming a vast ecosystem serving many functionalities based on this IoC.

Google Guice came later and focused solely on DI.

Just take a look at the code examples, and you'll understand the issue much better.

Conclusion

Here, I've shared with you the concepts of Dependency Injection and Inversion of Control. You might know how they originated and what problems they aim to solve. I hope you've gained a closer, practical, and easier-to-understand perspective.

Wishing you good learning and remember to share it with fellow learners.

Next Post Previous Post
No Comment
Add Comment
comment url