Sobes.tech
Junior

What is dependency injection and what benefits does it bring?

sobes.tech AI

Answer from AI

Dependency Injection (DI) is a design pattern where components receive dependencies from outside rather than creating them themselves. It is one of the principles of Inversion of Control (IoC).

Benefits of DI:

  1. Improved testability: Easier to substitute mocks/stubs for dependencies during unit testing.
  2. Loose Coupling: Components depend less on specific implementations of dependencies.
  3. Increased flexibility and extensibility: Easily change dependency implementations without modifying the main code of the component.
  4. Improved readability and maintainability: Clearly shows what dependencies a component requires.

Example without DI vs with DI:

Without DI:

// Class that creates a dependency inside itself
public class ServiceA {
    private DatabaseConnection dbConnection;

    public ServiceA() {
        // Strong coupling: explicitly creating a specific implementation
        this.dbConnection = new OracleConnection(); 
    }

    public void doSomething() {
        dbConnection.connect();
        // ...
    }
}

With DI (via constructor):

// Dependency interface
public interface DatabaseConnection {
    void connect();
    void disconnect();
}

// Different implementations
public class OracleConnection implements DatabaseConnection {
    @Override
    public void connect() {
        System.out.println("Connecting to Oracle...");
    }
    @Override
    public void disconnect() {
        System.out.println("Disconnecting from Oracle...");
    }
}

public class MySQLConnection implements DatabaseConnection {
    @Override
    public void connect() {
        System.out.println("Connecting to MySQL...");
    }
    @Override
    public void disconnect() {
        System.out.println("Disconnecting from MySQL...");
    }
}

// Class receiving dependency from outside
public class ServiceB {
    private DatabaseConnection dbConnection;

    // Dependency injection via constructor
    public ServiceB(DatabaseConnection dbConnection) {
        this.dbConnection = dbConnection;
    }

    public void doSomething() {
        dbConnection.connect();
        // ...
    }

    public void setDbConnection(DatabaseConnection dbConnection) {
        this.dbConnection = dbConnection;
    }
}

// Code performing injection (configuration)
public class Application {
    public static void main(String[] args) {
        // Create dependency
        DatabaseConnection connection = new MySQLConnection(); 
        // Inject dependency into ServiceB
        ServiceB service = new ServiceB(connection); 
        service.doSomething();

        // Or change dependency without modifying ServiceB
        DatabaseConnection anotherConnection = new OracleConnection();
        ServiceB anotherService  = new ServiceB(anotherConnection);
        anotherService.doSomething();
    }
}

Methods of injection:

  • Constructor Injection: Dependencies are provided via the class constructor. Preferred for mandatory dependencies.
  • Setter Injection: Dependencies are provided via setter methods. Suitable for optional dependencies.
  • Field Injection: Dependencies are injected directly into class fields (commonly used with frameworks, requires reflection). Not recommended without DI frameworks due to testing complexity and hidden dependencies.
What is dependency injection and what benefits does… - sobes.tech