Dependency Inversion Principle (DIP)
This is part of my SOLID Software Principles in Practice post.
As I mentioned earlier in this series, DIP is closely tied to the Open/Closed Principle (OCP) . In fact, DIP enables OCP.
DIP states that:
- High level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
To clarify what this means, lets revisit a code snippet from OCP:
public final class Invaders {
private final Collection<Invader> invaders = ...;
private final SimpleInvaderMover simpleInvaderMover = new SimpleInvaderMover();
private final OpenGlInvaderRenderer openGlInvaderRenderer = new OpenGlInvaderRenderer();
...
public void mainLoop() {
for (Invader invader : invaders) {
simpleInvaderMover.move(invader);
openGlInvaderRenderer.render(invader);
}
}
}
Here we have the Invaders class depending directly on two concrete implementations - SimpleInvaderMover and OpenGlInvaderRenderer. So, this code currently breaks DIP. To fix this, we need to use something known as Inversion of Control (IOC). Again, let’s look how we addressed the code above during our OCP discussion:
public final class Invaders {
private Collection<Invader> invaders = ...;
// See DIP for how these are instantiated
private InvaderMover invaderMover = ...;
private InvaderRenderer invaderRenderer = ...;
...
public void mainLoop() {
for (Invader invader : invaders) {
invaderMover.move(invader);
invaderRenderer.render(invader);
}
}
}
Now, we have the high level Invaders class depending on abstractions of the low level classes instead, as InvaderMover and InvaderRenderer are both interfaces, as shown in the class diagram below. The class diagram also shows that our lower level classes are depending on abstractions.
Finally, we need to cover where the concrete implementations of these abstractions are actually instantiated. To do this, we use a mechanism known as Dependency Injection (DI). In the code snippet below, we use Constructor DI:
public final class Invaders {
private final InvaderMover invaderMover;
private final InvaderRenderer invaderRenderer;
public Invaders(InvaderMover invaderMover, InvaderRenderer invaderRenderer) {
this.invaderMover = invaderMover;
this.invaderRenderer = invaderRenderer;
}
...
private final Invaders invaders = new Invaders(new SimpleInvaderMover(),
new OpenGlInvaderRenderer());
}
I always prefer Constructor DI, as you also have the chance to make the injected instance immutable, as above.
Another DI mechanism is Method DI:
public final class Invaders {
public void mainLoop(InvaderMover invaderMover, InvaderRenderer invaderRenderer) {
for (Invader invader : invaders) {
invaderMover.move(invader);
invaderRenderer.render(invader);
}
}
}
...
invaders.mainLoop(new SimpleInvaderMover(), new OpenGlInvaderRenderer())
With Method DI there is no need to store either of the abstractions as class properties.
Another way is Property DI, which is my least favourite mechanism:
public final class Invaders {
private InvaderMover invaderMover;
private InvaderRenderer invaderRenderer;
public void setInvaderMover(InvaderMover invaderMover) {
this.invaderMover = invaderMover;
}
public void setInvaderRenderer(InvaderRenderer invaderRenderer) {
this.invaderRenderer = invaderRenderer;
}
}
...
invaders.setInvaderMover(new SimpleInvaderMover());
invaders.setInvaderRenderer(new OpenGlInvaderRenderer());
If you want to, You can also make use of Dependency Injection Frameworks, which do all sorts of fancy things using mechanisms like Reflection and Annotations. Personally I am a big advocate for simplicity in code, so I tend not to bother, as the concept is pretty simple.