Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP) is the last of the five SOLID principles. It says that your important business code should not depend on low-level details. Both should depend on abstractions (interfaces).
Definition
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.
- High-level module: the business logic (what the app does), such as placing an order.
- Low-level module: the details (how it is done), such as MySQL, an email service, or a payment API.
Normally, the business code calls the details directly, so it depends on them. DIP inverts this: the business code defines an interface, and the details implement it.
Analogy: a lamp does not wire itself to the power station. It has a standard plug, and any socket that fits will work. The lamp depends on the plug standard (the abstraction), not on a specific power source.
Why It Matters
When business code is tied to a concrete detail:
- Changing the detail (a new database, a new payment provider) forces you to edit the business code.
- Testing is hard. To test an order, you need a real database and a real email server.
- Reuse is hard. The business logic cannot move to another project without its details.
A Violation
public class MySqlDatabase {public void save(Order order) {// write to MySQL}}public class EmailService {public void send(String to, String message) {// send a real email}}public class OrderService {// The high-level class creates its own low-level detailsprivate MySqlDatabase database = new MySqlDatabase();private EmailService emailService = new EmailService();public void placeOrder(Order order) {database.save(order);emailService.send(order.getCustomerEmail(), "Order placed");}}
OrderService is glued to MySQL and to this exact email class:
- Moving to PostgreSQL means editing
OrderService. - A unit test of
placeOrderreally writes to a database and sends an email.
1Before: OrderService ----> MySqlDatabase (business depends on a detail)The Fix
Step 1: the high-level code defines what it needs as an interface. Step 2: the details implement that interface. Step 3: the dependency is passed in from outside (dependency injection).
// Abstractions, owned by the business layerpublic interface OrderRepository {void save(Order order);}public interface NotificationSender {void send(String to, String message);}// Details implement the abstractionspublic class MySqlOrderRepository implements OrderRepository {public void save(Order order) { /* write to MySQL */ }}public class EmailNotificationSender implements NotificationSender {public void send(String to, String message) { /* send an email */ }}// High-level code depends only on abstractionspublic class OrderService {private final OrderRepository repository;private final NotificationSender sender;public OrderService(OrderRepository repository, NotificationSender sender) {this.repository = repository;this.sender = sender;}public void placeOrder(Order order) {repository.save(order);sender.send(order.getCustomerEmail(), "Order placed");}}
1After: OrderService ----> OrderRepository <---- MySqlOrderRepository2 (interface)The arrow from the detail now points up to the interface. That is the inversion.
Easy to Swap
To move to PostgreSQL or a different notification channel, add a new class. OrderService does not change.
public class PostgresOrderRepository implements OrderRepository {public void save(Order order) { /* write to PostgreSQL */ }}public class SmsNotificationSender implements NotificationSender {public void send(String to, String message) { /* send an SMS */ }}// Only the wiring changesOrderService service = new OrderService(new PostgresOrderRepository(),new SmsNotificationSender());
Easy to Test
In a unit test, use fake versions. There is no database and no email server:
@Testvoid placeOrderSavesAndNotifies() {FakeOrderRepository repository = new FakeOrderRepository();FakeNotificationSender sender = new FakeNotificationSender();OrderService service = new OrderService(repository, sender);service.placeOrder(new Order("asha@example.com"));assertEquals(1, repository.savedOrders().size());assertEquals(1, sender.sentMessages().size());}
DIP vs Dependency Injection vs IoC
These three names are often mixed up:
| Term | What it is |
|---|---|
| Dependency Inversion (DIP) | A design principle: depend on abstractions, not on concrete details |
| Dependency Injection (DI) | A technique: pass the dependencies from outside (usually through the constructor) |
| Inversion of Control (IoC) | A wider idea: a framework or container controls object creation, instead of your code |
Spring is an IoC container. It creates the beans and injects them, so with @Service and constructor injection you get DI almost for free.
@Servicepublic class OrderService {private final OrderRepository repository; // an interface, not a concrete classpublic OrderService(OrderRepository repository) { // Spring injects the matching beanthis.repository = repository;}}
Note: DI is the tool, DIP is the goal. You can use DI and still break DIP, for example by injecting
MySqlDatabase(a concrete class) instead of an interface.
Spotting a Violation
newinside business classes, such asnew MySqlDatabase()ornew EmailService().- Business code imports low-level packages like
java.sqlor an SDK for a payment provider. - Fields typed as concrete classes, not interfaces.
- Unit tests need a real database or network.
- Changing a library forces edits across many business classes.
Common Mistakes
1. An interface for everything. A small utility or a class that will never change (like a StringFormatter) does not need one. Use abstractions at boundaries that change: databases, external APIs, and message queues.
2. Putting the interface next to the detail. The interface should belong to the high-level code (what the business needs). If it lives in the database package and mirrors its methods, the business still depends on the detail.
3. Calling it "DIP" because you used a framework. Using Spring does not guarantee DIP. Check that your fields are interfaces.
SOLID Together
| Letter | Principle | One-line idea |
|---|---|---|
| S | Single Responsibility | A class has one reason to change |
| O | Open/Closed | Extend with new code, do not modify old code |
| L | Liskov Substitution | A child can replace its parent safely |
| I | Interface Segregation | Prefer small interfaces over fat ones |
| D | Dependency Inversion | Depend on abstractions, not on details |
DIP ties the others together: OCP needs interfaces to plug into, ISP keeps them small, and LSP makes sure the implementations behave.
Summary
- DIP: high-level code and low-level details should both depend on abstractions.
- The business layer owns the interface, and the details implement it.
- The dependency is passed in (dependency injection), not created with
newinside the class. - Benefits: swap details easily, test without real systems, and reuse business logic.
- DIP is the principle, DI is the technique, and IoC containers like Spring do the wiring.
- Warning signs:
newin business code, concrete-class fields, and tests that need a real database. - Use abstractions at boundaries that change, not for every class.
Interview Answer: What Is the Dependency Inversion Principle?
The Dependency Inversion Principle says that high-level modules should not depend on low-level modules. Both should depend on abstractions. Business logic should not be tied to details like a specific database or email provider.
For example, if OrderService creates a MySqlDatabase with new, it is glued to MySQL, and testing it needs a real database. Instead, I define an OrderRepository interface that the business layer owns. A MySqlOrderRepository implements it, and I pass it into OrderService through the constructor. Now I can switch to PostgreSQL by adding a class, and I can test with a fake repository.
It is different from Dependency Injection, which is the technique of passing dependencies in, and from IoC, where a container like Spring creates and injects the objects. DI is how I achieve DIP. I use abstractions at the boundaries that are likely to change, like databases and external services, and not for every small class.