Basic Polymorphism
In Ruby, inheritance polymorphism can be achieved doing something like the following:
class Bird
  def initialize(name)
    @name = name
  end
  def speak
    puts 'Tweet'
  end
  def fly
    puts 'Up up and away...'
  end
end
This is our base class, Bird. All other classes will inherit the methods in this class.
class Duck < Bird
  def speak
    puts "Quack I am #{@name}"
  end
end
class Penguin < Bird
  def speak
    puts "Squak I am #{@name}"
  end
  def fly
    puts 'Nope. I swim...'
  end
end
These are the classes that inherit from our base class. Ducks and Penguins are Birds. So, they will have basic methods speak() and fly(). The cool thing is we can override existing methods of the superclass from within the subclass to change the default behavior of a Bird. For example, Penguins don't tweet or fly. So, we override the base methods to better reflect the behavior of penguins.
So, now we can do something like this:
birds = [Bird.new('Tweetie'), Duck.new('Donald'), Penguin.new('Mumbo')]
birds.each do |b|
  b.fly
end
# Up up and away...
# Up up and away...
# Nope. I swim...
birds.each do |b|
  b.speak
end
# Tweet
# Quack I am Donald
# Squak I am Mumbo
The Ruby Way
In Ruby, traditional methods of polymorphism (seen above and in languages such as Java) are not widely accepted. A concept known as "duck typing" is preferred. You can read more about duck typing here.Basically, we follow a simple rule: If it walks, swims, and quacks like a duck, I call that bird a duck
We can get the same type of "polymorphic" behavior by simply implementing each method individually in each class and including any common methods:
class Duck
  def speak
    puts "Quack I am #{@name}"
  end
  def fly
    puts 'Up up and away...'
  end
end
class Penguin
  def speak
    puts "Squak I am #{@name}"
  end
  def fly
    puts 'Nope. I swim...'
  end
end
Now, each type of Bird has its methods individually defined according to its own specific behavior. What about the shared initialize() constructor?
module Bird
  def initialize(name)
    @name = name
  end
end
We put any shared methods in a module. This way, we can include them in our classes:
class Duck include Bird ... end class Penguin include Bird ... end
Do It Your Way
I have no strong opinion on whatever approach you decide to use. There are evangelists on both ends who live and die by either one. Solve your problem any way you please!Resources:
Ruby polymorphism discussion: http://stackoverflow.com/questions/137661/how-do-you-do-polymorphism-in-ruby
The Ruby Way excerpt: http://books.google.com/books?id=ows9jTsyaaEC&pg=PA14
Ruby Learning: http://rubylearning.com/satishtalim/ruby_inheritance.html
Very nice, thank you. Is duck typing intended to fully replace inheritance, or are there some cases where inheritance is preferred, even in ruby?
ReplyDelete