2008-09-26 51 views
14

En C#, I puede hacer esto:¿Cómo se hace el polimorfismo en Ruby?

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, la salida es (ligeramente parafraseado):

  • Woof
  • perro está durmiendo
  • maullido
  • gato está durmiendo

Puesto que C# es a menudo burlado por su sintaxis tipo detallado, ¿cómo manejar métodos polimorfismo/virtuales en un lenguaje con tipos de pato como Ruby?

+0

usted ha mencionado polimorfismo "real". ¿Cuál es su definición de "real" te dejo la mía: http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming – OscarRyz

Respuesta

15

de edición: se ha añadido más código de tu pregunta actualizado

exención de responsabilidad: No he utilizado Rubí en un año o así, y no lo tiene instalado en esta máquina, por lo que la sintaxis puede ser que sea totalmente erróneo. Pero los conceptos son correctos.


la misma manera, las clases y los métodos sobrescritos:

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 
} 
+0

Lo sentimos, pero la versión de Ruby es "Gato clase: Animal"? ¿No es "<" por herencia? –

+4

Brent: eso sería "Ruby como lo recuerda un usuario de Python" –

+1

Entonces, ¿class.name dará como resultado Animal or Dog? Soy realmente curioso. – FlySwat

2

Basándose en la respuesta anterior, ¿es así como podría hacerlo?


segundo corte después de la clarificación:

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 
} 

(voy a dejar esto como es, pero "self.class.name" gana sobre ".to_s")

+1

'MakeNoise' en lugar de' make_noise'? ¿Usando 'regresa'? ¿Por qué no seguir las convenciones de Ruby? –

+0

¿Por qué debería seguir una convención que no me gusta intensamente? En mi humilde opinión, el guión bajo es feo e impide el flujo de la lectura; CamelCase es mucho más fácil de leer. ¿Realmente me votaste por eso? Meh es mi respuesta –

+1

@ Brent- Debes seguir una convención (independientemente de tu opinión sobre ella) si estás escribiendo códigos que otras personas usan. Esperarán que los nombres de CamelCase se refieran a las clases y subrayar nombres separados para referirse a los métodos de clase y no sabrán nada acerca de sus ideas sobre la convención. Si solo estás escribiendo un código para que lo leas, esa es una cosa, pero si otros van a usar tu código, sigue el Principio del menor asombro. – bta

10

El uso idiomático Rubí

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 
+0

No soy un gurú de Ruby, pero no "pone" la cadena de salida? Si es así, este ejemplo lo llama dos veces cuando se llama 'sleep', una vez dentro del método y una vez con su valor de retorno (¡cualquiera que sea!) –

+0

Tienes razón, y' puts' devuelve 'nil', por lo que "puts" externo simplemente "imprimiría" una nueva línea, equivalente a 'print (" # {nil} \ n ")', que es lo mismo que 'print (" \ n ")'. No noté la nueva línea final en mi salida, lo siento. – manveru

+0

Debería ser 'puts animal.make_noise; animal.sleep', not 'puts animal.sleep' – YoTengoUnLCD

0

Esto es como lo escribiría:

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 

No es realmente diferente de las otras soluciones, pero este es el estilo que yo preferiría.

Eso es 12 líneas frente a las 41 líneas (en realidad, puede afeitarse 3 líneas utilizando un inicializador de colección) del ejemplo de C# originales. ¡No está mal!

22

Todas las respuestas hasta el momento se ven bastante bien para mí. Pensé que solo mencionaría que toda la herencia no es del todo necesaria. Excluyendo el comportamiento de "dormir" por un momento, podemos lograr el resultado deseado utilizando el pato-tipado y omitiendo la necesidad de crear una clase base Animal. Google para "pato-escribir" debe producir cualquier número de explicaciones, por lo que aquí vamos a decir "si camina como un pato y grazna como un pato ..."

El comportamiento de "reposo" se podría proporcionar mediante el uso de un módulo de mixin, como Array, Hash y otras clases integradas de Ruby inclue Enumerable. No estoy sugiriendo que sea necesariamente mejor, solo una forma diferente y tal vez más idiomática de Ruby de hacerlo.

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 

¿Sabes el resto ...

+2

+1 para mezclar la funcionalidad común en lugar de heredarla. Si bien ambos métodos funcionan, esto es más bien "la forma Ruby de hacerlo" (para este ejemplo en particular). – bta

+0

Es curioso que en CRuby, esto sea casi equivalente a la herencia: incluyendo el módulo animal establece la superclase de Perro para ser animal, y la superclase anterior de Perro para ser la superclase de Animal. Si bien esto funciona, y puede ser "la forma más fácil de hacerlo", uno debe notar que esto es mucho menos eficiente que heredar: CRuby hace una copia del módulo para cada clase, incluyéndolo (por la razón indicada anteriormente). – YoTengoUnLCD

1

El principio de la tipificación de pato es sólo que el objeto tiene que responder a los llamados métodos. Así que algo así puede hacer el truco 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 
1

Una pequeña variante de solución de manveru cuales dinámico crear diferentes tipos de objetos basada en una variedad de tipos de clase. No es realmente diferente, solo un poco más claro.

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 
0

Hay un método becomes que implementa un polimorfismo (por afrontamiento todas las variables de instancia de la clase dada a uno nuevo)

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 

y el resultado es:

Dog with name Spot becomes Cat 
and makes a noise: Meow 
---- 
Cat with name Tom becomes Cat 
and makes a noise: Meow 
---- 
  • A polimorfismo podría ser útil para evitar la declaración if ())

  • Si utiliza RubyOnRailsbecomes método ya se implementa: becomes

  • QuickTip: si se mezcla el polimorfismo con STI que le trae el combo más eficiente para refactorizar su código

I desearía que le ayudó a