The Dependency Inversion Principle (D in SOLID) has two parts: high-level modules (business rules) must not depend on low-level modules (SMTP, databases, files, the clock); both must depend on abstractions. And abstractions must not depend on details: the details depend on abstractions.
The violation: a hard-wired detail
OrderService creates EmailSender() in its constructor. The business rule now knows the delivery channel. Sending an SMS means editing the service, and a test can only run by sending a real email.
class EmailSender:
def send(self, to, text):
print(f"EMAIL to {to}: {text}")
class OrderService:
def __init__(self):
self.sender = EmailSender() # hard-wired detail
def place(self, user, item):
self.sender.send(user, f"order {item} accepted")
OrderService().place("ann@x.io", "book")EMAIL to ann@x.io: order book accepted