Loading Light/Dark Toggle
 All publications

<h1>

Dependency Injection & Dependency Inversion Principle 

</h1>

Updated on Dec 30, 2024 . 4 min read

<article>

In software development, two often misunderstood concepts —Dependency Injection (DI) and the Dependency Inversion Principle (DIP)— play crucial roles in building scalable, maintainable, and testable applications. While they are closely related, they serve different purposes.

Dependency Injection is a design pattern focused on how dependencies are provided to a class, making code more modular and easier to test. On the other hand, the Dependency Inversion Principle, part of the SOLID principles, is a design guideline that encourages us to depend on abstractions rather than concrete implementations.

In this post, we'll explore these two concepts, how they relate to each other, and how they differ in purpose and implementation.

Dependency Injection (DI)

It is a design pattern where a class receives (or is "injected with") the dependencies it needs from the outside, rather than creating them itself. This pattern enhances modularity, testability, and flexibility in an application.

Key Concepts:

  1. Dependency: A class that another class relies on to perform its tasks.
  2. Injection: Providing that dependency from an external source, usually through the constructor, a method, or a property.

Types of Injection:

  1. Constructor Injection: Dependencies are passed as constructor parameters.
class Service { private final Repository repository; public Service(Repository repository) { this.repository = repository; } }
  1. Setter Injection: Dependencies are provided via setter methods.
class Service { private Repository repository; public void setRepository(Repository repository) { this.repository = repository; } }
  1. Interface Injection: The class implements an interface that defines how dependencies should be injected.

Advantages of DI:

  • Decoupling: Classes depend on abstractions rather than concrete implementations.
  • Testability: Mocks and stubs can be easily injected for unit testing.
  • Flexibility: Dependencies can be changed without altering the dependent class.

Example Without Dependency Injection

In this example, the Service class directly creates an instance of MySQLRepository within its implementation. This creates a rigid dependency between Service and MySQLRepository, making it harder to switch implementations or perform testing.

class MySQLRepository { public void save(String data) { System.out.println("Data saved in MySQL: " + data); } } class Service { private MySQLRepository repository; public Service() { this.repository = new MySQLRepository(); // Dependency created internally } public void saveData(String data) { repository.save(data); } } public class Main { public static void main(String[] args) { Service service = new Service(); service.saveData("Test data"); } }

Problems:

  1. Service directly depends on the concrete implementation (MySQLRepository), violating the Dependency Inversion Principle.
  2. It’s inflexible. Changing to another implementation of Repository (e.g., PostgresRepository) would require modifying the Service code.
  3. It’s hard to test since you cannot inject a mock or stub of MySQLRepository.

Solution with Dependency Injection

Here, we apply dependency injection to decouple Service from the specific implementation of Repository.

// Define an abstraction interface Repository { void save(String data); } // Concrete implementation 1 class MySQLRepository implements Repository { public void save(String data) { System.out.println("Data saved in MySQL: " + data); } } // Concrete implementation 2 class PostgresRepository implements Repository { public void save(String data) { System.out.println("Data saved in PostgreSQL: " + data); } } // Class depending on an abstraction class Service { private Repository repository; // Dependency injected through the constructor public Service(Repository repository) { this.repository = repository; } public void saveData(String data) { repository.save(data); } } // Usage in the main program public class Main { public static void main(String[] args) { // Inject the concrete implementation into the constructor Repository repository = new MySQLRepository(); // Easily switch to PostgresRepository Service service = new Service(repository); service.saveData("Test data"); } }

Advantages of the solution:

  1. Decoupling: Service depends on an interface (Repository), not on concrete implementations (MySQLRepository or PostgresRepository).
  2. Flexibility: Changing the Repository implementation is as simple as instantiating a different class (PostgresRepository).
  3. Testability: You can inject a mock or stub of Repository to perform unit tests without relying on real databases.

Dependency Inversion Principle (DIP)

The Dependency Inversion Principle is one of the SOLID principles and states that:

  1. High-level modules (core logic) should not depend on low-level modules (implementation details). Both should depend on abstractions.
  2. Abstractions should not depend on details; details should depend on abstractions.
Inversion of control
Inversion of control

Summary

It can be said that the Dependency Inversion Principle (DIP) provides the foundation for Dependency Injection (DI).

DIP ensures that both high-level modules (business logic) and low-level modules (implementation details) depend on abstractions rather than concrete implementations. This abstraction allows for the creation of flexible and decoupled systems, which is precisely what Dependency Injection takes advantage of.

In other words:

  • DIP defines the principle: "Depend on abstractions, not on concretions."
  • DI is the practical application of this principle by providing those abstractions to a class from the outside, enabling easy replacement, configuration, and testing of dependencies.

Thus, DI is a way to implement the idea proposed by DIP.


You can find this and other posts on my Medium profile some of my projects on my Github or on my LinkedIn profile.

¡Thank you for reading this article!

If you want to ask me any questions, don't hesitate! My inbox will always be open. Whether you have a question or just want to say hello, I will do my best to answer you!

</article>