We’ve been paying closer attention lately to how we use design patterns in our Ruby on Rails work.
Decorators have emerged as one pattern that’s helped us keep code ready for change. We’ve chosen our spots carefully, but it’s proven useful quite often.
For me, it’s been a new technique that’s required asking a lot of questions to evaluate alternative decorator implementations in Ruby and understand terms.
Some questions I’ve had:
- What’s the difference between a decorator and a strategy?
- What’s the difference between a decorator and a composite?
- What’s the difference between a decorator and a presenter?
Intent
A decorator’s intent, as described in Design Patterns by the Gang of Four, is:
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
Invoking a classic decorator in Ruby looks like this:
coffee = Coffee.new
Sugar.new(Milk.new(coffee)).cost
A popular alternative style is:
coffee = Coffee.new
coffee.extend Milk
coffee.extend Sugar
coffee.cost
There’s a family of patterns that are similar to decorators.
When would we use them instead of a decorator?
Strategy
The Gang of Four differentiates these succinctly:
- A decorator changes an object’s skin.
- A strategy changes an object’s guts.
Said another way, a decorator is likely to add functionality (decorate) an object and a strategy is likely to swap functionality.
Example of a strategy in Ruby using the same Coffee analogy:
class Coffee
def initialize(brewing_strategy = DripBrewingStrategy.new)
@brewing_strategy = brewing_strategy
end
def brew
@brewing_strategy.brew
end
end
Coffee.new SteepBrewingStrategy.new
The form is similar to a PORO decorator, which is why the patterns feel related.
The difference is that the object passed to the constructor is not being “wrapped” like a decorator. We’re “swapping” functionality.
The decorator is additive and the strategy is a replacement.
With words like “guts”, “swapping”, and “replacement”, it’s no surprise that strategies are often used with Dependency “Injection”. In the example above, constructor injection lets us easily swap the brewing strategy.
Composite
Gang of Four:
- A decorator can be viewed as a composite with only one component.
- A decorator isn’t intended for object aggregation.
Example of a composite in Ruby from the ActivePresenter library:
class SignupPresenter < ActivePresenter::Base
presents :user, :account
end
A decorator decorates a single component and a composite composes multiple
components (user
and account
in the example above).
Both patterns are named well.
Presenters
Now, the most confusing one:
- A decorator is a class that adds functionality to another class.
- A presenter is a class that adds presentation functionality to another class.
- A presenter is sometimes a decorator.
- A presenter is sometimes a composite (as in the ActivePresenter example above).
This is my own definition. I don’t have an authority to refer you to since you won’t find “Presenter” in Gang of Four. As best I can tell, the Rails community began using the term “Presenter” with this 2007 article by Jay Fields.
The easiest mnemonic is just to defer to their names. What makes a presenter a presenter is its presentation-ness.
One example:
class HumanizedStat
def initialize(component)
@component = component
end
def to_s
# giant case statement that used to be in a model
end
# a bunch of private helper methods supporting #to_s
# which used to be in app/helpers
end
In this case, the presenter looks a lot like a decorator (but doesn’t meet the Gang of Four definition): it’s adding functionality to a single component.
However, because the functionality is completely presentation-related, I’d
expect this to be considered a presenter and maybe saved in a app/presenters
directory.