Skip to main content

Practical Examples of Dependency Inversion Principle

Dependency Inversion Principle (DIP)

Practical Examples

Good Design

Here is a code snippet showing an implementation that adheres to the Dependency Inversion Principle (DIP):

// Abstraction
interface PaymentMethod {
void process();
}

// High-Level Module
class PaymentProcessor {
private PaymentMethod payment;

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

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

// Low-Level Modules
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");
}
}

// Usage
public class Main {
public static void main(String[] args) {
PaymentMethod creditCardPayment = new CreditCardPayment();
PaymentProcessor processor = new PaymentProcessor(creditCardPayment);
processor.processPayment();

PaymentMethod payPalPayment = new PayPalPayment();
processor = new PaymentProcessor(payPalPayment);
processor.processPayment();
}
}

Violation Example

Here is a code snippet that violates DIP by having the high-level module directly depend on the low-level module:

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();
}
}

Explanation: In this example, the PaymentProcessor class directly depends on the CreditCardPayment class. This violates DIP because if you need to change the payment method (e.g., add PayPal or Bitcoin payments), you have to modify the PaymentProcessor class. This tight coupling makes the system less flexible and harder to maintain.

Refactored Example

Refactor the violating example to adhere to DIP:

// Abstraction
interface PaymentMethod {
void process();
}

// High-Level Module
class PaymentProcessor {
private PaymentMethod payment;

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

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

// Low-Level Module
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");
}
}

// Usage
public class Main {
public static void main(String[] args) {
PaymentMethod creditCardPayment = new CreditCardPayment();
PaymentProcessor processor = new PaymentProcessor(creditCardPayment);
processor.processPayment();

PaymentMethod payPalPayment = new PayPalPayment();
processor = new PaymentProcessor(payPalPayment);
processor.processPayment();
}
}

In the refactored example, the PaymentProcessor class depends on the PaymentMethod abstraction rather than the CreditCardPayment concrete implementation. This adherence to DIP allows for greater flexibility and easier maintenance, as new payment methods can be added without modifying the high-level module.