How do you do Polymorphismus in Ruby?
-
02-07-2019 - |
Frage
In C #, ich kann 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 der Ausgang (Ein wenig paraphrasiert):
- Woof
- Hund Schläft
- Meow
- Katze schläft
Da C # oft für seine ausführliche Art Syntax verspottet wird, wie gehen Sie Polymorphismus / virtuelle Methoden in einer Ente typisierte Sprache wie Ruby?
Lösung
edit: mehr Code für Ihre aktualisierte Frage hinzugefügt
Disclaimer: Ich habe nicht Rubin in einem Jahr verwendet oder so, und habe es nicht auf diesem Computer installiert, so dass die Syntax möglicherweise völlig falsch sein. Aber die Begriffe korrekt sind.
Genau die gleiche Art und 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
}
Andere Tipps
Alle Antworten so weit schauen ziemlich gut zu mir. Ich dachte, ich würde nur erwähnen, dass das ganze Erbe, was nicht unbedingt notwendig ist. Ohne den „Schlaf“ Verhalten für einen Moment können wir das ganze gewünschte Ergebnis mit Ente-Eingabe erreichen und die Notwendigkeit, das Weglassen überhaupt ein Tier Basisklasse zu erstellen. für „duck-typing“ Googeln eine beliebige Anzahl von Erklärungen ergeben sollte, so denn hier sagen wir einfach „wenn es wie eine Ente geht und quakt wie eine Ente ...“
Das „Schlaf“ Verhalten durch die Verwendung eines mixin Modul zur Verfügung gestellt werden könnte, wie Array, Hash und anderen Ruby-integrierte Klassen inclue Enumerable. Ich schlage nicht vor, es ist unbedingt besser, nur eine andere und vielleicht mehr idiomatisch Ruby-Art und Weise 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
Sie kennen den Rest ...
Mit idiomatischem 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 |clazz|
animal = clazz.new
puts animal.make_noise
animal.sleep
end
Auf der Grundlage der vorherigen Antwort, das ist, wie Sie könnte es tun?
Zweiter Schnitt nach der 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 diese lassen wie es ist, aber "self.class.name" gewinnt ".to_s")
Das Prinzip der Ente Typisierung ist nur, dass das Objekt auf die aufgerufenen Methoden zu reagieren. So etwas wie das kann den Trick auch:
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
Eine kleine Variante der manveru-Lösung, die eine dynamische andere Art von Objekt in einer Reihe von Klassentypen basieren erstellen. Nicht wirklich anders, nur ein wenig klarer.
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
Dies ist, wie ich es schreiben würde:
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
Es ist nicht wirklich von den anderen Lösungen, aber das ist der Stil, den ich lieber.
Das ist 12 Zeilen gegen die 41 Linien (tatsächlich, können Sie 3 Zeilen abrasieren durch eine Auflistungsinitialisierer verwendet wird) aus dem ursprünglichen C # Beispiel. Nicht schlecht!
Es gibt eine Methode becomes
, die einen Polymorphismus implementiert (durch alle Instanzvariablen von bestimmten Klasse zu neuen Bewältigungs)
class Animal
attr_reader :name
def initialize(name = nil)
@name = name
end
def make_noise
''
end
def becomes(klass)
became = klass.new
became.instance_variables.each do |instance_variable|
value = self.instance_variable_get(instance_variable)
became.instance_variable_set(instance_variable, value)
end
became
end
end
class Dog < Animal
def make_noise
'Woof'
end
end
class Cat < Animal
def make_noise
'Meow'
end
end
animals = [Dog.new('Spot'), Cat.new('Tom')]
animals.each do |animal|
new_animal = animal.becomes(Cat)
puts "#{animal.class} with name #{animal.name} becomes #{new_animal.class}"
puts "and makes a noise: #{new_animal.make_noise}"
puts '----'
end
und das Ergebnis ist:
Dog with name Spot becomes Cat
and makes a noise: Meow
----
Cat with name Tom becomes Cat
and makes a noise: Meow
----
-
Ein Polymorphismus nützlich sein könnte, zu vermeiden
if
Aussage ( antiifcampaign.com ) -
Wenn Sie
RubyOnRails
becomes
Methode wird bereits umgesetzt:becomes
-
Quicktip: Wenn Sie Polymorphismus mit STI es Sie bringt die effizienteste Combo Code
Refactoring
Ich wünsche es Ihnen geholfen,