CLOSE
Updated on 05 Jul, 202613 mins read 5 views

Inversion of Control (IoC)

"Don't control your dependencies; let someone else control them for you."

or more generally,

Inversion of Control is a design principle in which the control of creating, managing, or invoking objects is transferred from your code to another component (called the framework, container, or runtime).

Why Does IoC Exist?

Imagine you are building an online shopping application.

You write:

class EmailService
{
public:
    void sendEmail()
    {
        // Send email
    }
};

class OrderService
{
private:
    EmailService emailService;

public:
    void placeOrder()
    {
        // Process order

        emailService.sendEmail();
    }
};

Looks fine.

But ask yourself:

Who create EmailService?

Answer:

OrderService did.

So OrderService is controlling its dependency.

What Problems Does This Cause?

Suppose tomorrow:

You want to use:

  1. SMTP
  2. SendGrid
  3. Amazon SES
  4. MockEmailService (for testing)

Now OrderService must change.

class OrderService
{
private:
    SendGridEmailService emailService;
};

Tomorrow:

class OrderService
{
private:
    SMTPEmailService emailService;
};

Every time the implementation changes, OrderService changes.

This violates:

  • Open/Closed Principle
  • Depenency Inversion Principle

What Does “Inversion” Mean?

Normally, your class says:

“I need an EmailService, so I will create one.”

With IoC, your class says:

“I need an EmailService, but I don't care who creates it.”

Someone else provides it, Control has been inverted.

Instead of:

OrderService
      │
      ▼
Creates EmailService

We get:

Application
      │
Creates EmailService
      │
      ▼
Gives it to
      │
      ▼
OrderService

Now OrderService no longer control the creation of its depdency.

An Analogy

Imagine you are chef.

Without IoC

You build your own kitchen.

You buy:

  • Stove
  • Refrigerator
  • Oven
  • Knives

Every chef repeats the same work.

With IoC:

You walk into a professional kitchen.

Everything is already there. You simply cook.

The chef focuses on cooking. The kitchen management handles the equipment.

IoC works the same way.

Your class focuses on business logic. Someone else manages object creation.

IoC Without Dependency Injection

Many people think:

IoC =  Dependency Injection

This is incorrect.

Dependency Injection is one way to achieve IoC.

IoC is the broader principle.

Other mechanisms include:

  • Depdency Injection
  • Service Locator
  • Event-driven callbacks
  • Framework lifecycle management
  • Factories

Achieving IoC Using Dependency Injection

First define an abstraction.

class IEmailService
{
public:
    virtual void sendEmail() = 0;
    virtual ~IEmailService() = default;
};

Implementation:

class SMTPEmailService : public IEmailService
{
public:
    void sendEmail() override
    {
        std::cout << "SMTP Email\n";
    }
};

Now Inject it.

class OrderService
{
private:
    IEmailService* emailService;

public:
    OrderService(IEmailService* service)
        : emailService(service)
    {
    }

    void placeOrder()
    {
        emailService->sendEmail();
    }
};

Application code:

int main()
{
    SMTPEmailService smtp;

    OrderService service(&smtp);

    service.placeOrder();
}

Notice:

OrderService never creates SMTPEmailService.

Control has been inverted.

Benefits of IoC

  • Loose Couping: Classes depend on abstraction rather than concrete implementations.
  • Better testability: You can inject mocks or stubs.
  • Flexibility: Swap implementations without modifying business logic.
  • Maintainability: Changes are localized.
  • Reusability: Components can be reused in different contexts.
Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *

Your experience on this site will be improved by allowing cookies Cookie Policy