Injecting dependencies allows you to keep dependency resolutions
close to the logic that affects them. It can prevent sub-dependencies
from leaking throughout the code base, and it simplifies changing the
behavior of related components without modifying
those components’ classes.
Although many people think of dependency injection frameworks and XML
when they hear “dependency injection,” injecting a dependency is usually
as simple as passing it as a parameter.
Changing code to use dependency injection only takes a few steps:
Move the dependency decision to a higher level component.
Pass the dependency as a parameter to the lower level
Remove any sub-dependencies from the lower level component.
In our example applications, users can view a summary of the answers
to each question on a survey. Users can select from one of several
different summary types to view. For example, they can see the most
recent answer to each question, or they can see a percentage breakdown
of the answers to a multiple choice question.
The controller passes in the name of the summarizer that the user
This is hard to follow and causes shotgun surgery because
the logic of building the summarizer is in Question, far
away from the choice of which summarizer to use, which is in
SummariesController. Additionally, the options
parameter needs to be passed down several levels so that
summarizer-specific options can be provided when building the
Let’s remedy this by having the controller build the actual
summarizer instance. First, we’ll move that logic from
Question to SummariesController:
This interaction has already improved, because the
options argument is no longer uselessly passed around
through two models. It’s only used in the controller where the
summarizer instance is built. Building the summarizer in the controller
is appropriate, because the controller knows the name of the summarizer
we want to build, as well as which options are used when building
Now that we’re using dependency injection, we can take this even
By default, in order to prevent the summary from influencing a user’s
own answers, users don’t see summaries for questions they haven’t
answered yet. Users can click a link to override this decision and view
the summary for every question.
The information that determines whether or not to hide unanswered
questions lives in the controller:
Injecting dependencies in our example made each
Question and UnansweredQuestionHider—easier to
understand as a unit. However, it’s now difficult to understand what
kind of summaries will be produced just by looking at
Survey or Question. You need to follow the
stack up to SummariesController to understand the
dependencies and then look at each class to understand how they’re
In this case, we believe that using dependency injection resulted in
an overall win for readability and flexibility. However, it’s important
to remember that the further you move a dependency’s resolution from its
use, the harder it is to figure out what’s actually being used in lower
In our example, there isn’t an easy way to know which class will be
instantiated for the summarizer parameter to
# app/models/question.rbdef summary_using(summarizer) value = summarizer.summarize(self)Summary.new(title, value)end
In our case, that will be one of Summarizer::Breakdown,
Summarizer::UserAnswer, or a
UnansweredQuestionHider that decorates one of the above.
Developers will need to trace back up through Survey to
SummariesController to gather all the possible
When pulling dependency resolution up into a higher level class,
check that class to make sure it doesn’t become a large class because of all the
logic surrounding dependency resolution.
If a class is suffering from divergent change
because of new or modified dependencies, try moving dependency
resolution further up the stack to a container class whose sole
responsibility is managing dependencies.