Dependency Inversion Principle states that entities must depend on abstractions, not on concretions. It asserts that the high-level module must not depend on the low-level module; but they should depend on abstractions. It is one of the fundamental pillars of SOLID principles.
But why the term inversion?
traditionally, software structures had higher level modules depending on low level modules. E.g. it made sense to have a higher level module ButtonAtKitchen
class handling inputs for a KitchenLamp1
(lower level) and KitchenLamp2
(lower level) class. Unfortunately that made software a lot more specific than it needed to be. So when we make the software more general by adding “contracts”, kitchen lamps are now dependent on an abstract Button
class.
In other words the details are now dependent on abstractions instead of the other way around.
It’s direction is inverted now.
Dependency Inversion Principle in action
Here is an example of a PasswordReminder
that connects to a MySQL database:
class MySQLConnection
{
public function connect()
{
// handle the database connection
return 'Database connection';
}
}
class PasswordReminder
{
private $dbConnection;
public function __construct(MySQLConnection $dbConnection)
{
$this->dbConnection = $dbConnection;
}
}
The MySQLConnection
is the low-level module while the PasswordReminder
is high level. But according to the definition of dependency inversion principle, this snippet above violates this principle as the PasswordReminder
class is being forced to depend on the MySQLConnection
class. For some reason if we were to change the database engine at a later time, we would also have to edit the PasswordReminder
class, and this would violate the open-close principle.
The PasswordReminder
class should not care what database our application uses. To address these issues, we can code to an interface since high-level and low-level modules should depend on abstraction.
interface DBConnectionInterface
{
public function connect();
}
class MySQLConnection implements DBConnectionInterface
{
public function connect()
{
// handle the database connection
return 'Database connection';
}
}
class PasswordReminder
{
private $dbConnection;
public function __construct(DBConnectionInterface $dbConnection)
{
$this->dbConnection = $dbConnection;
}
}
The interface has a connect method and the MySQLConnection
class implements this interface. Also, instead of directly type-hinting MySQLConnection
class in the constructor of the PasswordReminder
, we instead type-hint the DBConnectionInterface
and no matter the type of database our application uses, the PasswordReminder
class can connect to the database without any problems. We don’t need to update or edit it; and open-close principle is not violated too.
This code establishes that both the high-level and low-level modules depend on abstraction.
As both OCP and DIP both are discussing abstractions; OCP is similar to DIP, right?
No, not really.
Although they both are discussing abstractions they’re conceptually different.
Both principles are looking at different contexts, OCP on one particular module and DIP on several modules.
For example; in the previous example; MySQLConnection
are not extensible (nor currently has any requirement explaining they need to be).
The design is breaking OCP but follows DIP.
But what does the statement – “both the high level components and the low level components, rely on the abstractions and are dependent on them” really means?
It means both should depend on the same abstraction in some way.
The high level components are dependent on the abstraction – Meaning the high level components talk to an interface to communicate with the low level components, instead of communicating with concrete low level components directly.
The low level components implement this interface. The low level components are dependent on the abstraction – Meaning the low level components are defined and designed in the terms of the interface and are designed to fit the interface. They are dependent on the interface, in the way that the interface defines how they are designed.
This way, both the high level components and the low level ones are ‘dependent on the abstraction’, but in different ways.
The last piece of the puzzle is that the interface is defined by the high-level component, not the low-level one. In other words, the high-level concern (e.g. the domain model) gets to choose the most convenient way to talk to the low-level code (like data-access code), and it’s the job of the low-level code to conform to that interface. When we understand that the interface is part of the high-level behavior, the term ‘dependency inversion’ makes much more sense.
Hollywood principle?
A common way of writing OO code is to to have code such as:
void SomeMethod()
{
SomeClass x = new SomeClass(params...);
...
}
or code such as:
void SomeMethod()
{
SomeClass x = SomeStaticLocator.GetSomeClass();
...
}
In both cases, the code is obtaining other parts of the system by “asking” for them.
With dependency injection/dependency inversion, the opposite (inverse) approach is taken:
void SomeMethod(SomeClass x)
{
...
}
Rather than the method asking for its dependencies, it is told what they are. This helps to reduce coupling within the system and makes testing a lot simpler. Further improvements are to then design to interfaces, rather than concrete types to further decouple parts of the system:
void SomeMethod(ISomeClass x)
{
...
}
Because the method/class is being supplied its dependencies, rather than it needing to request them itself, the terms “tell; don’t ask” and “don’t call us, we’ll call you” are often used to sum up the behaviour of DI.
IoC is the general concept where control of flow is Inverted from client code to framework, which “Does something for the client”. SL (Service Locator) and DI (Dependency Injection) are two design patterns stem off from IoC.
- In case of DI (Dependency Injection), the application framework returns to client pre-configured dependencies. Dependencies are configured by way of Constructor/Setter/Method “Injection” – Injection is just a big word for simply “Setting member variables” via Constructor/Setter/Method.
- In case of SL (Service Locator), the application framework, containing all services instances, returns relevant Service instance by looking up a map, with a key specified by client. Service Locator which is basically a map.
Such Confusion, Much Wow!
IoC is a generic term meaning that rather than having the application call the implementations provided by a library (also know as toolkit), a framework calls the implementations provided by the application.
DI (dependency injection) is a form of IoC, where implementations are passed into an object through constructors/setters/service lookups, which the object will ‘depend’ on in order to behave correctly.
IoC without using DI, for example would be the Template pattern because the implementation can only be changed through sub-classing.
DI frameworks are designed to make use of DI and can define interfaces (or Annotations in Java) to make it easy to pass in the implementations.
IoC containers are DI frameworks that can work outside of the programming language. In some we can configure which implementations to use in metadata files (e.g. XML) which are less invasive.
Dependency inversion principle is a fancy name for “coding to abstraction, rather than concrete implementation”.
In procedural programming, the Hollywood principle is not applicable. The flow of the application goes (mostly) from the top to bottom with possible loops, containing switches and conditionals, which decide what is the next action.
This means, the user has no control over the procedure execution, because it is predetermined by the software programmer.
DIP means, we do not know what exactly is going to be executed until the caller, usually the user of our application, wants to use (for example) some service.
An Example; may be?
interface FancyStuff
{
int GiveMeNumber();
bool ProcessNumber(int number);
}
We have an interface which institutes a contract that by calling the GiveMeNumber()
method we will have an int
to work with and also allowing us to use the ProcessNumber()
method returning boolean
and taking in an int
as a parameter.
Somewhere in our app, perhaps the service layer, we can have a class depending on this interface.
class FancyStuffService
{
private FancyStuff fancyStuff;
public FancyStuffService(FancyStuff fancyStuff)
{
this.fancyStuff = fancyStuff;
}
public bool FancyOperation()
{
var number = this.fancyStuff.GiveMeNumber();
++number;
return this.fancyStuff.ProcessNumber(number);
}
}
Notice, how we are using some methods, but we do not have any implementation. The class FancyStuffService
has no idea, what the implementations for the methods GiveMeNumber()
and ProcessNumber()
look like, but it knows, which return values it may expect and what to pass to them.
Naturally, without an implementation, the interface is pretty useless, so we implement two versions of the FancyStuff
interface to provide the logic.
class FancyStuffImplOne : FancyStuff
{
int GiveMeNumber()
{
return 5;
}
bool ProcessNumber(int number)
{
return number != 0;
}
}
class FancyStuffImplTwo : FancyStuff
{
int GiveMeNumber()
{
return 42;
}
bool ProcessNumber(int number)
{
return number < 15;
}
}
We have an implementation of our FancyStuff
interface, we have a FancyStuffService
which uses the interface, and we have to wire it all together.
But how?
In our factory pile, of course. That is where all the new operators in our application are, meaning it is a place where the objects are constructed.
class FancyStuffServiceFactory
{
private FancyStuff ObtainFancyStuffImplementation(int id)
{
switch (id)
{
case 1:
return new FancyStuffImplOne();
case 2:
return new FancyStuffImplTwo();
default:
throw new RuntimException("Undefined fancy stuff ID.");
}
}
public FancyStuffService BuildFancyStuffService(int fancyStuffId)
{
var fancyStuff = this.ObtainFancyStuffImplementation(fancyStuffId);
return new FancyStuffService(fancyStuff);
}
}
And the variable fancyStuffId
is exactly what we want. The integer could be populated during the runtime with an input from the user, perhaps via a selectbox, or any other method.
Only after a user inserts or selects some number, the factory is called, passing the user’s input into the factory, which in return creates a FancyStuffService
for the user to use. Once that is done, there can be an event mapped to call the FancyOperation()
method of the FancyStuffService
class.
Also notice, how we do not know which implementation of the FancyStuff
will be chosen until we really need it. That is the Hollywood principle at work.
Note, that DIP is directly tied to dependency injection and cannot work without it. Which is good, considering dependency injection prevents global states and static methods and leads to code which is testable.