Photo by Aida L from Unsplash

SOLID: Dependency Inversion Principle

Paul Allies

--

All programs are composed of functions and data structures. The SOLID principles introduced by Robert C. Martin, help us group our code into modules and advise how to interconnect them. The whole point of application architecture is to manage change. Software requirements change all the time and we as developers need to make sure these changes don’t break existing code.

There are 5 principles that make up the acronym are:

Each of these principles can stand on its own. I’ve covered the Single Responsibility Principle and in this second post on SOLID Principles, I will attempt to explain the DIP (Dependency Inversion Principle).

The DIP tells us that the most flexible systems are those in which source code dependencies refer only to abstractions, not implementations.

Put another way, high-level modules should not depend on low-level modules but should depend on their abstractions.

Ok, let’s start by identifying the problem we’re trying to solve?. Let’s create a low-level user repository module/class:

and a high-level class, that uses/depends on the user repository:

and to use the high-level class we do the following:

The problem lies within the GetAllUsers class. We have a direct or hard dependency between the “GetAllUsers” and “UserRepository” Class as indicated by the import statement and the creation of a new UserRepository instance.

If we were importing a system, framework library or any static dependency, then it should be ok, however, for something like an actively developing module as a user repository it would be advisable not to have a hard dependency as we do here within the high-level module

Why is this a problem?

Well, if we tightly couple the high-level component(“GetAllUsers”) to low-level dependencies (user repository) then changes are risky. The strength of the high-level module is directly dependent on the strength of the low-level module.

If we however depend on the interface/abstraction of the dependency then we reduce the risk to high-level components.

Let’s change the code to illustrate. We split the user repository into an interface and implementation

Let’s also change the higher-level module

Now we have the high-level class not directly dependent on the lower-level module but rather its interface or abstraction. The high level also does not “new up” a user repository implementation

We put them all together as follows:

Now, the creation of dependencies has been inverted. Classes of the lower level are created first and then injected through the constructor into the high-level class.

This is great for swapping out dependencies especially when unit testing, as we can test higher-level modules in isolation by providing them with mock dependencies.

This article was originally published on Nanosoft

--

--