Skip to Content
Course content

193: Duck Typing vs Type Checking in Ruby

Click on the "Edit" button in the top corner of the screen to edit your slide content.

What actually is "Duck Typing" and how does it differ from what I'm used to?

You've probably heard the phrase: "If it walks like a duck and it quacks like a duck, then it's a duck." In Ruby, this isn't just a cute saying; it's a core design philosophy. In many languages, if a function expects a PaymentGateway object, you have to pass it something that explicitly inherits from the PaymentGateway class.

Ruby doesn't care about the "family tree" of the object. It only cares about whether the object can perform the action you're asking for. If you call .process_payment on an object, Ruby doesn't ask, "Are you a PaymentGateway?" It simply asks, "Do you have a method called process_payment?" If the answer is yes, it runs. I love this because it makes your code incredibly flexible.

class Stripe
  def process_payment(amount)
    puts "Processing $#{amount} via Stripe..."
  end
end

class PayPal
  def process_payment(amount)
    puts "Processing $#{amount} via PayPal..."
  end
end

# This method doesn't care about the class, only the behavior
def checkout(gateway, amount)
  gateway.process_payment(amount)
end

checkout(Stripe.new, 100) # Works!
checkout(PayPal.new, 100) # Also works!

Why shouldn't I just use is_a? to make sure I have the right object?

I see a lot of developers coming from Java or C# who instinctively reach for is_a? or kind_of?. They'll write something like if payment_method.is_a?(CreditCard). While that works, it creates a rigid coupling between your logic and your class names.

Imagine you've written a whole system that accepts CreditCard and PayPal. Six months from now, your boss tells you that you need to support ApplePay. If you used type checking with is_a?, you now have to hunt down every single if/else block in your codebase and add || payment_method.is_a?(ApplePay). That's a recipe for bugs.

With duck typing, you don't change a single line of the checkout logic. You just create an ApplePay class with a process_payment method, and the rest of the system accepts it immediately. You're coding to an interface (the method name) rather than an implementation (the class name).

If I stop checking types, won't my app crash constantly with NoMethodError?

This is the valid "scary" part of duck typing. If you pass a String into that checkout method, Ruby will blow up because String doesn't have a process_payment method. You have a few ways to handle this depending on how strict you want to be.

If you're in a situation where you aren't 100% sure what's coming in, I recommend using respond_to?. This is the "polite" way of duck typing. Instead of demanding the object be a certain class, you just ask if it's capable of the specific action.

def checkout(gateway, amount)
  if gateway.respond_to?(:process_payment)
    gateway.process_payment(amount)
  else
    raise "The provided gateway doesn't know how to process payments!"
  end
end

Personally, I try to avoid respond_to? unless I'm building a library for other people. In my own application code, I usually prefer to "ask for forgiveness rather than permission." I'll just call the method and let the error happen during development—that's how I find out I'm passing the wrong object. It keeps the "happy path" of the code much cleaner.




📋 Practical Task

Build a Polyglot Notification Dispatcher

You are building a notification system that can send alerts via different channels. Instead of checking the type of the notifier, you will use duck typing to handle them uniformly.

  • Create three classes: EmailNotifier, SMSNotifier, and SlackNotifier.
  • Each class must implement a method called send_alert(message) that prints a specific string (e.g., "Sending Email: [message]").
  • Create a class called NotificationDispatcher with a method dispatch(notifiers, message).
  • The dispatch method should take an array of notifier objects and a message string, then loop through the array and call send_alert on each one.
  • Test your dispatcher by passing it an array containing one of each notifier.
  • Bonus: Try passing an object that doesn't implement send_alert into the array and use respond_to? inside the dispatch method to skip it gracefully without crashing the program.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.