Dependency Inversion Principle

Dated Sep 13, 2026; last modified on Wed, 16 Sep 2026

Dependency Inversion Principle

Your code should depend on abstractions, not concrete implementations. Instead of:

public class EmailSender
{
  public void Send(string message) { ... }
}

public class NotificationService
{
  private readonly EmailSender _emailSender = new();

  public void Notify(string message) => _emailSender.Send(message);
}

… have:

public interface IMessageSender
{
  void Send(string message);
}

public class EmailMessageSender : IMessageSender
{
  public void Send(string message) { ... }
}

public class NotificationService(IMessageSender messageSender)
{
  public void Notify(string message) => messageSender.Send(message);
}

… so that swapping out an EmailMessageSender for an SmsMessageSender is easy. Even more commonly, unit testing NotificationService with a MockMessageSender that doesn’t send real messages.

Notes from Chromium

Phrased differently, inversion of control allows users of a framework/library (clients) to customize the behavior of the framework. The control flow is inverted because the library (e.g., NotificationService) calls back into the client’s code (e.g., the specific IMessageSender implementation) so that control flows back from a low-level class to a high-level class. .

Dependency injection (via the constructor) is one technique of achieving dependency inversion .

The low-level class can execute a callback, e.g.,

class StringKVStore {
 public:
  // ...
  using KeyPredicate = base::RepeatingCallback<bool(const string&)>;
};

void StringKVStore::GetKeysMatching(const KeyPredicate& predicate) {
  set<string> keys;
  for (const auto& key : internal_keys()) {
    if (predicate.Run(key)) keys.insert(key);
  }
  return keys;
}

… provided by the higher-level class, e.g.,

bool IsKeyInteresting(const string& key) { ... }

void YourFunction() {
  StringKVStore::KeyPredicate = base::BindRepeating(&IsKeyInteresting);
  for (const auto& key : kv_store_.GetKeysMatching(predicate)) { ... }
}

While callbacks are heavyweights in performance, memory usage, and nested setup code, they have several advantages: no separate interface needed; client methods don’t need specific names; client methods can be bound with any needed state; avoid multiple inheritance; can be passed around by themselves.

Observers are useful when more than one client cares to passively listen to events happening without modifying the state of the framework object being observed, e.g.,

class StringKVStore::Observer {
 public:
  virtual void OnKeyChanged(StringKVStore* store,
                            const string& key,
                            const string& from_value,
                            const string& to_value) {}
};

class StringKVStore {
 public:
  // ...
  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);
  // ...

  void Put(const string& key, const string& value) {
    optional<string> maybe_old_value = Get(key);
    // ...
    if (maybe_old_value.has_value()) {
      for (const auto& observer : observers_)
        observer.OnKeyChanged(this, key, maybe_old_value.value(), value);
    }
  }
};

class HelloKeyWatcher : public StringKVStore::Observer {
 public:
  void OnKeyChanged(StringKVStore* store,
                    const string& key,
                    const string& from_value,
                    const string& to_value) override {
    if (key == "hello") ++hello_changes_;
  }
};

Delegates are responsible for implementing part of the framework that is deliberately missing, e.g.,

class StringKVStore::Delegate {
 public:
  // Use case: Provide logic that happens synchronously with what's happening in
  // the framework.
  virtual bool ShouldPersistKey(StringKVStore* store, const string& key);

  // Allow clients to inject their own subclasses of framework objects that need
  // to be constructed by the framework.
  virtual unique_ptr<StringKVStoreBackend> CreateBackend(StringKVStore* store);
};

References

  1. Design Principles | Hello Interview Low Level Design. www.hellointerview.com . Accessed Sep 13, 2026.
  2. Chromium Docs - docs/patterns/inversion-of-control.md. chromium.googlesource.com . Accessed Sep 13, 2026.