viernes, 25 de abril de 2014

Clases y objetos en Ruby (III)

En los anteriores artículos sobre Clases y objetos en Ruby, hemos abordado los fundamentos acerca de la creación de clases, el acceso a las propiedades de un objeto y la visibilidad de las variables. En este artículo aprenderemos a aprovechar la herencia de clases y el control de acceso o visibilidad de los métodos de una clase.

HERENCIA

La herencia es una funcionalidad del paradigma de la programación orientada a objetos. Esta funcionalidad permite reutilizar el código, con el consiguiente ahorro de tiempos, esfuerzos y costes.

Gracias a la herencia podemos crear nuevas clases que hereden propiedades y métodos de otras clases. La clase de la cual se hereda se suele denominar clase padre o superclase, y la clase que hereda se suele denominar clase hija, clase especializada o subclase.

Para entenderlo mejor, aprovecharemos el ejemplo de la clase Humano. Esta clase es muy genérica y contiene las propiedades y métodos comunes a todo ser humano. Podríamos querer especializar esta clase en una nueva clase llamada Profesional, con la cual vamos a tratar un ser humano trabajador. Esta nueva clase heredará todas las propiedades de Humano, añadiendo nuevas propiedades y métodos especializados de un trabajador.

Para que una clase herede de otra ha de utilizarse la siguiente sintaxis:

class subclase < superclase
...
end

La clase Profesional quedaría así:

load "Humano.rb"

class Profesional < Humano

   def initialize(nombre, apellidos, edad, altura, peso, sexo, profesion, especialidad, experiencia)
     super(nombre, apellidos, edad, altura, peso, sexo)
     @profesion = profesion
     @especialidad = especialidad
     @experiencia = experiencia
   end

   def to_s
     super + " > Profesion: #{@profesion} - Especialidad: #{@especialidad} - Experiencia: #{@experiencia} años"
   end
end

un_profesor = Profesional.new("Andres", "Garcia Tomillar", 56, 1.82, 97.5, "hombre", "Profesor", "Literatura", 27)
puts un_profesor.to_s
un_profesor.edad = 57
puts un_profesor.to_s

La primera línea:

load "Humano.rb"

Permite cargar la clase Humano para poder ser utilizada por la subclase Profesional. Esto se realiza en el caso de que la superclase resida en otro archivo distinto al de la subclase.

En la declaración de la clase estamos indicando que la clase Profesional es una subclase de la superclase Humano.

Después, inicializamos las propiedades de esta nueva clase, por lo que hay que inicializar las propiedades de Humano (superclase) y de la propia clase Profesional (subclase).

El método initialize tiene los mismos parámetros que el constructor de la superclase, más sus parámetros propios o especializados.

La sentencia super invoca al mismo método de la superclase. En el caso del método initialize, invoca al método initialize de la clase Humano, pasando los parámetros correspondientes. En el caso del método to_s, la llamada al método retorna la cadena devuelta por método to_s de la superclase, y le concatena otra cadena con el resultado de las propiedades de la subclase, y la nueva cadena es retornada.

En la línea

un_profesor.edad = 57

se invoca al método heredado de la superclase, asignando el valor a esta propiedad heredada.

El resultado final será el siguiente:

Andres Garcia Tomillar, hombre, de 56 años, de 97.5 kg y 1.82 m > Profesion: Profesor - Especialidad: Literatura - Experiencia: 27 años
Andres Garcia Tomillar, hombre, de 57 años, de 97.5 kg y 1.82 m > Profesion: Profesor - Especialidad: Literatura - Experiencia: 27 años


VISIBILIDAD DE LOS METODOS

Al tratar una compleja aplicación con objetos, es necesario tener un control de acceso a los métodos. En artículos anteriores aprendimos la visibilidad el control de acceso a las propiedades. Ahora le toca el turno a los métodos.

ACCESO PUBLICO

Los métodos, por defecto, son declarados con una visibilidad pública. Es decir, que cualquiera puede acceder a los mismos.

La única excepción sería el método initialize, el cual tiene siempre un acceso privado.

ACCESO PROTEGIDO

Un método de acceso protegido puede ser invocado únicamente por la clase que lo define y sus subclases. Puede decirse que es un método estrictamente “familiar”, porque permanece exclusivamente dentro de su familia.

ACCESO PRIVADO

Un método de acceso privado es accesible únicamente por la clase que lo define y sus subclases directas (primer nivel de jerarquía), dentro de ese mismo objeto.

Un método privado sólo puede ser invocado dentro del contexto del objeto de llamada. No se puede acceder directamente a los métodos privados de otro objeto.

DEFINICION DE LA VISIBILIDAD

Para definir si un método tiene una determinada visibilidad, es necesario escribir el método dentro de un bloque de código mediante la palabra clave public (opcional), protected o private.

La estructura quedaría de la siguiente manera:

class Clase

   # bloque para metodos publicos
   # (la palabra public es opcional)
public

   def metodos_publicos
     ...
   end

   # bloque para metodos protegidos
protected

   def metodos_protegidos
     ...
   end

   # bloque para metodos privados
private

   def metodos_privados
     ...
   end

end

Otra forma más simple de organizar y definir el control de acceso a los métodos, es definiendo todos los métodos de forma normal, y, al final, definir qué control de acceso poseen los distintos métodos. La estructura quedaría de la siguiente manera:

class Clase

   def metodo1
     ...
   end

   def metodo2
     ...
   end

   def metodo3
     ...
   end

   def metodo4
     ...
   end

   public :metodo1, metodo3
   protected :metodo2
   private :metodo4
end

Esta estructura es una simplificación de la estructura anterior. El precompilador de Ruby organizará automáticamente el acceso como se ha visto en la primera estructura.

EJEMPLO DE CONTROL DE ACCESO

El siguiente ejemplo ilustra el funcionamiento del control de acceso a los métodos.

class Clase1

   def initialize
     @var = 1
   end

   def var
     @var
   end

   def incrementar
     @var = @var + 1
   end

   def incrementar_por(incremento)
     @var = var + incremento
   end

   def decrementar
     @var = @var - 1
   end

   public :var, :incrementar
   protected :incrementar_por
   private :decrementar

end

class Clase2 < Clase1

   def initialize
     super
     @var = var
   end

   def procesar
     @var = incrementar
     @var = incrementar_por(10)
     @var = decrementar
     @var #retorna el resultado
   end
end

class Clase3 < Clase2

   def initialize
     @var = 10
     @objeto = Clase2.new
   end

   # Este metodo accede al metodo heredado de
   # la instancia de Clase2
   def incrementar
     @objeto.incrementar
   end

   # Este metodo accede al metodo heredado de
   # la instancia de Clase2
   def incrementar_por(incremento)
     @objeto.incrementar_por(incremento)
   end

   # Este metodo accede al metodo heredado de
   # la instancia de Clase2
   # Este metodo dara un error, pues se intenta
   # acceder a un metodo privado de Clase1
   # directamente a traves de un objeto de Clase2
  # def decrementar
    # @objeto.decrementar
  # end

   # Este metodo accede a los metodos heredados
   # directamente de Clase1
   def procesar
     @var = incrementar
     @var = incrementar_por(10)
     @var = decrementar
     @var #retorna el resultado
   end
end

ejemplo = Clase2.new
puts ejemplo.procesar # Retorna 11
puts ejemplo.var # Acceso a metodo publico
# ejemplo.incrementar_por(3) # Dara un error de acceso
# ejemplo.decrementar # Dara un error de acceso

ejemplo2 = Clase3.new
puts ejemplo2.incrementar # Retorna 2
puts ejemplo2.incrementar_por(5) # Retorna 7
# puts ejemplo2.decrementar # Dara un error de acceso
puts ejemplo2.procesar #Retorna 17

Se han creado tres clases. Clase1 es la superclase. Clase2 es subclase directa de Clase1. Clase3 es subclase directa de Clase2, pero de segundo nivel con respecto a Clase1.

En Clase1 se han definido varios métodos con distinto control de acceso.

En la ejecución se crea una instancia de Clase2. Dentro de esta clase hay un método procesar, el cual accede, desde dentro de la clase, a todos los métodos heredados de Clase1, pues tiene visibilidad a sus métodos públicos, protegidos y privados.

Si intentamos acceder directamente a los métodos heredados de Clase1, a través de una instancia de Clase2, no tendremos acceso a los métodos protegidos ni privados. Éstos son sólo accesibles desde el interior de la propia Clase2.

La Clase3 hereda directamente de Clase2. Además, define una propiedad @obj que es una también una instancia de Clase2, a fin de poder comprobar el control de acceso mediante herencia y mediante instancia a esta clase. En el caso de instancia, no tendremos acceso al método privado heredado de Clase1.




TE PUEDE INTERESAR