A Decorator vs. a Subclass

In my most recent article, I mentioned a great new feature in Rails 5.1, delegate_missing_to. With delegate_missing_to, any method that you can’t find on one object is called on another object, instead:

class Player
  delegate_missing_to :@user

  def initalize(user)
    @user = user
  end

  def points
    Game.points_for_user(user.id)
  end
end

Player.new(user).name # calls user.name

But, like Gavin mentioned in the comments, this seems like an odd way to avoid inheritance. Why not just use a subclass? You’d get the same effect, and you don’t have to add a whole new feature. It seems like a weird thing to add.

There must be a reason delegate_missing_to was added, though. And for Rails features, pull requests are a great way to find those reasons. In this pull request, DHH mentioned why he suggested the feature:

Here’s a common pattern if you want to build a decorator:

That seems like a pretty good place to start digging.

Why decorators?

When you build a decorator, you’re changing the way an object acts, without creating a new subclass. For example, in the code from earlier:

class Player
  delegate_missing_to :@user

  def initalize(user)
    @user = user
  end

  def points
    Game.points_for_user(user.id)
  end
end

You’d say that “Player decorates user,” because a Player almost acts like a User, but has an extra method, points. And it does this without inheritance.

Why would you need something like this? That’s a tough question to answer, because like many design patterns, it’s not always clear where you’d want to use it instead of something else.

When would you use a decorator?

Decorators could just be a more complicated way to do inheritance. I mean, which of these two lines of code is better?

player = Player.new(User.new(name: "Justin")) # Player decorates User
player = Player.new(name: "Justin")           # Player subclasses User

Clearly the second one, right? Here, creating Player as a decorator instead of a subclass is just a waste of code.

But sometimes, you want to add functionality to an object later on, far away from where you created the object. For example, what if you had code like this?

user = User.find(1)

... some time later ...

player = Player.new(user)

Now, you can create the user however you want, in whatever method you want. The code that creates the User object doesn’t know or care that a Player class even exists. And you can still use the original User object if you don’t want those extra methods anymore.

This helps you separate behavior into different classes. Each class can focus on how the User object would be used in a specific situation – a Player, an Employee, a Developer. With inheritance, it’s easy for all this stuff to get jumbled together.

MrChris mentioned another benefit to decorators in the comments:

When you decorate an object, you can only call the public methods on that object. When you subclass, you can call any method, even private ones. That can make subclasses break more frequently, because they can accidentally rely on their parents’ implementation details. Those details will usually change more often than the public methods.

Decorators can be especially useful when you’re breaking apart large classes. With decorators, it’s easier to follow the Single-Responsibility Principleeach decorator can do one thing and do it well, and you can combine decorators to get more complex behavior.


There are a lot of ways to share behavior in Ruby. You can subclass, you can combine modules, you can even grab methods off one class and attach them to another if you want to. The decorator pattern, though, gives you something a little different. You’re just using instance variables and method calls, the building blocks of any object-oriented language. From those fundamentals, you can have flexible behavior while your app is running – all without overcomplicating your code.

Pushing through tutorials, and still not learning anything?

Have you slogged through the same guide three times and still don't know how to build a real app?

In this free 7-day Rails course, you'll learn specific steps to start your own Rails apps — without giving up, and without being overwhelmed.

You'll also discover the fastest way to learn new Rails features with your 32-page sample of Practicing Rails: Learn Rails Without Being Overwhelmed.

Sign up below to get started:

Powered by ConvertKit

Did you like this article? You should read these:

Comments