Polimorfismo en Ruby

Se me ha ocurrido escribir sobre este tema para aquellos que, como yo, vienen de otros lenguages orientados a objetos. Hay cosas en Ruby que se hacen de manera distinta y es muy dificil cambiar las costumbres de un día para otro.

Una de las cosas que se hace distinto en Ruby es el tema del polimorfismo. En C++ tenemos las denominadas clases abstractas y en C# o Java los interfaces. Estos elementos de programacion nos facilitan el polimorfismo ya que imponen un contrato a cumplir por la clase de una instancia. De esta maneras y, en tiempo de compilación, se comprueba si un determinado objeto responde a una determinada función, independientemente de su implementación (de ahí el polimorfismo).

En Ruby los interfaces son totalmente inutiles: A cada objeto le basta con responder al metodo buscado para que se le pueda invocar sea del tipo que sea el objeto. Esto se conoce como Duck Typing:

cuando veo un ave que camina como un pato, nada como un pato y se oye como un pato, a esa ave yo la llamo pato.

Es eso polimorfismo? Pues en un grado extremo, en mi opinión, sí lo es.

Pero Ruby tiene una característica muy interesante y es que se puede definir un interfaz e incluirlo en la funcionalidad de la clase consiguiendo no solo que un objeto responda a ciertas llamadas sino que además incluya el comportamiento esperado de esta funcionalidad.

Con esto se conigue dotar a nuestros objetos de un funcionamiento predefinido y, aunque sean objetos de características muy distintas si se les incluyen cierto modulo pasaran a tener un comportamiento similar en dicho modulo. Sirva como ejemplo el siguiente código:

module Herbivore
  def eat(meal)
    raise "Herbibore only eats Grass" unless meal.kind_of?(Grass)
    "#{self.class.name}: I am eating grass"
  end
end

module Carnivore
  def eat(meal)
    raise "Carnivore only eats Cows" unless meal.kind_of?(Cow)
    "#{self.class.name}: I am eating a cow"
  end
end

module Sleeper
  def sleep
    "#{self.class.name}: Rest till the next day"
  end
end

class Grass
end

class Cow
  include Herbivore
  include Sleeper
end

class Lion
  include Carnivore
  include Sleeper
end

c = Cow.new
g = Grass.new
l = Lion.new

puts c.eat(g)
puts l.eat(c)

[c,l].each {|a| puts a.sleep}

Tanto el Lion como la Cow son Sleepers. Con esto, no solo he conseguido hacerles cumplir un interfaz (ambos responden al método sleep) sino que además los dos comparten la misma funcionalidad.

En el caso de ser Carnivore o Herbívore también he conseguido que ambos cumplan un interfaz (respondiendo al método eat), pero su funcionalidad difiere (una Cow no puede comer otra cosas que Grass). Este es la flexibilidad de Ruby: Nada te impide definir interfaces de funcionamiento (comprobados en tiempo de ejecución), como extender dichos interfaces en funcionalidades para compartir comportamientos entre objetos distintos.

Como se puede ver, se convierte en una manera muy natural de programar:

  • Una Cow es Hervibore y Sleeper
  • Un Lion es Carnivore y Sleeper
  • Una Grass no es nada de lo que tengamos definido.

Por cierto, como era de imaginar, la salida del codigo anterior sería algo como lo siguiente:

Cow: I am eating grass
Lion: I am eating a cow
Cow: Rest till the next day
Lion: Rest till the next day

Todavía no hay comentarios

Enviar un comentario nuevo

El contenido de este campo se mantiene privado y no se mostrará públicamente.
  • Etiquetas HTML permitidas: <a> <blockquote> <br> <cite> <code> <dd> <div> <dl> <dt> <em> <h1> <h2> <h3> <h4> <h5> <h6> <hr> <img> <li> <ol> <p> <pre> <span> <strong> <swf> <table> <tbody> <td> <th> <tr> <ul>
    Allowed Style properties: background-color, background-image, border, border-bottom, border-bottom-color, border-bottom-style, border-bottom-width, border-color, border-left, border-left-color, border-left-style, border-left-width, border-right, border-right-color, border-right-style, border-right-width, border-spacing, border-style, border-top, border-top-color, border-top-style, border-top-width, border-width, color, direction, font, font-family, font-size, font-style, font-variant, font-weight, height, left, line-height, list-style-type, margin, margin-bottom, margin-left, margin-right, margin-top, padding, padding-bottom, padding-left, padding-right, padding-top, right, text-align, text-decoration, top, width
  • Syntax highlight code surrounded by the {syntaxhighlighter OPTIONS}...{/syntaxhighlighter} tags.
  • E-Mail addresses are hidden with reCAPTCHA Mailhide.

Más información sobre opciones de formato

CAPTCHA
Esta pregunta es para comprobar si eres un ser humano y evitar el envío automático desde sistemas de spam