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

jueves, 24 de abril de 2014

Clases y objetos en Ruby (II)

En el primer artículo sobre Clases y Objetos en Ruby, hablamos sobre las propiedades de los objetos y cómo se accedía a dichas propiedades para consultar o modificar sus valores. A modo de recordatorio, comentábamos que las propiedades de un objeto estaban gestionadas por unas variables con visibilidad de instancia, es decir que sólo podían ser accedidas desde la propia clase, y no directamente desde fuera de ella. Para acceder a estas variables había que crear e invocar a ciertos métodos para acceso de lectura (consulta) o de escritura (modificación).

En este artículo simplificaremos aún más la forma de acceder a las propiedades, y veremos en detalle la visibilidad de las variables en una clase.

SIMPLIFICANDO EL ACCESO A LAS PROPIEDADES

Recordemos el código de nuestra clase Humano:

class Humano

   def initialize(nombre, apellidos, edad, altura, peso, sexo)
     @nombre = nombre
     @apellidos = apellidos
     @edad = edad
     @altura = altura
     @peso = peso
     @sexo = sexo
   end

   def to_s
     "#{@nombre} #{@apellidos}, #{@sexo}, de #{@edad} años, de #{@peso} kg y #{@altura} m"
   end

   def nombre
     @nombre
   end

   def apellidos
     @apellidos
   end

   def edad
     @edad
   end

   def altura
     @altura
   end

   def peso
     @peso
   end

   def sexo
     @sexo
   end

   def nombre=(nombre)
     @nombre = nombre
   end

   def apellidos=(apellidos)
     @apellidos = apellidos
   end

   def edad=(edad)
     @edad = edad
   end

   def altura=(altura)
     @altura = altura
   end

   def peso=(peso)
     @peso = peso
   end

   def sexo=(sexo)
     @sexo = sexo
   end

end

un_humano = Humano.new("Adela","Sanchez Gomez", "27", 1.67, 55.7, "mujer")
otro_humano = Humano.new("Javier", "Martin Lopez", "44", 1.74, 80.5, "hombre")
puts "Valor de un_humano: " + un_humano.to_s
puts "Valor de otro_humano: " + otro_humano.to_s
un_humano.peso = 71
un_humano.edad = 32
puts un_humano.nombre + " " + un_humano.apellidos + " tiene #{un_humano.edad} años de edad y #{un_humano.peso} kg de peso"


Para consultar el valor de una propiedad utilizábamos un método que tenía el mismo nombre que la propiedad, retornando el valor de la propiedad, representado por una variable de instancia:

def nombre
   @nombre
end

Ruby permite simplificar toda esta sintaxis, mediante la siguiente abreviatura:

attr_reader :propiedad1, :propiedad2, ...

A la hora de ejecutar la aplicación, el pre-compilador de Ruby generará, automáticamente, los métodos de acceso de lectura correspondientes.

Para modificar el valor de una propiedad utilizábamos un método que tenía el mismo nombre que la propiedad más el signo igual, asignando a la propiedad el valor pasado por parámetro:

def nombre=(nombre)
   @nombre = nombre
end

Ruby permite simplificar toda esta sintaxis, mediante la siguiente abreviatura:

attr_writer :propiedad1, :propiedad2, ...

A la hora de ejecutar la aplicación, el pre-compilador de Ruby generará, automáticamente, los métodos de acceso de escritura correspondientes.

Así pues, el código final de la aplicacion se reduciría drásticamente:

class Humano
   attr_reader :nombre, :apellidos, :edad, :altura, :peso, :sexo
   attr_writer :nombre, :apellidos, :edad, :altura, :peso, :sexo

   def initialize(nombre, apellidos, edad, altura, peso, sexo)
     @nombre = nombre
     @apellidos = apellidos
     @edad = edad
     @altura = altura
     @peso = peso
     @sexo = sexo
   end

   def to_s
     "#{@nombre} #{@apellidos}, #{@sexo}, de #{@edad} años, de #{@peso} kg y #{@altura} m"
   end

end

un_humano = Humano.new("Adela","Sanchez Gomez", "27", 1.67, 55.7, "mujer")
otro_humano = Humano.new("Javier", "Martin Lopez", "44", 1.74, 80.5, "hombre")
puts "Valor de un_humano: " + un_humano.to_s
puts "Valor de otro_humano: " + otro_humano.to_s
un_humano.peso = 71
un_humano.edad = 32
puts un_humano.nombre + " " + un_humano.apellidos + " tiene #{un_humano.edad} años de edad y #{un_humano.peso} kg de peso"


VISIBILIDAD DE LAS VARIABLES

VARIABLES DE METODO

Las variables definidas en un método, ya sea como parámetro o como parte del cuerpo del propio método, solamente tienen visibilidad dentro del propio método. Es decir, se crean en el método y se destruyen cuando termina la ejecución del método.

def metodo(nombre)
   mi_nombre = nombre
end

Las variables de método tienen su propia instancia en memoria, por lo que, aunque exista una variable de visibilidad superior con idéntico nombre, éstas se asumen como diferentes:

def metodo(nombre)
   @nombre = nombre
end

En este caso, se asigna a la variable de instancia @nombre el valor de la variable de método nombre pasada por parámetro. Cuando termina la ejecución del método, la variable nombre se destruye, pero la variable @nombre sigue vigente con el valor asignado.



VARIABLES DE INSTANCIA

Las variables de instancia son variables con una visibilidad para toda la clase, es decir, que puede ser utilizada por cualquier método de la misma mientras el objeto instanciado esté vivo. Estas variables son privadas a la clase, y no pueden ser accedidas fuera de ella, a no ser que se utilicen métodos que accedan de forma explícita a los mismos.

Por ejemplo:

class Clase
   def initialize
     @var=12
   end
end

miClase = Clase.new()
puts "Valor: #{miClase.var}"

Esto generará el siguiente error:

Clase.rb:24:in '<main>': undefined method 'var' for #<Clase:0x0000000318de38 @var=12> (NoMethodError)

Tampoco permitirá acceder mediante la siguiente expresión:

puts "Valor: #{miClase.@var}"

Generará el siguiente error:

Clase.rb:24: syntax error, unexpected tIVAR, expecting '('puts "Valor: #{miClase.@var}"

Ello se debe a que al escribir el nombre del objeto y un punto, lo siguiente que espera es un método, no una variable.

Como vimos en el artículo anterior, el acceso de lectura y escritura se realizará mediante dos métodos que tienen el mismo nombre que la propiedad:

class Clase
   def initialize
     @var=12
   end

   # Metodo de consulta a propiedad
   def var
     @var
   end

   # Metodo de asignacion a propiedad
   def var=(var)
     @var=var
   end
end

miClase = Clase.new()
puts "Valor inicial: #{miClase.var}"
# Llama al metodo de asignacion a propiedad
miClase.var = 24
# Llama al metodo de consulta a propiedad
puts "Valor cambiado: #{miClase.var}"

El resultado es el siguiente:

Valor inicial: 12
Valor cambiado: 24

Podemos utilizar otros métodos para acceder a las variables de instancia de otras maneras:

class Clase
   def initialize
     @var=12
   end

   def incrementar
     @var = @var+1
   end

   def incrementar_por(incremento)
     # incremento es variable de metodo
     @var = @var+incremento
   end

   # Metodo de consulta a propiedad
   def var
     @var
   end

   # Metodo de asignacion a propiedad
   def var=(var)
     # Asignacion de variable de metodo a variable de instancia
     @var=var
   end
end

miClase = Clase.new()
puts "Valor inicial: #{miClase.var}"
# Llama al metodo de asignacion a propiedad
miClase.var = 24
# Llama al metodo de consulta a propiedad
puts "Valor cambiado: #{miClase.var}"
# Llama al metodo incrementar dos veces
miClase.incrementar
miClase.incrementar
puts "Valor incrementado: #{miClase.var}"
# Llama al metodo incrementar_por
# incremento de 10
miClase.incrementar_por(10)
puts "Valor incrementado por 10: #{miClase.var}"

El método incrementar suma 1 al valor actual de la variable de instancia.

El método incrementar_por suma el valor pasado por parámetro al valor actual de la variable de instancia.

El resultado será el siguiente

Valor inicial: 12
Valor cambiado: 24
Valor incrementado: 26
Valor incrementado por 10: 36



TE PUEDE INTERESAR

Clases y objetos en Ruby (I)

Este es el primero de una serie de artículos que revisa en profundidad el paradigma de la programación orientada a objetos mediante Ruby. Estos artículos no pretenden ser una clase magistral de este paradigma, pero sus ejemplos ayudan a entender claramente tanto sus principios como su aplicación en Ruby.

En este primer artículos aprenderemos conceptos básicos, tales como clase, objeto, propiedad y método. También aprenderemos cómo podemos consultar y modificar el valor de una propiedad.

INTRODUCCION

En un lenguaje de programación orientado a objetos, toma como concepto primordial el hecho de que todo cuanto existe se interpreta y se ve como un objeto, el cual puede tener unas propiedades o atributos, así como también unos métodos o acciones.

Todo comienza por una clase, que es una definición genérica, un molde o una abstracción de un tipo de objeto. A partir de la clase se generarán instancias u objetos, que son las entidades físicas de la clase.

EJEMPLO DE CLASE Y OBJETO

Imaginemos a un ser humano. La definición genérica de un ser humano puede tener una serie de propiedades comunes, como un nombre, unos apellidos, un sexo, una edad, una altura y un peso.

Un ser humano puede desempeñar diferentes acciones, tales como respirar, mirar, escuchar, andar, nadar, etc.

Yo, tú, él, ella, aquel, aquella... somos objetos o entidades físicas de un ser humano. Como entidades físicas, somos realidades y no abstracciones. Tenemos las propiedades comunes de un ser humano, pero los valores de nuestras propiedades son únicos e inherentes a cada uno.

Para llevar a cabo este ejemplo, crearemos el siguiente ejemplo en un fichero llamado Humano.rb:

class Humano
   def initialize
     @nombre = "Anonimo"
     @apellidos = ""
     @edad = 33
     @altura = 1.75
     @peso = 73.5
     @sexo = "hombre"
   end

   def to_s
     "#{@nombre} #{@apellidos}, #{@sexo}, de #{@edad} años, de #{@peso} kg y #{@altura} m"
   end
end

un_humano = Humano.new()
puts un_humano.to_s

Una clase en Ruby comienza con la declaración class, seguida del nombre de la clase. Al ser una definición genérica, su nombre empieza con mayúsculas.

Esta clase Humano tiene dos métodos o acciones: initialize y to_s

El método initialize es un método especial común a todas las clases. Es un método constructor, es decir, que cuando creemos una instancia o un objeto de esta clase, éste se invocará automáticamente en primer lugar para inicializar el objeto. Este método se suele utilizar para inicializar los valores de las propiedades del objeto y/o lanzar algún método de arranque. En este ejemplo, define seis variables de instancia, las cuales comienzan por el símbolo @, y representan cada una de las propiedades o atributos. Este tipo de variables sólo tienen visibilidad dentro del objeto, asegurando así la seguridad de los datos.

El método to_s es el símil del método toString de otros lenguajes de programación. Este método se utiliza para que nos devuelva una cadena de texto, que suele ser la información más relevante del objeto. En nuestro caso, retornará un texto con los valores de cada una de las propiedades del objeto. Este método es opcional.

Fuera de la definición de la clase, se han introducido dos líneas que se ejecutarán al lanzar este programa desde el intérprete de Ruby.

La primera línea crea una variable a la que se le asigna una nueva instancia de objeto. Con el método new, Ruby crea una nueva instancia de la clase Humano.

La segunda línea visualiza en pantalla la cadena retornada por el método to_s

Para ejecutar esta aplicación, ejecutaremos la siguiente línea desde la consola de comandos del sistema operativo:

ruby Humano.rb
Anonimo , hombre, de 33 años, de 73.5 kg y 1.75 m


INICIALIZANDO PROPIEDADES

En el ejemplo anterior estamos limitados, pues podemos crear múltiples objetos o instancias, pero todos tendrán los mismos valores en sus propiedades. Es decir, crearemos clones de humanos, donde, en su esencia, son objetos distintos, pero que en su apariencia son idénticos.

Para resolver esto, modificaremos el método constructor, permitiendo definir estas propiedades al mismo tiempo que se crea el objeto:

class Humano

   def initialize(nombre, apellidos, edad, altura, peso, sexo)
     @nombre = nombre
     @apellidos = apellidos
     @edad = edad
     @altura = altura
     @peso = peso
     @sexo = sexo
   end

   def to_s
     "#{@nombre} #{@apellidos}, #{@sexo}, de #{@edad} años, de #{@peso} kg y #{@altura} m"
   end

end

un_humano = Humano.new("Adela","Sanchez Gomez", "27", 1.67, 55.7, "mujer")
otro_humano = Humano.new("Javier", "Martin Lopez", "44", 1.74, 80.5, "hombre")
puts "Valor de un_humano: " + un_humano.to_s
puts "Valor de otro_humano: " + otro_humano.to_s

El método constructor tiene una lista de parámetros, los cuales son variables de ámbito del método (fuera de ella no tienen sentido). Cada una de estas variables recogerá un valor, y dicho valor se asignará, dentro del método, a una variable de ámbito de instancia. Es importante entender que una variable de método sólo tiene visibilidad dentro de un método, y que una variable de instancia tiene visibilidad en toda la clase. Aunque las variables tengan el mismo nombre, su ámbito y entidad son diferentes. Así:

@nombre = nombre

Asigna a la variable de instancia @nombre, el valor de la variable de método nombre.

El resultado será que, cada vez que se crea un objeto, se inicializará dicho objeto con los valores que pasemos como parámetros en el método new

La aplicación arrancará en la primera línea inmediatamente después de la definición de la clase. Se crearán dos objetos (un_humano y otro_humano), cada uno con unas propiedades o atributos concretos. A continuación, se visualizará los valores de dichas propiedades.

Valor de un_humano: Adela Sanchez Gomez, mujer, de 27 años, de 55.7 kg y 1.67 m
Valor de otro_humano: Javier Martin Lopez, hombre, de 44 años, de 80.5 kg y 1.74 m


ACCEDIENDO AL VALOR DE UNA PROPIEDAD

El ejemplo ha ido mejorando, pero tiene el inconveniente de que nos muestra todos los valores en el orden y el formato que el método to_s ha definido. Si queremos acceder libremente a una determinada propiedad, para cualquier propósito, hemos de definir métodos específicos dentro de la clase, con el mismo nombre que la propiedad:

def nombre
   @nombre
end

def apellidos
   @apellidos
end

def edad
   @edad
end

def altura
   @altura
end

def peso
   @peso
end

def sexo
   @sexo
end

Cada uno de los métodos retorna el valor de la propiedad que tiene su mismo nombre.

De esta manera, podemos consultar el valor de cada una de las propiedades del objeto de la siguiente manera:

nombre_objeto.nombre_propiedad

Como en la siguiente línea:

puts un_humano.nombre + " " + un_humano.apellidos + " tiene " + un_humano.edad + " años de edad"
Adela Sanchez Gomez tiene 27 años de edad


CAMBIANDO EL VALOR DE UNA PROPIEDAD

Nuestro humano va mejorando. Podemos conocer el valor de cada una de sus propiedades en cualquier momento. Sin embargo, un objeto no tiene por qué ser estático de por vida, si no que puede cambiar el valor de sus propiedades en cualquier momento. No hemos de matar a nuestro ser humano para crear un clon con los nuevos valores de sus propiedades.

La propiedad más proclive a cambiar sería su edad, su peso o su altura. Pero también es posible que quiera cambiar su nombre, sus apellidos y (¿por qué no?) su sexo.

La forma de poder asignar valores a una propiedad concreta es definiendo un método que tenga el mismo nombre de la variable más el signo = (igual), seguido de un parámetro que capture el valor y se lo asigne a la propiedad:

def nombre=(nombre)
   @nombre = nombre
end

def apellidos=(apellidos)
   @apellidos = apellidos
end

def edad=(edad)
   @edad = edad
end

def altura=(altura)
   @altura = altura
end

def peso=(peso)
   @peso = peso
end

def sexo=(sexo)
   @sexo = sexo
end

Cada uno de los métodos asigna a la propiedad con su mismo nombre, el valor pasado como parámetro.

De esta manera, podemos asignar el valor a cada una de las propiedades de la siguiente manera:

nombre_objeto.nombre_propiedad = nuevo valor

Como en las siguientes líneas:

un_humano.peso = 71
un_humano.edad = 32

Aquí podemos ver el resultado:

puts un_humano.nombre + " " + un_humano.apellidos + " tiene #{un_humano.edad} años de edad y #{un_humano.peso} kg de peso"
Adela Sanchez Gomez tiene 32 años de edad y 71 kg de peso


ENTENDIENDO EL ACCESO A LAS PROPIEDADES

En Ruby, las propiedades de un objeto son exclusivamente variables de instancia. Su visibilidad es privada y se accede estrictamente desde la propia clase. Esto asegura el encapsulamiento de éstas, así como también la seguridad de los datos.

En otros lenguajes de programación, resulta muy cómodo utilizar definir una clase de la siguiente manera:

/tr>
public class MiClase {
   public valor1 as Integer;
   public valor2 as String;
   ...
}

e instanciar la clase en objetos con acceso público a sus propiedades:

Set miObjeto As new MiClase;
miObjeto.valor1=1;
miObjeto.valor2="Valor"

Ruby contempla esta forma de gestionar los datos, gracias a los hashes o diccionarios, con mayor funcionalidad si cabe.

El problema de utilizar las clases de esta manera es la seguridad de la información, y el uso incontrolado o indebido por otras clases de una aplicación compleja. Para evitar ésto, las propiedades se definen como privadas a la clase (variables de instancia). De esta manera, su acceso está controlado de forma exclusiva por la propia clase, y para poder consultar o modificar el valor de una determinada propiedad, es necesario el uso de un método específico.

En el caso de Ruby, el uso de estos métodos ofrece una sintáxis muy similar a como si accediéramos de forma pública, lo que facilita mucho su semántica. Sin embargo, podemos hacer uso de una técnica llamada getters y setters, tal y como se realiza en otros lenguajes de programación.

Esta técnica consiste en definir un método get (obtener) y un método set (establecer) para el acceso a la propiedad. Por ejemplo, para acceder a la propiedad nombre, se utilizarían estos dos métodos:

def getNombre()
   @nombre
end

def setNombre(nombre)
   @nombre = nombre
end

A la hora de utilizar el objeto, lo realizaríamos de la siguiente manera:

objeto.setNombre(“Felipe”)
mi_nombre = objeto.getNombre()

El resultado es el mismo a como hemos visto anteriormente, aunque el nombre del método no es el mismo que el de la propiedad. La técnica vista anteriormente es más sencilla e intuitiva.




TE PUEDE INTERESAR