Skip to main content

Definition of Dependency Inversion Principle

Dependency Inversion Principle (DIP)

Definition

Formal Definition

The Dependency Inversion Principle states: "High-level modules should not depend on low-level modules. Both should depend on abstractions."

This means that the core functionality of an application (high-level modules) should not be directly tied to specific implementations (low-level modules). Instead, both should rely on abstract interfaces or classes.

Simplified Definition

In simpler terms, the Dependency Inversion Principle means that your main business logic should not be directly connected to specific details. Instead, it should work with general concepts (abstractions), making it easier to change the details without affecting the overall system.

Key Concept

The key concept of DIP is to depend on abstractions rather than concrete implementations. Here’s what this involves:

  • Abstractions: These are interfaces or abstract classes that define the general behavior expected in a system. They do not contain specific implementation details.
  • Concrete Implementations: These are the specific classes that implement the abstractions, providing the detailed behavior.

Example:

Imagine you are developing a payment processing system:

  • Without DIP:
    • The high-level module (e.g., PaymentProcessor) directly depends on a low-level module (e.g., CreditCardPayment).
    • If you need to add a new payment method (e.g., PayPalPayment), you would have to modify the PaymentProcessor class, which violates DIP.
class CreditCardPayment {
public void process() {
System.out.println("Processing credit card payment");
}
}

class PaymentProcessor {
private CreditCardPayment payment;

public PaymentProcessor() {
this.payment = new CreditCardPayment();
}

public void processPayment() {
payment.process();
}
}
  • With DIP:
    • The high-level module (PaymentProcessor) depends on an abstraction (PaymentMethod).
    • Low-level modules (CreditCardPayment, PayPalPayment) implement the abstraction.
    • Adding new payment methods does not require changes to the PaymentProcessor class.
interface PaymentMethod {
void process();
}

class CreditCardPayment implements PaymentMethod {
@Override
public void process() {
System.out.println("Processing credit card payment");
}
}

class PayPalPayment implements PaymentMethod {
@Override
public void process() {
System.out.println("Processing PayPal payment");
}
}

class PaymentProcessor {
private PaymentMethod payment;

public PaymentProcessor(PaymentMethod payment) {
this.payment = payment;
}

public void processPayment() {
payment.process();
}
}

By depending on abstractions rather than concrete implementations, DIP allows for greater flexibility, easier maintenance, and improved scalability of the system.