14 Stimmen

Wie kann man Polymorphismus in Ruby einsetzen?

In C# kann ich dies tun:

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!";
    }
}

Offensichtlich ist das Ergebnis (leicht umschrieben):

  • Wuff
  • Der Hund schläft
  • Miau
  • Die Katze schläft

Da C# oft für seine ausführliche Typsyntax verspottet wird, wie gehen Sie mit Polymorphismus/virtuellen Methoden in einer duck typed Sprache wie Ruby um?

22voto

Mike Woodhouse Punkte 50241

Alle bisherigen Antworten sehen für mich ziemlich gut aus. Ich wollte nur erwähnen, dass die ganze Sache mit der Vererbung nicht unbedingt notwendig ist. Sieht man einmal vom "Schlaf"-Verhalten ab, können wir das gewünschte Ergebnis auch mit Duck-Typing erreichen, ohne eine Basisklasse Animal erstellen zu müssen. Wenn man nach "duck-typing" googelt, findet man eine ganze Reihe von Erklärungen, also sagen wir hier einfach "wenn es wie eine Ente läuft und wie eine Ente quakt...".

Das "Schlaf"-Verhalten könnte durch die Verwendung eines Mixin-Moduls bereitgestellt werden, wie Array, Hash und andere in Ruby eingebaute Klassen wie Enumerable. Ich behaupte nicht, dass es unbedingt besser ist, nur eine andere und vielleicht mehr idiomatisch Ruby Weg, es zu tun.

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

Den Rest kennen Sie...

15voto

John Millikin Punkte 190278

Edit: mehr Code für Ihre aktualisierte Frage hinzugefügt

Haftungsausschluss: Ich habe Ruby seit etwa einem Jahr nicht mehr benutzt und habe es auch nicht auf diesem Rechner installiert, daher könnte die Syntax völlig falsch sein. Aber die Konzepte sind korrekt.


Auf genau dieselbe Weise, mit Klassen und überschriebenen Methoden:

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
}

10voto

manveru Punkte 2600

Idiomatisches Ruby verwenden

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 |clazz|
  animal = clazz.new
  puts animal.make_noise
  animal.sleep
end

2voto

Brent.Longborough Punkte 9075

Könnten Sie, aufbauend auf der vorherigen Antwort, folgendermaßen vorgehen?


Zweiter Schnitt nach Klärung:

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
}

(Ich werde dies so belassen, aber "self.class.name" gewinnt gegenüber ".to_s")

1voto

zimbatm Punkte 9228

Das Prinzip der Duck-Typisierung besteht lediglich darin, dass das Objekt auf die aufgerufenen Methoden reagieren muss. So etwas kann also auch den Trick machen:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X