Definition:
High-level modules should not depend on low-level modules; both should depend on abstractions. Additionally, abstractions should not depend on details; details should depend on abstractions.
Explanation:
The Dependency Inversion Principle encourages decoupling high-level and low-level modules by introducing abstraction layers, typically through interfaces or abstract classes. This allows high-level modules to remain unaffected by changes in low-level modules, enhancing flexibility and facilitating easier maintenance and testing.
Example:
Consider a LightBulb class directly used by a Switch class. To adhere to DIP, introduce an abstraction that both depend on.
# Violates DIP
class LightBulb:
def turn_on(self):
pass
def turn_off(self):
pass
class Switch:
def __init__(self, bulb: LightBulb):
self.bulb = bulb
def operate(self, on: bool):
if on:
self.bulb.turn_on()
else:
self.bulb.turn_off()
# Adheres to DIP by introducing an abstraction
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):
# Turn on the bulb
pass
def turn_off(self):
# Turn off the bulb
pass
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()