Demystifying Duck Typing in Ruby: Take Your OO Design Skills to the Next Level
By: Drew Beckmen
Tags: Programming Languages, Ruby, Object Oriented, Dynamically Typed, Duck Typing

So what is duck typing? Unlike concrete topics in OO design such as inheritance, duck typing is more of a philosophy than a concept rooted in Ruby syntax. The fundamental idea of duck typing is that Ruby does not care about an object’s class; Ruby’s sole concern is whether a given object can respond to the appropriate message (ie. method). In other words, Rubyists should shift away from a class-based perspective and adopt a message-based perspective. An object is defined by what it can do and what messages it can respond to (its interface), not what it is (the class).

To illustrate the concept, I will provide a simple example. First, we will look at an example in which a programmer ignores Ruby’s dynamically typed nature and assumes a “static type” approach.

Since we are working with object-oriented design, we will create an example that models real-world phenomena. Our example will deal with a Trip class. The Trip class will have an instance method called prepare , which will handle all the actions of preparing for a specific trip.

In this code, I have taken a class-focused approach. Using a case statement, I check the class of an individual preparer before calling an instance method within that preparer’s class. However, this approach is short-sighted. It introduces a significant number of dependencies into your code, and as the preparation for a trip becomes more extensive, those dependencies will only increase. Our code depends directly on the existence of classes Vacationer and TravelAgent.

What if we shift our frame of thinking? Since Ruby is dynamically typed, an object is not limited by its type, but by the messages to which it can respond. If we use case statements to check the class of an object, we are essentially writing code as if Ruby were statically typed. We need to rework the code to harness the power of dynamically typed languages.

How could we design our code so that we have just a single dependency? Answer: duck typing.

Our single dependency will be that every object in the preparers array is a Preparer , which will be our duck type. Preparer is not a class we will code or an abstract class we will define as in Java. Instead, Preparer is a duck type in which every Preparer instance will respond to a single method: prepare_trip.

First, we will define our Trip class as follows:

As you can already see, the number of dependencies has dropped dramatically. Now, the prepare method only knows that each preparer responds to a method prepare_trip .

Now, let’s rework our TravelAgent and Vacationer classes!

To test the code, we can run the following line:

We will get the following output:

Magic! Now, we don’t care whether a preparer is an instance of TravelAgent or Vacationer. The class of a preparer is not relevant — we only need to ensure that each preparer responds to the message prepare_trip. By creating a Preparer duck, we harness the power of Ruby as a dynamically typed language.

To reinforce the benefits of duck typing and dynamically typed languages in general, I have included the analogous Java code in the repl below.

Java: Static Typing from Repl.it

Press the green play button to see the output. The program will print the same output as the Ruby program, but the Java implementation is drastically different. Rather than creating a duck type, Java requires us to explicitly state that our Vacationer and TravelAgent classes extend the abstract class Preparer. We can then define an array of Preparer objects in our main method, which is then passed to the prepare method within our Trip class.

In Ruby, there is no need for abstract classes and type checking. All you need to focus on are the messages to which your objects respond!

For many people accustomed to statically typed languages, the concept of duck typing may seem foreign. However, if you are able to get beyond the “class-first” mindset and embrace objects for their behaviors rather than their types, you will be well on your way to becoming a powerful Ruby programmer.