Decorators can be used to place new concerns on top of existing
objects without modifying existing classes. They combine best with small
classes containing few methods, and make the most sense when modifying
the behavior of existing methods, rather than adding new methods.
The steps for extracting a decorator vary depending on the initial
state, but they often include the following:
Extract a new decorator class, starting with the alternative
Compose the decorator in the original class.
Move state specific to the alternate behavior into the
Invert control, applying the decorator to the original class from
its container, rather than composing the decorator from the original
In our example application, users can view a summary of the answers
to each question on a survey. 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. This
concern is mixed across several levels, and introducing the change
affects several classes. Let’s see if we can refactor our application to
make similar changes easier in the future.
Currently, the controller determines whether or not unanswered
questions should display summaries:
Now that we have a class to handle this logic, we can move some of
the parameters into instance state. In
Survey#summaries_using, we use the same summarizer and user
instance; only the question varies as we iterate through questions to
summarize. Let’s move everything but the question into instance
variables on the decorator:
In the end, the component we want to wrap with our decorator is the
summarizer, so we want the decorator to obey the same interface as its
component—the summarizer. Let’s rename our only public method so that it
follows the summarizer interface:
decorator now follows the component interface in name—but not
behavior. In our application, summarizers return a string that
represents the answers to a question, but our decorator is returning a
Summary instead. Let’s fix our decorator to follow the
component interface by returning just a string:
Decorators must keep up to date with their component interface. Our
decorator follows the summarizer interface. Every decorator we add for
this interface is one more class that will need to change any time we
change the interface.
We removed a concern from Survey by hiding it behind a
decorator, but this may make it harder for a developer to understand how
a Survey might return the hidden response text, since that
text doesn’t appear anywhere in that class.
The component we decorated had the smallest possible interface: one
public method. Classes with more public methods are more difficult to
Decorators can modify methods in the component interface easily, but
adding new methods won’t work with multiple decorators without
meta-programming like method_missing. These constructs are
harder to follow and should be used with care.
It’s unlikely that your automated test suite has enough coverage to
check every component implementation with every decorator. Run through
the application in a browser after introducing new decorators. Test and
fix any issues you run into.
Make sure that inverting control didn’t push anything over the line
into a large class.
The canonical reference for writing fantastic Rails applications from authors who have created hundreds.