πŸ“ What You’ll Learn

  • What Dependency Inversion really means
  • Why high-level modules shouldn’t depend on low-level ones
  • Practical code examples
  • How to apply DIP using interfaces and dependency injection

πŸ“– What Is the Dependency Inversion Principle?

The Dependency Inversion Principle says:

High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.

In other words:
Depend on interfaces, not concrete implementations.

❌ A Typical Violation of DIP

public class LightBulb {
    public void turnOn() { ... }
    public void turnOff() { ... }
}

public class Switch {
    private LightBulb bulb = new LightBulb();

    public void operate() {
        bulb.turnOn(); // tightly coupled
    }
}

Here, Switch depends directly on LightBulb, a low-level module. Changing LightBulb means modifying Switch.

βœ… Applying DIP with Abstraction

Introduce an interface:

public interface Switchable {
    void turnOn();
    void turnOff();
}

Now decouple:

public class LightBulb implements Switchable {
    public void turnOn() { ... }
    public void turnOff() { ... }
}

public class Switch {
    private Switchable device;

    public Switch(Switchable device) {
        this.device = device;
    }

    public void operate() {
        device.turnOn();
    }
}

Now, Switch depends on the abstraction Switchable, not the concrete class.

πŸ§ͺ Real-World Analogy: Payment Gateway

Imagine your app handles payments:

class PaymentService {
    private PayPalAPI paypal = new PayPalAPI();

    public void pay() {
        paypal.sendMoney();
    }
}

Tomorrow, you switch to Stripe. That’s a nightmare.

Better:

interface PaymentGateway {
    void pay();
}

class PayPalGateway implements PaymentGateway { ... }
class StripeGateway implements PaymentGateway { ... }

class PaymentService {
    private PaymentGateway gateway;

    public PaymentService(PaymentGateway gateway) {
        this.gateway = gateway;
    }

    public void processPayment() {
        gateway.pay();
    }
}

Now you can swap payment providers without rewriting business logic.

🧠 Benefits of DIP

  • 🚦 Loose coupling
  • πŸ”„ Easy to change implementations
  • πŸ§ͺ Better testability (mocks, stubs)
  • πŸ”§ Cleaner architecture

πŸ“˜ Recap

  • DIP = Rely on abstractions, not concrete implementations
  • Both high-level and low-level modules should depend on interfaces
  • Enables flexibility, testability, and long-term maintainability

🏁 Wrapping Up the SOLID Series

Congratulations! You've now learned:

  1. Single Responsibility Principle
  2. Open/Closed Principle
  3. Liskov Substitution Principle
  4. Interface Segregation Principle
  5. Dependency Inversion Principle

You're on the path to writing clean, robust, and scalable object-oriented code.