Problem Statement
When high-level modules depend directly on low-level modules, changes in low-level details force changes in high-level logic. This tight coupling makes the system fragile and hard to maintain or extend.
This diagram shows both high-level and low-level modules depending on a shared abstraction, which decouples their implementations.
### Before applying Dependency Inversion Principle (tightly coupled) class MySQLDatabase: def connect(self): print("Connecting to MySQL database") class UserService: def __init__(self): self.db = MySQLDatabase() # Direct dependency def get_user(self, user_id): self.db.connect() print(f"Fetching user {user_id}") ### After applying Dependency Inversion Principle (depends on abstraction) from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def connect(self): pass class MySQLDatabase(Database): def connect(self): print("Connecting to MySQL database") class UserService: def __init__(self, db: Database): self.db = db # Depends on abstraction def get_user(self, user_id): self.db.connect() print(f"Fetching user {user_id}") # Usage mysql_db = MySQLDatabase() service = UserService(mysql_db) service.get_user(42)
Dependency Inversion Principle in system design?Payment that both PaymentProcessor and CreditCardPayment depend on, following Dependency Inversion.PaymentProcessor to CreditCardPayment by direct reference or instantiation violate DIP. interface PaymentProcessor { void process(); } class CreditCardPayment implements PaymentProcessor { void process() { /* pay with card */ } } reverses roles incorrectly.interface Logger {
void log(String message);
}
class ConsoleLogger implements Logger {
public void log(String message) {
System.out.println("Console: " + message);
}
}
class Application {
private Logger logger;
public Application(Logger logger) {
this.logger = logger;
}
public void run() {
logger.log("App started");
}
}
public class Main {
public static void main(String[] args) {
Logger logger = new ConsoleLogger();
Application app = new Application(logger);
app.run();
}
}ConsoleLogger and passes it to Application. Calling app.run() calls logger.log("App started").ConsoleLogger.log prints "Console: " plus the message, so output is "Console: App started".class FileStorage {
public void save(String data) {
// save data to file
}
}
class DataManager {
private FileStorage storage = new FileStorage();
public void saveData(String data) {
storage.save(data);
}
}Notifier interface), not concrete classes. Create an interface Notifier with method send(message), and implement it in EmailNotifier, SMSNotifier, and PushNotifier. The high-level NotificationManager depends on Notifier interface. follows this by using interface and implementations.Pass the test and this pattern moves unknown