views:

2285

answers:

7

In C#, I can do this:

class Program
{
    static void Main(string[] args)
    {
        List<Animal> animals = new List<Animal>();

        animals.Add(new Dog());
        animals.Add(new Cat());

        foreach (Animal a in animals)
        {
            Console.WriteLine(a.MakeNoise());
            a.Sleep();
        }
    }
}

public class Animal
{
    public virtual string MakeNoise() { return String.Empty; }
    public void Sleep()
    {
        Console.Writeline(this.GetType().ToString() + " is sleeping.");
    }
}

public class Dog : Animal
{
    public override string MakeNoise()
    {
        return "Woof!";
    }
}

public class Cat : Animal
{
    public override string MakeNoise()
    {
        return "Meow!";
    }
}

Obviously, the output is (Slightly paraphrased):

  • Woof
  • Dog is Sleeping
  • Meow
  • Cat is Sleeping

Since C# is often mocked for its verbose type syntax, how do you handle polymorphism/virtual methods in a duck typed language such as Ruby?

+7  A: 

edit: added more code for your updated question

disclaimer: I haven't used Ruby in a year or so, and don't have it installed on this machine, so the syntax might be entirely wrong. But the concepts are correct.


The exact same way, with classes and overridden methods:

class Animal
    def MakeNoise
        return ""
    end
    def Sleep
        print self.class.name + " is sleeping.\n"
    end
end

class Dog < Animal
    def MakeNoise
        return "Woof!"
    end
end

class Cat < Animal
    def MakeNoise
        return "Meow!"
    end
end

animals = [Dog.new, Cat.new]
animals.each {|a|
    print a.MakeNoise + "\n"
    a.Sleep
}
John Millikin
Sorry, but what version of Ruby is "class Cat : Animal"? Isn't it "<" for inheritance?
Brent.Longborough
Brent: that would be "Ruby as remembered by a Python user"
John Millikin
So, will class.name output Animal or Dog? I'm genuinely curious.
FlySwat
It will output "Dog".
John Millikin
Yep LOL, I sometimes do "Python as remembered by a Ruby user" What's the reverse of Pythonic? Rubenesque?
Brent.Longborough
+3  A: 

Building on the previous answer, is this how you might do it?


Second cut after clarification:

class Animal
    def MakeNoise
        raise NotImplementedError # I don't remember the exact error class
    end
    def Sleep
        puts self.class.to_s + " is sleeping."
    end
end

class Dog < Animal
    def MakeNoise
        return "Woof!"
    end
end

class Cat < Animal
    def MakeNoise
        return "Meow!"
    end
end

animals = [Dog.new, Cat.new]
animals.each {|a|
    puts a.MakeNoise
    a.Sleep
}

(I'll leave this as is, but "self.class.name" wins over ".to_s")

Brent.Longborough
`MakeNoise` as opposed to `make_noise`? Using `returns`? Why not follow Ruby conventions?
macek
Why should I follow a convention I dislike intensely? IMHO, underscore is ugly and impedes the flow of reading; CamelCase is much easier to read.Did you really vote me down just for that? Meh is my answer
Brent.Longborough
@Brent- You should follow a convention (regardless of your opinion of it) if you are writing code that other people use. They will be expecting CamelCase names to refer to classes and underscore_separated names to refer to class methods and won't know anything about your thoughts on the convention. If you are only writing code for yourself to read then that's one thing but if others are going to be using your code, go with the Principle Of Least Astonishment.
bta
@bta - I understand where you're coming from. I'm really saying two things: First, I really don't think that a deviation from the "accepted" conventions *here* is worth a downvote; and second, that the "accepted" conventions are ugly and force me to choose between "being in the community" and "code beauty" (in the eye of *this* beholder.Yes, I use returns, because I think it makes clearer code. Yes, I use CamelCase, because I think it's easier to read. And yes, I use them here because I want to show what I think is better, and let others judge.
Brent.Longborough
@Brent- I often use `return` as well :) I disagree with some of your opinions on style, but I agree that style here is not worth a downvote. +1 for a working solution, despite coding style.
bta
I hate ruby conventions too... anyway I'm getting used to them. +1 for you anyway =)
emzero
+7  A: 

Using idiomatic Ruby

class Animal
  def sleep
    puts "#{self.class} is sleeping"
  end
end

class Dog < Animal
  def make_noise
    "Woof!"
  end
end

class Cat < Animal
  def make_noise
    "Meow!"
  end
end

[Dog, Cat].each do |obj|
  animal = obj.new
  animal.make_noise
  puts animal.sleep
end
manveru
I'm not a Ruby guru, but doesn't `puts` output the string? If so, this example calls it twice when calling `sleep` -- once within the method, and once with its return value (whatever that would be!)
Drew Noakes
You're right, and `puts` returns `nil`, so the outer `puts` would simply `print` a newline, equivalent to `print("#{nil}\n")`, which is same as `print("\n")`. I didn't notice the trailing newline in my output, sorry about that.
manveru
A: 

This is how I would write it:

class Animal
  def make_noise; '' end
  def sleep; puts "#{self.class.name} is sleeping." end
end

class Dog < Animal; def make_noise; 'Woof!' end end
class Cat < Animal; def make_noise; 'Meow!' end end

[Dog.new, Cat.new].each do |animal|
  puts animal.make_noise
  animal.sleep
end

It's not really different from the other solutions, but this is the style that I would prefer.

That's 12 lines vs. the 41 lines (actually, you can shave off 3 lines by using a collection initializer) from the original C# example. Not bad!

Jörg W Mittag
+7  A: 

All the answers so far look pretty good to me. I thought I'd just mention that the whole inheritance thing is not entirely necessary. Excluding the "sleep" behaviour for a moment, we can achieve the whole desired outcome using duck-typing and omitting the need to create an Animal base class at all. Googling for "duck-typing" should yield any number of explanations, so for here let's just say "if it walks like a duck and quacks like a duck..."

The "sleep" behaviour could be provided by using a mixin module, like Array, Hash and other Ruby built-in classes inclue Enumerable. I'm not suggesting it's necessarily better, just a different and perhaps more idiomatically Ruby way of doing it.

module Animal
  def sleep
    puts self.class.name + " sleeps"
  end
end

class Dog
  include Animal
  def make_noise
    puts "Woof"
  end
end

class Cat
  include Animal
  def make_noise
    puts "Meow"
  end
end

You know the rest...

Mike Woodhouse
+1 for mixing-in the common functionality instead of inheriting it. While both methods work, this is more of "the Ruby way of doing it" (for this particular example).
bta
A: 

The principle of duck typing is just that the object has to respond to the called methods. So something like that may do the trick too :

module Sleeping
  def sleep; puts "#{self} sleeps"
end

dog = "Dog"
dog.extend Sleeping
class << dog
  def make_noise; puts "Woof!" end
end

class Cat
  include Sleeping
  def to_s; "Cat" end
  def make_noise; puts "Meow!" end
end

[dog, Cat.new].each do |a|
  a.sleep
  a.make_noise
end
zimbatm
A: 

A little variant of manveru's solution which dynamic create different kind of object based in an array of Class types. Not really different, just a little more clear.

Species = [Dog, Cat]

Species.each do |specie|
  animal = specie.new   # this will create a different specie on each call of new
  print animal.MakeNoise + "\n"
  animal.Sleep
end
Anderson