OOP · SOLID Principles
Dependency Inversion Principle (DIP)
Prevents large and complex interfaces.
Benefits
- Prevents large and complex interfaces.
- Reduces unnecessary method implementations.
- Improves code readability and maintainability.
- Makes interfaces easier to understand and reuse.
- Encourages loosely coupled software design.
Best Practices
- Keep interfaces small and focused.
- Group related methods into separate interfaces.
- Let classes implement only the interfaces they actually need.
- Review interfaces regularly as requirements evolve.
- Prefer multiple small interfaces over one large interface.
Common Mistakes
- Creating "fat" interfaces with unrelated methods.
- Forcing classes to implement unused methods.
- Using empty method bodies to satisfy an interface.
- Combining unrelated responsibilities into one interface.
- Ignoring interface cohesion.
Real-World Problem
Imagine you're developing an E-Commerce Notification System. Initially, whenever an order is placed, the application sends a confirmation through Email. The NotificationService class directly creates an EmailService object and sends the notification.
As the business grows, customers request notifications through SMS, WhatsApp, and Push Notifications. Every time a new notification method is introduced, developers modify the NotificationService class to create a different service object.
Although the application works, the notification module becomes tightly coupled to specific implementations. Any change in the notification mechanism requires changes to the main business logic, making the application difficult to maintain and extend.
Problem The high-level class (NotificationService) directly depends on low-level classes (EmailService, SMSService, etc.). Whenever a new notification channel is added, the high-level class must be modified. This tight coupling makes the code less flexible, harder to test, and more difficult to extend.
Pain Points
The NotificationService is tightly coupled to EmailService. If the business decides to switch to SMS, WhatsApp, or Push Notifications, the existing class must be modified. This violates flexibility, increases maintenance effort, and makes unit testing difficult because dependencies cannot be easily replaced with mock implementations.
Solution
The Dependency Inversion Principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions. Instead of directly creating an EmailService object, NotificationService should
depend on a common interface such as Notification. Different notification methods can implement this interface, allowing new services to be introduced without modifying the high-level business logic .

Java Program 1 – DIP Violation

Java Program 2 – DIP Applied

Depend on abstractions, not concretions — high-level code shouldn't hardwire low-level classes.
interface MessageSender { // abstraction
void send(String msg);
}
class EmailSender implements MessageSender {
public void send(String msg) { System.out.println("Email: " + msg); }
}
class NotificationService {
private final MessageSender sender; // depends on the interface
NotificationService(MessageSender sender) { // injected, not 'new'ed
this.sender = sender;
}
void alert(String msg) { sender.send(msg); }
}