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