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 thePaymentProcessor
class, which violates DIP.
- The high-level module (e.g.,
- 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()
- 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.
- The high-level module (
- Java
- JavaScript
- Python
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();
}
}
class PaymentMethod {
process() {
throw new Error('This method should be overridden')
}
}
class CreditCardPayment extends PaymentMethod {
process() {
console.log('Processing credit card payment')
}
}
class PayPalPayment extends PaymentMethod {
process() {
console.log('Processing PayPal payment')
}
}
class PaymentProcessor {
constructor(payment) {
this.payment = payment
}
processPayment() {
this.payment.process()
}
}
const creditCardPayment = new CreditCardPayment()
const paymentProcessor = new PaymentProcessor(creditCardPayment)
paymentProcessor.processPayment() // Outputs: Processing credit card payment
const payPalPayment = new PayPalPayment()
const anotherPaymentProcessor = new PaymentProcessor(payPalPayment)
anotherPaymentProcessor.processPayment() // Outputs: Processing PayPal payment
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def process(self):
pass
class CreditCardPayment(PaymentMethod):
def process(self):
print("Processing credit card payment")
class PayPalPayment(PaymentMethod):
def process(self):
print("Processing PayPal payment")
class PaymentProcessor:
def __init__(self, payment):
self.payment = payment
def process_payment(self):
self.payment.process()
credit_card_payment = CreditCardPayment()
payment_processor = PaymentProcessor(credit_card_payment)
payment_processor.process_payment() # Outputs: Processing credit card payment
paypal_payment = PayPalPayment()
another_payment_processor = PaymentProcessor(paypal_payment)
another_payment_processor.process_payment() # Outputs: Processing PayPal payment
By depending on abstractions rather than concrete implementations, DIP allows for greater flexibility, easier maintenance, and improved scalability of the system.