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?
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?
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.
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
account in the example above).
Both patterns are named well.
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.
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