Question 91

Question

Explain how to implement a simple dependency injection container.

Answer

Why Dependency Injection?

  • Loose Coupling: DI promotes loose coupling between components. Instead of classes directly creating their dependencies, they receive them as parameters. This makes code more modular, testable, and easier to maintain.

  • Reusability: Dependencies can be swapped out easily without modifying the core logic of a class, allowing for flexibility and reuse across different parts of your application.

Basic JavaScript DI Container

class DependencyInjectionContainer {
  constructor() {
    this.dependencies = {}; 
  }

  register(name, dependency) {
    this.dependencies[name] = dependency;
  }

  resolve(name) {
    return this.dependencies[name];
  }
}

// Example Usage

class Database {
  constructor() {
    console.log("Database connected"); 
  }
}

class UserService {
  constructor(database) {
    this.database = database;
  }

  getUserById(id) {
    console.log(`Fetching user with ID ${id} using database:`, this.database);
  }
}

const container = new DependencyInjectionContainer();
container.register('Database', Database); // Register the Database dependency

const userService = container.resolve('UserService'); 
userService.getUserById(1);

Explanation:

  1. DependencyInjectionContainer Class: This class acts as our DI container. It has a dependencies object to store registered dependencies by name.

  2. register(name, dependency) Method: Used to add dependencies to the container. The name is used to identify the dependency later, and dependency is the actual instance or constructor function of the class.

  3. resolve(name) Method: Retrieves a registered dependency by its name. It returns the associated instance or function.

  4. Example Usage:

    • We define a Database class to represent database interactions (simplified).

    • The UserService class depends on a database. Instead of creating the database directly, it takes it as a parameter in its constructor.

    • In the main part:

      • We create an instance of DependencyInjectionContainer.

      • We register the Database class with the container using container.register('Database', Database).

      • We resolve the UserService (which depends on the Database), and it receives a database instance injected by the container.

Key Points:

  • Flexibility: You can easily change dependencies without modifying classes that use them.

  • Testability: It's much easier to test individual components when they receive their dependencies through injection, allowing you to control what they interact with during tests.

Last updated