Aaron Proctor

The incoherent braindump of a software developer

Dependency Inversion Principle

This is part 5 of a series of blog posts on the SOLID principles – five fundamental principles of object-oriented software design.

The Dependency Inversion Principle states that:

A. High level modules should not depend upon low level modules. Both should depend upon abstractions.

B. Abstractions should not depend upon details. Details should depend upon abstractions.

The two parts to the dependency inversion principle might seem a little obtuse but understanding this principle is the key to writing decoupled code.

Dependency inversion is not just about working with abstractions rather than implementations; this should be a given already. The principle specifically concerns the ownership of these abstractions.

In a traditional, procedurally-written application, high level code tends to depend on low level implementations. For example, suppose we add code to render our game board to the screen. The physical rendering is encapsulated in the Viewport class, and a method added to our Gameboard object:

Clearly this violates SRP at the very least, so as we’ve already seen this should be factored out into a separate class:

We have solved some issues, but we still depend on the Viewport class – and create this dependency ourselves. As we’ve seen, this sort of dependency hierarchy can lead to brittle code.

We should already be comfortable enough to work with interfaces to come up with a reasonable abstraction.

In isolation that’s fine; indeed what we’ve done here is dependency injection, which is a result of (but not the same as) dependency inversion. However, what happens when the rendering process needs to change? A developer working on that might consider that the IViewport interface needs to change, since the change is relating to the viewport.

The problem with changing the interface, of course, is that everything which depends on it needs to change. This means we need to change the important, high-level code (in this case Renderer) just because a lower-level detailed implementation (the Viewport) needs to change.

This concept is the key to understanding the Dependency Inversion Principle – the IViewport abstraction is not owned by the viewport, it’s owned by the renderer. It is the rendering process that should determine how it needs to talk to the viewport, rather than the viewport dictating to the renderer how it needs to be called.

In our case the abstraction – the underlying truth of the process which does not change – is that when the game wishes to render the gameboard in the current frame, it needs to determine what needs to be rendered, and give that information to the viewport. This is a high-level policy, rather than an implementation detail, and the driver of that high level policy (the Renderer class) should not change because some detail changes (the Viewport class).

Post a comment