DIP

Why should high-level logic depend on low-level details? Master Dependency Inversion to build truly decoupled and testable systems.

May 1, 20264 min read5 / 6

The Dependency Inversion Principle (DIP) is the "D" in SOLID and the secret to building world-class software. It states that high-level modules should not depend on low-level modules. Both should depend on Abstractions.

The Essentials

The "Decoupling Magic" guide:

  1. Abstractions First: Your core business logic shouldn't know about specific databases, APIs, or files.
  2. Inversion of Control: Instead of a class creating its own dependencies, they are "given" to the class (usually through a constructor).
  3. Pluggability: DIP makes your system "pluggable." You can swap a SQL database for a NoSQL one without touching your core logic.
  4. Testability: By depending on interfaces, you can easily "mock" external systems during unit testing.

The Power Plug Analogy

Think about the power outlet in your wall.

  1. The High-Level Module: Your Laptop.
  2. The Low-Level Module: The specific power plant (Coal, Nuclear, Solar).
  3. The Abstraction: The Socket.

Your laptop doesn't care which power plant is generating the electricity. It only cares that the plug fits the socket. If the city switches from Coal to Solar, your laptop keeps working without any changes.

DIP in Action: The Logger Problem

Tight Coupling

The High-Level Store depends directly on a specific FileLogger. It is "stuck" with it.

class Store {
  logger = new FileLogger();
  purchase() {
    this.logger.log("Bought");
  }
}

If you want to log to Cloud instead of File, you must modify the Store class. This violates OCP and DIP.

Dependency Inversion

Store depends on a Logger interface. The specific implementation is "injected" at runtime.

interface Logger { log(m); }

class Store {
  constructor(logger: Logger) {}
  purchase() {
    this.logger.log("Bought");
  }
}

You can swap File, Cloud, or Mock loggers without changing a single line of Store logic.

Code Implementation: Making it Pluggable

While the grid shows the concept, here is how you implement it across languages:

// The Abstraction (The Socket) interface Logger { log(message: string): void; } // Low-Level Modules (The Power Plants) class FileLogger implements Logger { log(msg: string) { console.log("Writing to File: " + msg); } } class CloudLogger implements Logger { log(msg: string) { console.log("Sending to Cloud: " + msg); } } // High-Level Module (The Laptop) class Store { constructor(private logger: Logger) {} purchase() { this.logger.log("Item purchased successfully!"); } } // Usage const store = new Store(new CloudLogger()); // Plug in Cloud store.purchase();

Why It Matters: Evolution

  1. Change is Easy: Today you use MySQL. Tomorrow your manager says: "Switch to MongoDB." If you followed DIP, this change takes minutes.
  2. Testing is Fast: You don't want your unit tests to actually write to a real database. With DIP, you just pass a MockLogger that does nothing, making your tests lightning fast.

By mastering Dependency Inversion, you move from writing "Rigid" code to writing "Fluid" code that can survive and thrive in a world of constant technological change.

You've completed the five pillars of SOLID! Now it's time for the ultimate test: the SOLID Boss Battle.


Practice what you just read.

DIP: Cloud Storage Decoupling
1 exercise