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):
- Java
- JavaScript
- Python
// 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();
}
}
// Abstraction
class PaymentMethod {
process() {
throw new Error('This method should be overridden')
}
}
// High-Level Module
class PaymentProcessor {
constructor(payment) {
this.payment = payment
}
processPayment() {
this.payment.process()
}
}
// Low-Level Modules
class CreditCardPayment extends PaymentMethod {
process() {
console.log('Processing credit card payment')
}
}
class PayPalPayment extends PaymentMethod {
process() {
console.log('Processing PayPal payment')
}
}
// Usage
const creditCardPayment = new CreditCardPayment()
let processor = new PaymentProcessor(creditCardPayment)
processor.processPayment() // Outputs: Processing credit card payment
const payPalPayment = new PayPalPayment()
processor = new PaymentProcessor(payPalPayment)
processor.processPayment() // Outputs: Processing PayPal payment
from abc import ABC, abstractmethod
# Abstraction
class PaymentMethod(ABC):
@abstractmethod
def process(self):
pass
# High-Level Module
class PaymentProcessor:
def __init__(self, payment):
self.payment = payment
def process_payment(self):
self.payment.process()
# Low-Level Modules
class CreditCardPayment(PaymentMethod):
def process(self):
print("Processing credit card payment")
class PayPalPayment(PaymentMethod):
def process(self):
print("Processing PayPal payment")
# Usage
credit_card_payment = CreditCardPayment()
processor = PaymentProcessor(credit_card_payment)
processor.process_payment() # Outputs: Processing credit card payment
paypal_payment = PayPalPayment()
processor = PaymentProcessor(paypal_payment)
processor.process_payment() # Outputs: Processing PayPal payment
Violation Example
Here is a code snippet that violates DIP by having the high-level module directly depend on the low-level module:
- Java
- JavaScript
- Python
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();
}
}
class CreditCardPayment {
process() {
console.log('Processing credit card payment')
}
}
class PaymentProcessor {
constructor() {
this.payment = new CreditCardPayment()
}
processPayment() {
this.payment.process()
}
}
class CreditCardPayment:
def process(self):
print("Processing credit card payment")
class PaymentProcessor:
def __init__(self):
self.payment = CreditCardPayment()
def process_payment(self):
self.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:
- Java
- JavaScript
- Python
// 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();
}
}
// Abstraction
class PaymentMethod {
process() {
throw new Error('This method should be overridden')
}
}
// High-Level Module
class PaymentProcessor {
constructor(payment) {
this.payment = payment
}
processPayment() {
this.payment.process()
}
}
// Low-Level Modules
class CreditCardPayment extends PaymentMethod {
process() {
console.log('Processing credit card payment')
}
}
class PayPalPayment extends PaymentMethod {
process() {
console.log('Processing PayPal payment')
}
}
// Usage
const creditCardPayment = new CreditCardPayment()
let processor = new PaymentProcessor(creditCardPayment)
processor.processPayment() // Outputs: Processing credit card payment
const payPalPayment = new PayPalPayment()
processor = new PaymentProcessor(payPalPayment)
processor.processPayment() // Outputs: Processing PayPal payment
from abc import ABC, abstractmethod
# Abstraction
class PaymentMethod(ABC):
@abstractmethod
def process(self):
pass
# High-Level Module
class PaymentProcessor:
def __init__(self, payment):
self.payment = payment
def process_payment(self):
self.payment.process()
# Low-Level Modules
class CreditCardPayment(PaymentMethod):
def process(self):
print("Processing credit card payment")
class PayPalPayment(PaymentMethod):
def process(self):
print("Processing PayPal payment")
# Usage
credit_card_payment = CreditCardPayment()
processor = PaymentProcessor(credit_card_payment)
processor.process_payment() # Outputs: Processing credit card payment
paypal_payment = PayPalPayment()
processor = PaymentProcessor(paypal_payment)
processor.process_payment() # Outputs: Processing PayPal payment
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.