DEV Community

Cover image for Loose Coupling and Dependency Injection (DI) principle
Vasil Vasilev
Vasil Vasilev

Posted on • Edited on

Loose Coupling and Dependency Injection (DI) principle

What is the Dependency Injection (DI) principle?

The Dependency Injection principle is a software design pattern that promotes loose coupling and high modularity in object-oriented programming.
With simple words, we provide dependencies to a class from the outside rather than create in the class itself.

Instead of this:

class UserService {

  logging(message) {
    console.log(message);
  }

  addUser(user) {
    // Add user logic...
    this.log(`User added: ${user}`);
  }
}


const userService = new UserService(); // Create the UserService

// Usage
userService.addUser("John Doe");
Enter fullscreen mode Exit fullscreen mode

We do this:

// Logger class
class Logger {
  logging(message) {
    console.log(message);
  }
}

// UserService class with Logger dependency
class UserService {
  constructor(logger) {
    this.logger = logger;
  }

  addUser(user) {
    // Add user logic...
    this.logger.logging(`User added: ${user}`);
  }
}

// Creating instances and injecting dependencies

const logger = new Logger(); 
// Create a Logger instance

const userService = new UserService(logger); 
// Inject the Logger instance into UserService

// Usage
userService.addUser("John Doe");
Enter fullscreen mode Exit fullscreen mode

The reason for such an abstraction is to achieve separations of concerns. We want our addUser function to be part of our UserServices class, but our logging function being too fundamental to fix into only one class, should be abstracted away from UserServices class into its own class.

Why? The answer is re-usability. Therefore, we abstract logging function and inject it as a dependency via the constructor.

Constructor

The constructor, thus, is a crucial tool since it not only initialises class properties but also serves as an entrypoint for its class' dependencies on other classes (the UserService doesn't create a Logger instance internally but relies on the Logger instance provided to it).

Summary

We go from thigh coupling (logger function being an integral part of UserService class) to loose coupling (The Logger instance is independent of the UserService, and we can easily replace it with a different Logger implementation without modifying the UserService code), thus, achieving reusability and abstraction.

Top comments (0)