Dependency Inversion: depend on a contract

Keep business policy pointed at an abstraction; let replaceable infrastructure implement that abstraction.

Time O(1) dispatchSpace O(1)Code C++, Python and C#
Watch it animatedStep through it, with the running line highlighted. Free with an account.

The idea

OrderService is high-level policy; EmailNotifier is an infrastructure detail. Both meet at INotifier, so the policy does not need to construct or know the concrete email implementation. Constructor injection supplies the chosen implementation.

Code

class Notifier {
public:
    virtual void send(const string& message) = 0;
    virtual ~Notifier() = default;
};
class EmailNotifier : public Notifier {
public:
    void send(const string& message) override {
        cout << "Email: " << message << "\n";
    }
};
class OrderService {
    Notifier& notifier;
public:
    OrderService(Notifier& notifier)
        : notifier(notifier) {}
    void ship() {
        notifier.send("Order shipped");
    }
};

int main() {
    EmailNotifier email;
    OrderService service(email);
    service.ship();
}
import asyncio
from typing import Protocol

class Notifier(Protocol):
    async def send(self, message: str) -> None: ...

class EmailNotifier:
    async def send(self, message: str) -> None:
        print(f"Email: {message}")

class OrderService:
    def __init__(self, notifier: Notifier):
        self.notifier = notifier

    async def ship(self) -> None:
        await self.notifier.send("Order shipped")

service = OrderService(EmailNotifier())
asyncio.run(service.ship())
interface INotifier {
    Task SendAsync(string message);
}
class EmailNotifier : INotifier {
    public Task SendAsync(string message) {
        Console.WriteLine($"Email: {message}");
        return Task.CompletedTask;
    }
}
class OrderService {
    private readonly INotifier notifier;
    public OrderService(INotifier notifier) {
        this.notifier = notifier;
    }
    public Task ShipAsync() =>
        notifier.SendAsync("Order shipped");
}

var service = new OrderService(new EmailNotifier());
await service.ShipAsync();

Tips

In the .NET library

At runtime OrderService still calls the notifier. The inversion is in the source dependency: the concrete notifier implements the contract that the high-level service consumes.