After defining contracts using interfaces and abstract classes, the next problem appears naturally in real systems:
How do we connect all these components without tightly coupling them?
This is where Dependency Injection (DI) becomes essential.
The Problem Without Dependency Injection
When a class creates its own dependencies, it becomes tightly coupled.
Example
class OrderService:
def __init__(self):
self.payment = CardPayment()
What’s wrong here?
- OrderService depends on a specific implementation
- You cannot easily switch payment methods
- Testing becomes difficult
- Changes in one class affect others
This creates rigid systems.
What is Dependency Injection?
Dependency Injection means:
Providing dependencies from the outside instead of creating them inside the class.
Correct Design
class OrderService:
def __init__(self, payment):
self.payment = payment
Now the dependency is injected externally.
Usage
service = OrderService(CardPayment())
or
service = OrderService(UpiPayment())
What Changed?
- OrderService no longer decides what payment system to use
- responsibility is moved outside
- system becomes flexible
Why Dependency Injection Matters
1. Loose Coupling
Classes do not depend on concrete implementations.
2. Easy Testing
Mock objects can replace real dependencies.
3. Flexibility
You can change behavior without modifying core logic.
Real System Example
Notification System
class NotificationService:
def __init__(self, notifier):
self.notifier = notifier
def notify(self, message):
self.notifier.send(message)
Now different implementations can be injected:
- EmailNotifier
- SmsNotifier
- PushNotifier
Key Design Insight
Classes should focus on what they do, not what they depend on.
Dependency Injection vs Object Creation
| Without DI | With DI |
|---|---|
| class creates dependencies | dependencies passed externally |
| tight coupling | loose coupling |
| hard to test | easy to test |
| rigid design | flexible design |
Where Object Creation Should Happen
If classes don’t create dependencies, then who does?
- Caller
- Factory
- Composition Root (central setup layer)
This separation is critical for clean architecture.
Common Mistake
Beginners often:
- mix creation logic inside business classes
- overuse DI without structure
- confuse DI with factories
Important Clarification
Dependency Injection is NOT:
- a framework
- a design pattern for creation
It is a design principle for decoupling.
Design Principle
Depend on abstractions, not concrete implementations — and receive dependencies instead of creating them.
Real-World Analogy
Think of a mobile phone:
- it does not create a SIM card internally
- SIM is inserted externally
The phone works regardless of SIM provider.
One-Line Takeaway
Dependency Injection decouples system components by moving responsibility of object creation outside the class.
Top comments (0)