High level modules should not depend upon low level modules. Both should depend upon abstractions.
Dependency inversion talks about the coupling between the different classes or modules. It focuses on the approach where the higher classes are not dependent on the lower classes instead depend upon the abstraction of the lower classes. The main motto of the dependency inversion is Any higher classes should always depend upon the abstraction of the class rather than the detail.
Let’s understand the dependency inversion principle with an example. Say you are a manager having some persons as an employee of which some are developers and some are UI designers and rest are testers(QA).

Now, you have exposed everything about the lower layer to the upper layer, thus abstraction is not mentioned. That means Manager must already know about the type of the workers that he can supervise.
Now if another type of worker comes under the manager lets say, DBA (Database Administrator), then the whole class needs to be rejigged. This is where dependency inversion principle pitch in.
Lets take another real world example:
Let’s think if we don’t have plugs and sockets. The computer is directly soldered into an electrical wire in the wall. Whenever you buy a motherboard you also get a mouse, keyboard and monitor, but they’re all directly soldered onto the motherboard by the manufacturer. Everything works fine, but things get complicated when you want to remove or replace anything. If you try to replace the mouse:
- You risk damaging the motherboard
- It takes forever to solder
- Soldering is error prone because the new mouse has different wires
The Dependency Inversion Principle is basically a way of adding plugs and sockets to your code. It allows you to create your high level modules (the computer) independent of the low level modules (the mouse). The low level modules can be created later, and are easily replaceable.
Plugs :
In C++, A plug can be base classes with virtual functions; templating; or may be preprocessor/mecros.
The plug is essentially an abstract interface. Any class can implement or inherit the abstract interface.
//This is the "plug" (abstract base class)
class Exporter {
public:
//pure virtual (not implemented)
virtual String convertDocumentToString(Document* doc) = 0;
};
//This is a concrete class that implements the "plug"
class CSVExporter : public Exporter {
public:
//concrete implementation
String convertDocumentToString(Document* doc);
};
//Another concrete class that implements the "plug"
class XMLExporter : public Exporter {
public:
//concrete implementation
String convertDocumentToString(Document* doc);
};
It is critical that the all implementations/subclasses adhere to the Liskov Substitution Principle. This is because the implementations/subclasses will be used via the abstract interface, not the concrete class interface.
Sockets:
The “sockets” are any functions or classes that use a “plug”.
//Class with an Exporter "socket"
class ExportController {
private:
Exporter* m_exporter;
public:
//this is the socket that accepts Exporter plugs
void setExporter(Exporter* exporter);
void runExport();
};
// ... (code omitted)
void ExportController::runExport()
{
Document* currentDocument = GetCurrentDocument();
String exportedString = m_exporter->convertDocumentToString(currentDocument);
String exportFilePath = GetSaveFilePath();
WriteStringToFile(exporterString, exportFilePath);
}
The above example is like as Open Closed Principle (OCP). Both make use of dependency injection (DI), but for different reasons. The OCP uses DI to “close” a class to modification, whereas the DIP uses DI to remove the dependencies on lower level classes.