Dependency Inversion Principle (DIP) — depend on abstractions
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
DIP says high-level policy and low-level implementation should meet through an abstraction shaped by the policy's needs. For example, OrderService may depend on PaymentGateway.charge(Money), while a Stripe adapter implements that contract using the Stripe SDK. The service does not import vendor types, and the adapter's source dependency points toward the application contract.
The principle is broader than 'always use interfaces.' A custom interface around String or a simple immutable value adds little. I apply DIP where database, messaging, filesystem, clock, or vendor details would otherwise leak into business rules. Dependency injection can provide the chosen adapter, but the abstraction and dependency direction are what make the design DIP.
Dependency Inversion Principle (DIP) — depend on abstractions
Direct Answer
DIP says high-level policy should not depend directly on low-level implementation details; both should depend on abstractions, and abstractions should be shaped by policy rather than by incidental details. In Java the abstraction may be an interface or abstract type, but DIP does not require an interface for every class.
Code Example
// High-level module defines the abstraction it needs
public interface OrderRepository {
Order findById(Long id);
void save(Order order);
}
// Low-level module depends on the abstraction (implements it)
@Repository
public class MySQLOrderRepository implements OrderRepository {
public Order findById(Long id) {
// JDBC or JPA code to query MySQL
return new Order(id, "Sample Item");
}
public void save(Order order) { /* persist to MySQL */ }
}
// High-level business logic depends on abstraction, not concrete class
@Service
public class OrderService {
private final OrderRepository repository; // interface, not MySQLOrderRepository
public OrderService(OrderRepository repository) {
this.repository = repository; // Spring injects concrete implementation
}
public Order processOrder(Long orderId) {
var order = repository.findById(orderId); // works with any implementation
// business logic here
repository.save(order);
return order;
}
}
Common Mistakes & Pitfalls
- Defining DIP as 'use interfaces': ownership and dependency direction matter.
- Putting vendor types in the abstraction: the detail still leaks into policy.
- Creating an interface for every class: this adds cost without a boundary.
- Confusing runtime call direction with source dependency direction: service calls the adapter at runtime while the adapter implements the inward contract.
- Assuming DI proves DIP: concrete injection can still couple policy to detail.
Check your understanding
Answer these without looking back at the article.
- What is the difference between DIP and Dependency Injection?
- How does Spring implement DIP?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.