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 = 57se 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ñosVISIBILIDAD 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.