Subsecciones


Predominancia/Invalidación de Métodos (overriding)

Se entiende por overriding la posibilidad de reemplazar la conducta de un método en la superclase.

[19:28][~/Dropbox/src/ruby/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules]$ cat overriding_methods.rb 
class WorldGreeter
  def greet                      # Display a greeting
    puts "#{greeting} #{who}"
  end 

  def greeting                   # What greeting to use 
    "Hello"
  end 

  def who                        # Who to greet
    "World"
  end 
end

# Greet the world in Spanish
class SpanishWorldGreeter < WorldGreeter
  def greeting                   # Override the greeting
    "Hola"
  end 
end

# We call a method defined in WorldGreeter, which calls the overridden
# version of greeting in SpanishWorldGreeter, and prints "Hola World"
SpanishWorldGreeter.new.greet

Ejecución:

[19:28][~/Dropbox/src/ruby/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules]$ ruby overriding_methods.rb 
Hola World

[19:34][~/Dropbox/src/ruby/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules]$ cat abstract_and_concrete_classes.rb 
# This class is abstract; it doesn't define greeting or who
# No special syntax is required: any class that invokes methods that are
# intended for a subclass to implement is abstract.
class AbstractGreeter
  def greet
    puts "#{greeting} #{who}"
  end
end

# A concrete subclass
class WorldGreeter < AbstractGreeter
  def greeting; "Hello"; end
  def who; "World"; end
end

WorldGreeter.new.greet  # Displays "Hello World"

Ejecución:

[19:34][~/Dropbox/src/ruby/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules]$ ruby abstract_and_concrete_classes.rb 
Hello World


Predominancia de Métodos Privados

Los métodos privados no pueden ser invocados con un receptor explícito pero son heredados por las subclases. Si ocurre que
  1. Nuestra clase hereda de otra
  2. Definimos un método (cualquiera que sea su visibilidad) en nuestra subclase que tiene el mismo nombre que un método privado en la superclase
  3. Entonces sobreescribimos el método privado.
  4. Si lo hemos hecho por equivocación y el método privado así llamado era usado como un helper por la superclase es muy posible que esta colisión produzca una conducta indeseable.
  5. Cuando herede procure estar familiarizado con la implementación de la superclase.

El siguiente ejemplo ilustra el problema:

[~/srcLPP/Chapter7ClassesAndModules/overriding_methods]$ cat overriding_private_methods.rb 
class MotherFoo
  def titi 
   foo
  end

  private
  def foo
   "mother foo"
  end
end

class ChildFoo < MotherFoo
  def foo 
    "child foo"
  end
end

c = ChildFoo.new
puts c.titi # child foo

print "Private methods of mother class: "
class MotherFoo
  puts MotherFoo.private_instance_methods(false)
end

La llamada a titi hace que se llame al método foo de la clase hija en vez de al método foo de la clase madre que era lo que el programador pretendía.

Una manera de paliar este problema consiste en documentar los métodos privados de nuestra clase en una sección para desarrolladores.

Como muestra la ejecución del anterior ejemplo siempre es posible averiguar que métodos privados tiene la superclase llamando a MotherFoo.private_instance_methods. De esta forma sabemos que nombres evitar en nuestra clase hija:

[~/srcLPP/Chapter7ClassesAndModules/overriding_methods]$ ruby overriding_private_methods.rb 
child foo
Private methods of mother class: foo
[~/srcLPP/Chapter7ClassesAndModules/overriding_methods]$

Otra forma de aliviar el problema es:

  1. crear un método anónimo de clase Proc o una lambda
  2. usar una variable de instancia de la clase para guardarlo.
  3. crear un accessor de lectura para la variable de instancia de la clase como objeto
Esto hace mas difícil que las clases que hereden puedan colisionar con nuestro método privado.

11:10][~/Dropbox/src/ruby/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules]$ cat overriding_private_methods3.rb 
class MotherFoo
  @ofoo = Proc.new do |x|
   "mother foo #{x}"
  end

  def self.foo(x)
    @ofoo.call(x)
  end

  def titi(y) 
    MotherFoo.foo(y) 
  end
end

class ChildFoo < MotherFoo
  # puts @ofoo.call # generates an exception: undefined method `call' for nil:NilClass (NoMethodError))
  def foo(z) 
    "child foo #{z}"
  end
  puts MotherFoo.foo(9)
end

c = ChildFoo.new
puts c.titi(4) 
puts c.foo(3)
Como se vé, es todavía posible - pero ahora explicitándolo con MotherFoo.foo - acceder al método "privado" evitando de este modo la colisión casual.

Ejecución:

[11:13][~/Dropbox/src/ruby/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules]$ ruby overriding_private_methods3.rb 
mother foo 9
mother foo 4
child foo 3

Casiano Rodriguez León 2015-06-18