Inversion of control is a technique for keeping software flexible. It
combines best with small classes with single
responsibilities. Inverting control means assigning dependencies at
run-time, rather than statically referencing dependencies at each
This can be hard to understand as an abstract concept, but it’s
fairly simple in practice. Let’s jump into an example:
The summaries_using method builds a summary of the
answers to each of the survey’s questions.
However, we also want to hide the answers to questions that the user
has not personally answered, so we decorate the
summarizer with an UnansweredQuestionHider.
Note that we’re statically referencing the concrete, lower-level detail
UnansweredQuestionHider from Survey rather
than depending on an abstraction.
In the current implementation, the
Survey#summaries_using method will need to change whenever
something changes about the summaries. For example, hiding the
unanswered questions requires
changes to this method.
Also, note that the conditional logic is spread across several
layers. SummariesController decides whether or not to hide
unanswered questions. That knowledge is passed into
also passes the current user down into
Survey#summaries_using, and from there it’s passed into
Now the Survey#summaries_using method is completely
ignorant of answer hiding; it simply accepts a summarizer
and the client (SummariesController) injects a decorated
dependency. This means that adding similar changes won’t require
changing the Summary class at all.
This also allows us to simplify UnansweredQuestionHider
by removing a condition:
While following the previous example, you probably noticed that we
didn’t eliminate the UnansweredQuestionHider dependency; we
just moved it around. This means that, while adding new summarizers or
decorators won’t affect Summary, they will affect
SummariesController in the current implementation. So, did
we actually make anything better?
In this case, the code was improved because the information that
affects the dependency decision—params[:unanswered]—is now
closer to where we make the decision. Before, we needed to pass a
Boolean down into summaries_using, causing that decision to
leak across layers.
If you push your dependency decisions up until they reach the layer
that contains the information needed to make those decisions, you will
prevent changes from affecting several layers.
This makes it difficult to know exactly what’s going to happen. You
can mitigate this issue by using naming conventions and well-named
classes. However, each abstraction introduces more vocabulary into the
application, making it more difficult for new developers to learn the
You may need to eliminate these smells in order to properly invert
Excessive use of callbacks will
make it harder to follow the DIP, because it’s harder to inject
dependencies into a callback.
Using mixins and STI
for reuse will make following the DIP more difficult, because
inheritance is always decided statically. Because a class can’t decide
its parent class at run-time, inheritance can’t follow inversion of
You can use these solutions to refactor towards DIP-compliance: