Como você faz o polimorfismo em Ruby?
-
02-07-2019 - |
Pergunta
Em C #, eu posso fazer isso:
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!";
}
}
Obviamente, a saída é (Um pouco parafraseado):
- Woof
- Dog está dormindo
- Meow
- gato está dormindo
Uma vez que C # é muitas vezes ridicularizado por sua sintaxe tipo detalhado, como você lida com o polimorfismo / métodos virtuais em uma linguagem digitada pato como Ruby?
Solução
edit: adicionado mais código para sua pergunta atualizadas
IMPORTANTE: Eu não usei o Ruby em um ano ou assim, e não o tem instalado nesta máquina, de modo que a sintaxe pode ser totalmente errado. Mas os conceitos estão corretos.
A exata mesma maneira, com classes e métodos substituídos:
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
}
Outras dicas
Todas as respostas até agora parecem muito bons para mim. Eu pensei que eu tinha acabado de mencionar que a coisa toda herança não é totalmente necessário. Excluindo-se o comportamento de "dormir" por um momento, podemos conseguir todo o resultado desejado usando pato-digitação e omitindo a necessidade de criar uma classe base animal em tudo. Googling para "pato-digitando" deve render qualquer número de explicações, assim, por aqui, vamos apenas dizer "se ele anda como um pato e grasna como um pato ..."
O comportamento "sleep" poderia ser fornecido por meio de um módulo de mixin, como Array, Hash e outro Rubi classes built-in inclue Enumerable. Eu não estou sugerindo é necessariamente melhor, só um talvez mais idiomaticamente Rubi forma diferente e de fazê-lo.
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
Você sabe o resto ...
Usando o Ruby idiomática
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
Com base na resposta anterior, é assim que você pode fazê-lo?
Segundo corte após esclarecimento:
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
}
(Eu vou deixar isso como está, mas "self.class.name" vitórias sobre ".to_s")
O princípio da tipagem pato é apenas que o objeto tem de responder aos métodos chamados. Então algo assim pode fazer o truque demasiado:
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
Um pouco variante da solução da manveru que dinâmica criar diferentes tipos de objeto com base em uma variedade de tipos de classe. Não é realmente diferente, um pouco mais clara.
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
Isto é como eu ia escrever isso:
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
Não é realmente diferente das outras soluções, mas este é o estilo que eu preferiria.
Isso é 12 linhas vs. as 41 linhas (na verdade, você pode raspar 3 linhas usando um inicializador de coleção) do exemplo original C #. Não é mau!
Há um método becomes
que implementa um polimorfismo (por enfrentar todas as variáveis ??de instância de determinada classe de novo)
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
e o resultado é:
Dog with name Spot becomes Cat
and makes a noise: Meow
----
Cat with name Tom becomes Cat
and makes a noise: Meow
----
-
Um polimorfismo pode ser útil para declaração
if
evitar ( antiifcampaign.com ) -
Se você usar
RubyOnRails
métodobecomes
já está implementado:becomes
-
Quicktip: se você misturar polimorfismo com STI que traz a você a combinação mais eficiente para refatorar seu código
Eu gostaria que ajudou você