🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
Introduction
The Dependency Inversion Principle (DIP) is one of the five SOLID principles of object-oriented design. It states that high-level modules should not depend on low-level modules. Both should depend on abstractions. Furthermore, abstractions should not depend on details. Details should depend on abstractions. This principle promotes loose coupling between software components.
Table of Contents
- What is the Dependency Inversion Principle?
- Benefits of the Dependency Inversion Principle
- Example: Violation of DIP
- Example: Adherence to DIP
- Real-World Example
- Conclusion
1. What is the Dependency Inversion Principle?
The Dependency Inversion Principle (DIP) asserts that:
- 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.
This principle ensures that the system's high-level policy does not depend on the low-level details but rather on an abstraction.
2. Benefits of the Dependency Inversion Principle
- Loose Coupling: Promotes loose coupling between classes and components.
- Flexibility: Enhances flexibility and makes it easier to change or replace components.
- Reusability: Encourages the development of reusable components.
- Testability: Improves testability by allowing dependencies to be easily mocked or stubbed.
3. Example: Violation of DIP
In this example, we'll create a LightBulb class and a Switch class that violates DIP by depending directly on LightBulb.
Example:
class LightBulb {
public void turnOn() {
System.out.println("LightBulb is turned on");
}
public void turnOff() {
System.out.println("LightBulb is turned off");
}
}
class Switch {
private LightBulb lightBulb;
public Switch(LightBulb lightBulb) {
this.lightBulb = lightBulb;
}
public void flip(boolean on) {
if (on) {
lightBulb.turnOn();
} else {
lightBulb.turnOff();
}
}
}
public class Main {
public static void main(String[] args) {
LightBulb lightBulb = new LightBulb();
Switch lightSwitch = new Switch(lightBulb);
lightSwitch.flip(true); // Output: LightBulb is turned on
lightSwitch.flip(false); // Output: LightBulb is turned off
}
}
Issues:
- The
Switchclass depends directly on theLightBulbclass, creating tight coupling. - Any change in the
LightBulbclass requires changes in theSwitchclass.
4. Example: Adherence to DIP
To adhere to DIP, we can introduce an abstraction for the LightBulb class and make the Switch class depend on this abstraction.
Example:
Step 1: Define the Switchable Interface
interface Switchable {
void turnOn();
void turnOff();
}
Step 2: Implement the LightBulb Class
class LightBulb implements Switchable {
@Override
public void turnOn() {
System.out.println("LightBulb is turned on");
}
@Override
public void turnOff() {
System.out.println("LightBulb is turned off");
}
}
Step 3: Implement the Switch Class
class Switch {
private Switchable switchable;
public Switch(Switchable switchable) {
this.switchable = switchable;
}
public void flip(boolean on) {
if (on) {
switchable.turnOn();
} else {
switchable.turnOff();
}
}
}
Step 4: Main Class to Demonstrate DIP
public class Main {
public static void main(String[] args) {
Switchable lightBulb = new LightBulb();
Switch lightSwitch = new Switch(lightBulb);
lightSwitch.flip(true); // Output: LightBulb is turned on
lightSwitch.flip(false); // Output: LightBulb is turned off
}
}
Explanation:
- Switchable: An interface that defines the
turnOnandturnOffmethods. - LightBulb: A class that implements the
Switchableinterface. - Switch: A class that depends on the
Switchableinterface rather than theLightBulbclass. - Main: Demonstrates the use of DIP by creating a
Switchobject that depends on theSwitchableinterface.
5. Real-World Example
Example: Payment Processing System
Consider a payment processing system where different payment methods (e.g., credit card, PayPal) need to be processed.
Step 1: Define the PaymentProcessor Interface
interface PaymentProcessor {
void processPayment(double amount);
}
Step 2: Implement Specific Payment Classes
class CreditCardPaymentProcessor implements PaymentProcessor {
@Override
public void processPayment(double amount) {
System.out.println("Processing credit card payment of $" + amount);
}
}
class PayPalPaymentProcessor implements PaymentProcessor {
@Override
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of $" + amount);
}
}
Step 3: Implement the PaymentService Class
class PaymentService {
private PaymentProcessor paymentProcessor;
public PaymentService(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void makePayment(double amount) {
paymentProcessor.processPayment(amount);
}
}
Step 4: Main Class to Demonstrate DIP
public class Main {
public static void main(String[] args) {
PaymentProcessor creditCardPayment = new CreditCardPaymentProcessor();
PaymentService paymentService = new PaymentService(creditCardPayment);
paymentService.makePayment(100.0); // Output: Processing credit card payment of $100.0
PaymentProcessor paypalPayment = new PayPalPaymentProcessor();
paymentService = new PaymentService(paypalPayment);
paymentService.makePayment(200.0); // Output: Processing PayPal payment of $200.0
}
}
Explanation:
- PaymentProcessor: An interface that defines the
processPaymentmethod. - CreditCardPaymentProcessor and PayPalPaymentProcessor: Classes that implement the
PaymentProcessorinterface. - PaymentService: A class that depends on the
PaymentProcessorinterface. - Main: Demonstrates the use of DIP by creating a
PaymentServiceobject that depends on thePaymentProcessorinterface.
6. Conclusion
The Dependency Inversion Principle (DIP) is a fundamental concept in object-oriented design that promotes loose coupling between high-level and low-level modules by depending on abstractions rather than concrete implementations. By adhering to DIP, developers can create more maintainable, flexible, and testable code. Understanding and applying DIP is essential for building robust and scalable Java applications.
Happy coding!
Comments
Post a Comment
Leave Comment