Polimorfismo, Comprobación de Tipos y Tipado Pato (Duck Typing)

Repase la sección Clase y Tipo de un Objeto. Polimorfismo 10.8.4.

Red or yellow, black or white, they are all ducks in the VM’s sight.

En programación orientada a objetos, el polimorfismo se refiere a la propiedad por la que es posible enviar mensajes (llamar a métodos) con el mismo nombre a objetos de clases distintas.

El único requisito que deben cumplir los objetos que se utilizan de manera polimórfica es saber responder al mensaje que se les envía.

[~/srcLPPruby/TheRubyProgrammingLanguage/Chapter3DatatypesAndObjects]$ cat type_versus_class_checking.rb 
# page 75
require "stringio"
class Tutu
  # Appends " chazam!" to its argument
  def chazam_check(x) # duck typing style
    raise "Error in chazam_check" unless x.respond_to? :"<<"

    x << " chazam!"
  end
end

if __FILE__ == $0
  t = Tutu.new

  t = Tutu.new
  puts "procesing a String"
  p t.chazam_check("ban ban")  # "ban ban chazam!""
  puts "procesing an Array"
  p t.chazam_check([1,2,3])    # [1, 2, 3, " chazam!"]"
  begin
    puts "Processing a Fixnum"
    p t.chazam_check(6)        # can't convert String into Integer
  rescue
    puts "t.chazam_check(6) fails:\n\tException type: <#{$!.class}>\n\tmessage: <#{$!}>"
  end
end

[~/srcLPPruby/TheRubyProgrammingLanguage/Chapter3DatatypesAndObjects]$ ruby type_versus_class_checking.rb 
procesing a String
"ban ban chazam!"
procesing an Array
[1, 2, 3, " chazam!"]
Processing a Fixnum
t.chazam_check(6) fails:
  Exception type: <TypeError>
  message: <can't convert String into Integer>

[~/srcLPPruby/TheRubyProgrammingLanguage/Chapter3DatatypesAndObjects]$ cat type_versus_class_checking2.rb 
# page 75
require "stringio"
class Tutu
  # Print the lengths of the lines in the IO object
  def show_lengths(x)
    raise "Error in show_lengths" unless x.is_a? IO
    #raise "Error in show_lengths" unless x.respond_to? "each_line"
    x.each_line do |line|
      print "#{line.length} "
    end
    puts ""
  end
end

if __FILE__ == $0
  t = Tutu.new

  #######################################################
  t.show_lengths(IO.open(File.open($0).to_i)) # 10 19 ...
  begin
    t.show_lengths(StringIO.open("Hello World!\nHello Mars!")) # Error in show_lengths
  rescue
    puts "t.show_lengths fails with StringIO:\n\tException type: <#{$!.class}>\n\tmessage: <#{$!}>"
  end
end

[~/srcLPPruby/TheRubyProgrammingLanguage/Chapter3DatatypesAndObjects]$ ruby type_versus_class_checking2.rb 
10 19 11 52 22 52 68 26 30 8 12 6 4 1 18 15 1 58 58 8 87 9 100 6 4 
t.show_lengths fails with StringIO:
  Exception type: <RuntimeError>
  message: <Error in show_lengths>
Aunque quizá sea larga y requiera para su comprensión completa mas conocimientos de los que tenemos en este punto, escuchar la charla Less: The Path to a Better Design por Sandi Metz en Vimeo puede resultar útil. Metz muestra como el uso de Tipado Pato (Duck Typing) puede ayudar a desacoplar nuestras clases, mejorando así nuestros diseños y haciéndo que nuestro software sea mas adaptable a los cambios que necesariamente habrán de sobrevenir durante su vida útil.

Véanse algunos comentarios de Nathan Youngman sobre la lectura del libro "Practical Object Oriented Design in Ruby: An Agile Primer" de Sandi Metz:

Procedural programs operate by performing a series of steps, whereas object-oriented programs are about sending messages.

When one object tells another to perform a series of steps, it knows too much! To minimize dependencies, an object needs to ask for what it wants, and trust that the receiver takes the necessary steps. It doesn't need to know the details.

The distinction between a message that asks for what the sender wants and a message that tells the receiver how to behave may seem subtle but the consequences are significant.

Rather than depending on a concrete Class, consider relying on a duck type that implements the necessary message. Depending on a “duck-like” thing is much more flexible.

Casiano Rodriguez León 2015-06-18