GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

Written by thoughtbot

Decorators Compared To Strategies, Composites, and Presenters

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?

Tie-dyed t-shirt

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.