Dependency Inversion Principle: Definition and Examples
Dependency Inversion Principle is a design guideline that says high-level modules should not depend on low-level modules; both should depend on abstractions like interfaces or abstract classes. It helps make code flexible and easier to change by reducing direct dependencies between parts.How It Works
Imagine you have a TV remote (high-level module) and a TV (low-level module). Instead of the remote knowing exactly how the TV works inside, it talks through a common interface like buttons and signals. This way, you can swap the TV with another brand without changing the remote.
The Dependency Inversion Principle works the same way in software. Instead of a high-level part of your program depending directly on a low-level part, both depend on an abstract layer. This abstraction hides details and allows parts to change independently.
This reduces tight coupling, which means changes in one part don’t break others. It also makes testing easier because you can replace real parts with simple mock versions.
Example
This example shows a simple way to apply the Dependency Inversion Principle using interfaces in Python.
from abc import ABC, abstractmethod class Switchable(ABC): @abstractmethod def turn_on(self): pass @abstractmethod def turn_off(self): pass class LightBulb(Switchable): def turn_on(self): print("LightBulb: turned on") def turn_off(self): print("LightBulb: turned off") class Switch: def __init__(self, device: Switchable): self.device = device def operate(self, on: bool): if on: self.device.turn_on() else: self.device.turn_off() # Usage light = LightBulb() switch = Switch(light) switch.operate(True) switch.operate(False)
When to Use
Use the Dependency Inversion Principle when you want to make your code easier to maintain and extend. It is especially helpful in large projects where many parts interact.
For example, in a payment system, instead of hardcoding a specific payment gateway, you define an interface for payment processing. This lets you add new gateways without changing the main code.
It also helps when writing tests, because you can replace real parts with simple mock objects that follow the same interface.
Key Points
- High-level modules should not depend on low-level modules directly.
- Both should depend on abstractions like interfaces or abstract classes.
- Abstractions should not depend on details; details depend on abstractions.
- Reduces tight coupling and improves flexibility.
- Makes testing and maintenance easier.