Clases y Módulos

P: ¿Puede repetirse la definición de una clase?
P: ¿Existen variables de clase?
P: ¿Qué es una variable de instancia de clase?
P: ¿Qué es un método singleton (solitario)?
P: ¿Tiene Ruby métodos de clase?
P: ¿Qué es una clase singleton?
P: ¿Qué es una función de módulo?
P: ¿Cuál es la diferencia entre una clase y un módulo?
P: ¿Se pueden hacer subclases de mñodulos?
P: Y un ejemplo de mix-in
P: ¿Por qué existen dos formas de definir los métodos de clase?
P: ¿Cuál es la diferencia entre load y require?
P: ¿Cuál es la diferencia entre include y extend?
P: ¿Quésignifica self?
P: ¿Por qué no puedo cargar variables desde un fichero a parte?

P: ¿Puede repetirse la definición de una clase?

R: Una clase se puede definir repetidamente. Cada definiicón se añade a la anterior. Si se redefine un método, el anterior es anulado y se pierde.

P: ¿Existen variables de clase?

R: Desde Ruby 1.5.3, si. Una variable precedida con dos arrobas es una variable de clase, accesible desde los métodos de instancia y clase.

	    
	    class CountEm
	      @@children = 0
	      def initialize
	        @@children += 1
		@myNumber = @@children
	      end

	      def whoAmI
	        "Soy el hijo número #@myNumber (de #@@children)"
	      end

	      def CountEm.totalChildren
	        @@children
	      end
	    end
	    
	    c1 = CountEm.new
	    c2 = CountEm.new
	    c3 = CountEm.new
	    c1.whoAmI                # -> "Soy el hijo número 1 (de 3)"
	    c3.whoAmI                # -> "Soy el hiji número 3 (de 3)"
	    CountEm.totalChildren    # -> 3
	  
Versiones anteriores de Ruby no tienen variables de clase. Sin embargo es posible utilizar clases contenedoras (Array, Hash, .. ) asignadas a una constante de clase para obtener el mismo efecto. Este ejemplo utiliza un array. Algunas personas se sienten más a gusto con hashes.
	    
	    class Foo
	      F = [0]                # -> pseudo variable de clase - array F
	      def foo
	        F[0] += 1
		puts F[0]
	      end
	    end
	  
La constante mantiene el número de veces que se ha llamado a foo para todas las instancias de la clase Foo.

P: ¿Qué es una variable de instancia de clase?

R:

	    
	    class Foo
	      @a = 123            # (1)
	      def foo
	        p @a              # (2) ... nil not 123
	      end
	    end
	  
(1) es una variable de instancia de clase y (2) es una variable de instancia normal (La cual no ha sido inicializada y tiene el valor nil). (2) pertenece a una instancia de la clase Foo. y (1) pertenece al objeto clase Foo, que es una instancia de la clase Class.

P: ¿Qué es un método singleton (solitario)?

R: Un método singleton es una instancia de un método asignado a un objeto determinado.

Se crean incluyendo el objeto en la definición:

	    
	    class  Foo
	    end

	    foo = Foo.new
	    bar = Foo.new
	    def foo.hello
	      puts "Hola"
	    end
	    foo.hello
	    bar.hello
	  
Produce:
	    Hola
	    prog.rb:10 undefined method 'hello' for #<Foo:0x4018d748> (NameError)
	  

Estos métodos son útiles si se quiere añadir un método a un objeto y no es adecuada la creación de una nueva clase.

P: ¿Tiene Ruby métodos de clase?

R: Un método singleton de una objeto clase es un método de clase. (Realmente, el método de clase se define en la metaclase, pero esto es transparente). Otra forma de considerar esto es decir que un método de clase es un método cuyo receptor es una clase.

Todo ello se reduce al hecho de que se puede llamar a los métodos de clase sin tener instancias de esa clase (objetos) como receptor.

Vamos a crear un método singleton para la clase Foo:

	    
	    class Foo
	      def Foo.test
	        "Este es Foo"
	      end
	    end
	    
	    # Que se le puede llamar de la siguiente forma.
	    
	    Foo.test                 # -> "Este es Foo"
	  
En el ejemplo Foo.test es un método de clase.

Los métodos definicos en la clase Class pueden utilizarse como métodos de clase de cualquier clase (!).

P: ¿Qué es una clase singleton?

R: Una clase singleton es una clase anónima que se crea mediante una subclase de la clase asociada a un objeto en concreto. Es otra forma de ampliar la funcionalidad asociada a un único objeto.

Retomemos la humilde Foo:


	    class Foo
	      def hello
	        print "hola"
	      end
	    end

	    foo = Foo.new
	    foo.hello
	  

Supongamos que se necesita añadir funcionalidad a nivel de clase para esta única instancia:

	    
	    class << foo
	      attr :name, TRUE
	      def hello
	        "hola, soy " + @name + "\n"
	      end
	    end

	    foo.name = "Tomás";
	    foo.hello           # -> "hola, soy Tomás"
	  
Se ha adaptado foo sin modificar las características de Foo.

P: ¿Qué es una función de módulo?

R: Una función de módulo es un método singleton privado definido en un módulo. En efecto, es similar a un método de clase (sección 8.5 anterior) que se puede llamar utilizando la notación Modulo.metodo:

	    
	    Math.sqrt(2)             # -> 1.414213562
	  
Sin embargo, debido a que los módulos se pueden "mezclar" con las clases, estas funciones se pueden utilizar también sin el prefijo (así es como se ponen a disposición de los objetos todas las funciones del Kernel):
	    
	    include Math
	    sqrt(2)                  # -> 1.414213562
	  
Para hacer un método una función de módulo hay que utilizar module_function.
	    
	    Module Test
	      def thing
	        # ...
	      end
	      module_function :thing
	    end
	  

P: ¿Cuál es la diferencia entre una clase y un módulo?

R: Los módulos son colecciones de métodos y contantes. No pueden generar instancias. Las clases pueden generar instancias (objetos) y cada instancia tiene un estado (las variables de instancia).

Los modulos se pueden mezclar en las clases y otros módulos. Los métodos y constantes del módulo que se mezcla se añaden a la clase, aumentando su funcionalidad. Sin embargo, las clases no se pueden mezclar con nada.

Una clase puede heredar de otra pero no de un módulo.

Un módulo no puede heredar.

P: ¿Se pueden hacer subclases de mñodulos?

R: No, Sin embargo se puede incluir el módulo en una clase u otro mñodulo para simular una herencia múltiple (utilidad mixin )

Esto no crea una subclase (que requeriría herencia) pero genera una relación is_a? entre la clase y el módulo.

P: Y un ejemplo de mix-in

R: El módulo Comparable proporciona diversos operadores de comparación (<,<=,>,between? etc ...). Los define en términos de llamadas al método general de comparación <=>. Sin embargo, él mismo no define <=>.

Digamos que se desea crear una clase donde las comparaciones se toman en base al número de patas que tiene un animal:

	    
	    class MyClass
	      include Comparable
	      attr :legs
	      def initialize(name, legs)
	        @name, @legs = name, legs
	      end
	      def <=>(o)
	        return @legs <=> o.legs
	      end
	    end
	    
	    c = MyClass.new('gato',4)
	    s = MyClass.new('serpiente',0)
	    p = myClass.new('loro',2)

	    c < s            # -> false
	    s < c            # -> true
	    p >= s           # -> true
	    p.between?(s,c)  # -> true
	    [p, s, c].sort   # -> [serpiente, loro, gato]
	  
Todo lo que debe hacer MyClass es definir la semántica del operador <=> y mezclarse con el módulo Comparable. Los métodos de este módulo son ahora indistiguibles de los de la clase y así ésta adquiere de repente nuevas funcionalidades. Y debido a que el mismo módulo Comparable se utiliza en muchas clases, la nueva clase comparte una semántica consistente y bien conocida.

P: ¿Por qué existen dos formas de definir los métodos de clase?

R: Se pueden definir en la definición de la clase y en el nivel superior.

	    
	    class Demo
	      def Demo.classMethod
	      end
	    end

	    def Demo.anotherClassMethod
	    end
	  
Sólo hay una diferencia significativa entre las dos. En la definición de la clase se puede acceder directamente a las contantes de la clase, ya que se encuentran accesibles por el ámbito. En el nivel superior, hay que utilizar la notación Class::CONST.

P: ¿Cuál es la diferencia entre load y require?

R: load carga y ejecuta un programa Ruby (*.rb).

require también carga un programa Ruby pero también módulos de extensión binarios de Ruby (Librerías compartidas o DLLs). Además require garantiza que un elemento nunca se carga más de una vez.

P: ¿Cuál es la diferencia entre include y extend?

R: include mezcla un módulo con una clase u otro módulo. Los métodos del módulo se llaman utilizando el estilo de llamadas a funciones (sin receptor).

extend se utiliza para incluir un módulo en un objeto (instancia). Los métodos del módulo pasan a ser métodos del objeto.

P: ¿Quésignifica self?

R: self el receptor que se está en ejecución en ese momento - el objeto al cual se aplica el método. Una llamada a un método como una función implica la existencia de self como receptor.

P: ¿Por qué no puedo cargar variables desde un fichero a parte?

R: Digamos que el fichero file1contiene:

	    
	    var1 = 99
	  
y que en algún otro fichero se tiene:
	    
	    require 'file1'
	    puts var1
	  
Produce:
	    
	    prog.rb:2: undefined local variable or method 'var1' for #<Object:0x4019ac90> (NameError) 
	  
Se genera un error porque load y require almacenan las variables locales en espacios de nombre separados y anónimos, descartándolos. Este diseño protege de la posibilidad de corromper el código.