Using the Decorator Pattern to simplify your Rails models

by burke on November 26, 2011 in Patterns with 7 Comments Tweet

The Decorator Pattern is a design pattern for augmenting an existing object with one or more additional methods. It’s an extremely useful tool for refactoring methods out of a complex class, and I personally feel that it’s grossly under-utilized by rails programmers.

Just about every rails application has to combine a handful of fields on a model in some human-friendly way. For example, say we want to represent a customer by their salutation, first name, last name, and email address everywhere they’re displayed in our system. Within the confines of the patterns rails provides you by default, your options are to (1) smash it all together in the view every time you display it; (2) create a method on the model; or (3) create a utility method in a helper.

All of these options are generally poor design choices, for various reasons:

  1. Leads to duplicate code. If the customer is displayed in more than one place, you’ve defined the formatting of their display name in multiple places.
  2. Quickly leads to very large models. Additionally, it defines something view-related just one level up from the database, which is a violation of all sorts of design principles.
  3. Helpers inevitably become grab bags of unrelated methods with no cohesion. It’s the best option of the three, but it still leads to pain eventually.

In this example, the combination of these four fields on the Customer class is very tightly bound to the Customer object itself. It does seem to make sense as a method on the Customer, but a database model is not a very good place to put for an arrangement of fields for a particular view.

Enter the Decorator.

A decorator wraps an existing object and augments it with one or more additional methods. In this case, the decorator would wrap a Customer, and provide a display_name method.

The implementation might look something like this:

class Decorator
  def initialize(decorated)
    @decorated = decorated
  end

  def method_missing(s, *a)
    @decorated.send(s, *a)
  end
end

class CustomerDecorator < Decorator
  def display_name
    "#{salutation} #{first_name} #{last_name} (#{email})"
  end
end

And then this, somewhere in or near a view:

@decorated_customer = CustomerDecorator.new(@customer)
# ...
@decorated_customer.salutation
@decorated_customer.display_name

The benefit of the decorator pattern is threefold:

  • It provides an opportunity to move view-related methods off of the model
  • It lets you put your models — generally the burliest of all rails objects — on a much-needed diet
  • It is much easier to test than the same method directly on the model. I’ll blog on this in the near future.

Incidentally, the implementation of the decorator pattern in this post is a naive and simple one. If it seems useful to you, be sure to check out Draper, a much more complete application of the pattern, to see if it suits your needs better. If you do use draper, be sure to check out Ryan Bates’ excellent screencast on it at railscasts.com.

  • Share this:
  • Share
  • Tagged: decorator, patterns, refactoring

    7 Comments on "Using the Decorator Pattern to simplify your Rails models"

    1. spacer Michael McClenaghan says:
      November 26, 2011 at 7:59 pm

      Good article – decorators and/or presenters seem to be all the rage in the Rails world right now. One that I’ve liked playing with is delegate_presenter. It’s a bit like your example, except that it inherits from SimpleDelegator rather than implementing a method_missing solution. That allows it to simply pass calls through to the model if needed.

      Reply
      • spacer burke says:
        November 27, 2011 at 8:14 am

        Funny story about SimpleDelegator: corelib.rubyonrails.org/classes/Delegator.html#M001385

        DelegatePresenter looks like a nice simple, clean solution. I like it. Thanks for pointing it out!

        Reply
    2. spacer Chris Nicola says:
      November 27, 2011 at 1:45 am

      Any reasons helpers are not appropriate for this type of thing?

      Reply
      • spacer burke says:
        November 27, 2011 at 8:00 am

        There’s nothing inappropriate about it, but when you get to the point that you have a small handful of helper methods that only deal with one class of object and have minimal interaction with ActionView helpers, I feel a decorator or presenter is a slightly cleaner abstraction than a helper.

        Reply
    3. spacer Brian Ewing says:
      November 27, 2011 at 7:43 am

      Why not use the Forwardable class? It lets you pass on methods like this in a much nicer fashion.
      railsmagazine.com/articles/4

      Reply
      • spacer burke says:
        November 27, 2011 at 8:09 am

        I don’t think Forwardable has a way to do default delegation, does it? Forwardable is great for presenters, but unless it has a simple way to delegate every method on the delegated object, I would argue that it’s more a presenter than a decorator — which is actually a completely acceptable and useful alternative almost all of the time. So I agree in general, but this post is specifically about decorators, which I can’t seem to figure out how to do (simply) with Forwardable.

        Reply
    4. spacer martin says:
      December 5, 2011 at 10:09 am

      Nice post, it is definitiley a lot cleaner than the other options. But isn’t the method missing a performance killer ? Every view will have to run through several method missings, will there be a performance impact? It would hurt to have to remove it all once the page needs performance optimizations.

      Reply

    Got something to say? Go for it!

    Click here to cancel reply.

    “Skinny Controller; Fat Model” is misleading →
     
    gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.