DEV Community

Saras Growth Space
Saras Growth Space

Posted on

LLD Object-Oriented Design: Dependency Injection (Decoupling System Design)

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()
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now the dependency is injected externally.


Usage

service = OrderService(CardPayment())
Enter fullscreen mode Exit fullscreen mode

or

service = OrderService(UpiPayment())
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)