29 ene. 2020

Los Módulos en Ruby (herencias cruzadas y mixin)

Ruby en línea: https://paiza.io/projects/3P-5xOa5TfPscvm1Npj98g?language=ruby
https://www.youtube.com/watch?v=9nZGCEX2bi4
==================================================================

En Ruby todo es un objeto, y los módulos son una clase especial que no puede instanciarse a sí misma, es una clase muy limitada que permite que desde fuera de ella se pueda llamar a su contenido.

Este llamamiento puede ser en dos direcciones, por un lado para añadir funcionalidad a las instancias de otras clases o para añadir funcionalidad a otras clases pero no a sus instancias.

Cuando se utiliza para añadir funcionalidad a las instancias, se utiliza la instrucción include que viene a significar, incluye el módulo en esta clase para dotar a las instancias esas características.

Cuando se utiliza con la instrucción extend, lo que se hace es extender con el contenido del módulo las variables de clase, pero no de sus instancias. Cuando se utiliza extend siempre se refiere a su propio objeto self, pues no es utilizable a ninguna de las instancias que se crean desde la clase.

Como su nombre indica, los módulos son construcciones modulares para hacer cosas o para tener a la mano su contenido, estas cosas solo son parámetros que pueden ser llamadas desde otros métodos, clases e instancias.

Pueden contener de todo; constantes, variables, métodos y clases.

Una forma de verlos es como una librería donde dejar una serie de variables, constantes, métodos y/o clases que pueden ser útiles desde fuera del módulo, quizá para no estar repitiendo código más a delante.

Su sintáxis es:

module Nombredelmódulo                        #empieza en mayúscula
.
.
.
end

Pueden estar escritos en el mismo fichero que el resto del programa, para lo cual han de ir antes de aquello que lo invoque y la manera de invocarlo es:

include Nombredelcontenidodelmodulo
y dentro del código n.nombremétododelmódulo
o
extend Nombredelcontenidomodulo

Así:
module Prueba
    puts "Esto es una prueba"
end

include Prueba        # o extend Prueba

Pero, también pueden ser ficheros de ruby aparte a los que se les invoca desde otro fichero ruby, de manera que habrá que utilizar al inicio del programa que lo va a necesitar. La invocación se realiza con la sentencia require_relative "nombredel fichero.rb"

Hay que tener en cuenta que require_relative es una sentencia que indica que el fichero está en el mismo directorio desde donde se está ejecutando el que lo llama, de lo contrario habría que poner su ruta con require sin _relative, así, por ejemplo:

require "c:/nombredefichero.rb"

Veamos poco a poco el tema de acceso a los módulos.

INCLUDE:

utilizamos include cuando queremos incluir su código en el código que lo necesita, amplía los métodos con los de los métodos.

Las instancias llaman a los métodos de los módulos.

h = Instancia.new
h.métododelmódulo

Veamos un ejemplo:

En este caso vamos a añadir una frase a un método, sólo podemos hacerlo mediante la clase self, pues no hay clase definida e include sólo actúa sobre una instancia de una clase, en Ruby siempre hay un Object sobre el que recae todo.

module Prueba
    puts "Esto es una prueba"
end

def Accion
    include Prueba         #también podríamos escribir exten Prueba
end

self
puts self.class

Da la salida:
Esto es una prueba
Object

Como vemos, self es de la clase Objet y es sobre el que recae la acción.

Ahora vamos a mezclar módulos y clases, a esto se le llama mixin.

module Saludar
    puts 'Este es mi saludo'
end

module Despedida
puts 'Ahora me despido'

class Persona

include Saludar
puts 'Hoooooola'
include Despedida
end

Ésto da la salida:

Este es mi saludo   # al entrar en la clase se salta al módulo Saludar y después vuelve a la clase
Ahora me despido   # ejecuta el otro módulo Despedida y regresa a la clase
Hoooooola

Nótese que los módulos que encuentre en la clase los ejecuta antes de continuar con la clase.

Bien, si tenemos varias clases, el método estará disponible a todas ellas siempre que se invoque fuera de ellas, si no es así, sólo será accesible desde la clase en la que esté invocado.

A su vez, dentro de la clase que lo invoca, no puede estar dentro de un método de su clase, siempre fuera de cualquier método entre el class Nombrecalse y su correspondiente end.

Si el método contiene método/s, entonces las instancias de la clase que lo invoque tendrá acceso a ello/s como si fuera suyo.

Veamos un ejemplo:

module Lenguaje
def idioma
puts 'Inglés'
end
end

module Ladrido
def ladrido
puts "Guau Guau Guau"
end
end

class Persona
include Lenguaje
def initialize
end

def nombre
puts "Eres persona con lenguaje "
end
end

class Perro
def initialize
end
include Ladrido
def nombre_perro
puts "soy un Chucho y digo "
end
end

h = Persona.new
h.nombre
h.idioma
p = Perro.new
p.nombre_perro
p.ladrido

la salida es:
Eres persona con lenguaje
Inglés
soy un Chucho y digo
Guau Guau Guau

Ni el p puede invocar el método nombre ni h el método ladrido, daría error.

imaginemos que el módulo Saludar está en un fichero llamado saludar.rb y el módulo Ladrido en otro llamado ladrar.rb, entonces el código quedaría así:

require_relative "saludar,rb"
require_relative  "ladrar.rb"

class Persona
include Lenguaje
def initialize
end

def nombre
puts "Eres persona con lenguaje "
end
end

class Perro
def initialize
end
include Ladrido
def nombre_perro
puts "soy un Chucho y digo "
end
end

h = Persona.new
h.nombre
h.idioma
p = Perro.new
p.nombre_perro
p.ladrido

y daría la misma salida.

Al hecho de introducir un módulo dentro de una clase se llama mixins, como mezcla de cosas.

EXTEND:
Se utiliza cuando se añade el código que lo usa a la clase como métodos de clase.

Como los métodos de los módulos se convierten en métodos de clase se llamarán desde la clase, no desde sus instancias, así:

Nonbredelaclase.nombremétododelmódulo

Ejemplos:

module Terrestre
def circula
puts "Puedo ir por la tierra"
end
end

module Acuatico
def navega
puts "Puedo navegar por el agua"
end
end

class Vehiculo
extend Terrestre
extend Acuatico

def vehiculo
puts "soy un vehículo"
end
end

Vehiculo.circula
Vehiculo.navega

Da la salida:

Puedo ir por la tierra
Puedo navegar por el agua

Como se ve es la clase Vehiculo y no sus instancias quien puede acceder a los métodos de los módulos Terrestre y Acuatico.

Si pusiéramos include, habría que acceder a través de las instancias, así:

module Terrestre
def circula
puts "Puedo ir por la tierra"
end
end

module Acuatico
def navega
puts "Puedo navegar por el agua"
end
end

class Vehiculo
include Terrestre
include Acuatico

def vehiculo
puts "soy un vehículo"
end
end

h = Vehiculo.new
h.circula
h.navega

que dará la salida:

Puedo ir por la tierra
Puedo navegar por el agua

Que se refieren a la instancia h.

Por último, hay otra manera de acceder a los valores de un módulo y es mediante ::

Desde fuera del método, se puede acceder a sus variables, métodos y clases con invocarlas así:

nombredelmodulo::loquequeramos

Veamos un ejemplo.

module Terrestre
def self.circula                              # Hay que poner self porque se refiere a sí mismo
puts "Puedo ir por la tierra"
end
end

module Acuatico
def self.navega                              # Hay que poner self porque se refiere a sí mismo
puts "Puedo navegar por el agua"
end
end

module Constantes
A = 100
BG = 896
end

puts Terrestre::circula
puts Acuatico::navega
puts Constantes::BG

Esto nos da la salida:

Puedo ir por la tierra

Puedo navegar por el agua

896



28 ene. 2020

Las Clases y los Objetos creados por ellas PARTE 3

Para terminar, vamos a ver los tipos de alcances que pueden tener los métodos de una clase.

Pueden ser cuatro:
Público: lo será siempre que no se declare otra cosa e indica que son accesibles por cualquiera que llame al método de la clase, de cualquier objeto de la clase y él mismo (self).

class Persona
def initialize(edad)
@edad = edad
end

def edad
@edad
end

end

Pedro = Persona.new(25)
Juan = Persona.new(64)

puts Pedro.edad
puts Juan.edad

Esto nos da la salida esperada de 25 y 64 respectivamente

también se puede escribir así:

def adios           #este es de visibilidad pública
@adios
end
private :adios

Protegido: Los objetos no pueden acceder al método protegido, sólo son accesibles a los otros objetos de la misma clase o las subclases, es decir, los que heredan de la clase principal de la misma clase, pero nunca por el propio objeto.


class Persona

def initialize(edad)
@edad = edad
end

def edad                           # este es el método protegido, pero si lo declaramos aquí no funciona
@edad
end

def compararedad(op)                                        #op es otro objeto con el que se comparará
if op.edad > edad                                                #el objeto Juan si puedo consultar el dato de Pedro
'La edad de la otra persona es mayor'
else
'La edad de la otra persona es la misma o menos'
end

end
protected :edad                                                     #OJO aquí si funciona porque ya ha sido leído
end
Pedro = Persona.new(25)
Juan = Persona.new(64)


puts Pedro.compararedad(Juan)
puts Juan.edad

Esto dará la salida:
La edad de la otra persona es mayor
prueba1.rb:27:in `<main>': protected method `edad' called for #<Persona:0x02d94a28 @edad=64> (NoMethodError)

el error es porque ni Pedro ni Juan tienen acceso a su método edad


Privado: Sólo es accesible por el propio objeto, es decir por self, no se puede acceder al método mismo pero si a un método que llame a las variables que interese saber.

class Persona

def initialize(edad, altura)                       
@edad = edad
@altura = altura
end

def edad
@edad
end

def altura                               #este es un método público
@altura
end

def variables
#este método variables llama a las variables edad y altura
puts "Este es el valor de la variable edad = #{edad} y altura = #{altura}"

end
private :edad                                      # edad es el método privado
end
Pedro = Persona.new(33, 180)
Juan = Persona.new(64,258)

puts Pedro.variables
puts Juan.variables
puts Juan.altura
puts Juan.edad

Que da la salida:

Este es el valor de la variable edad = 33 y altura = 180

Este es el valor de la variable edad = 64 y altura = 258

258
prueba1.rb:28:in `<main>': private method `edad' called for #<Persona:0x02e745d0 @edad=64, @altura=258> (NoMethodError)


Si sustituirmos la última línea de código por:
Puts Juan.send :edad                                       #send es un método que permite acceder siempre
nos imprimirá el valor de ese método 64 que antes nos daba error.

24 ene. 2020

Las Clases y los Objetos creados por ellas PARTE 2

Parte 1 -> https://ruby.obdnet.com/2020/01/las-clases-y-los-objetos-creados-por.html

Refrencia:
https://carlossanchezperez.wordpress.com/2013/05/19/mi-guia-de-ruby-las-clases-y-curiosidades-oop/
https://medium.com/@gsanchezd/entendiendo-los-objetos-en-ruby-18bcce934d66


De la PARTE 1, nos queda claro que una clase es un objeto que hace objetos, que dentro de la clase se definen las características o propiedades que llevarán las instancias, es decir, todos los objetos que de ella salgan, esto se hace a través de métodos de objetos que manejan las variables de instancia, y que se pueden acceder a ellas, para lectura, escritura o para ambas, mediante métodos de objetos específicos, que apuntan a la misma variable solo que un método la invoca y el otro la escribe, esto se utiliza cuando se inizializan las variables de instancia, pero de no necesitarlo mediante la sentencia attr_ que puede ser attr_accessor que permite ambas cosas, attr_writer sólo para escritura y attr_reader para lectura solamente.

Una de las características interesantes de las clases es la Herencia:

Heredar significa que desde una clase se pueden invocar los parámetros de otra, recibiendo de ésta sus variables de clase, incluso pudiendo modificar a éstas.

class Animal

@@animal = "Ser vivo"               #Variable de clase
@ser_vivo = "soy un ser vivo"     #Variable de instancia

def self.pruebolaclase                    #Método de clase
puts "Estoy en self.pruebolaclase"
puts @@animal
puts @ser_vivo
puts "Salgo de self"
end

def pruebolaclase

puts "Estoy en el método de instancia"
puts @@animal
puts @ser_vivo
puts "Salgo del método de instancia"
end

end

Animal.pruebolaclase                    #
Animal.new.pruebolaclase

Esto arroja la salida:
Estoy en self.pruebolaclase
Ser vivo
soy un ser vivo
Salgo de self
Estoy en el método de instancia
Ser vivo

Salgo del método de instancia


Si creamos una clase Perros:

class Perros < Animal

def tipo
@tipo = "Cánidos"
end

end

Perros.pruebolaclase

Arroja la salida:

Estoy en self.pruebolaclase
Ser vivo

Salgo de self

y si pedimos puts Perros.new.tipo

Nos da la salida:
Estoy en self.pruebolaclase
Ser vivo

Salgo de self
Cánidos

Como vemos, Perros hereda la variable de la clase Animales pero sólo puede ver su propia variable de instancia.

Si desde Perros, variamos la variable de clase @@animal por @@animal = "Soy un animal" lo hereda tanto Perros como Animal.

class Animal

@@animal = "Ser vivo"               #Variable de clase
@ser_vivo = "soy un ser vivo"     #Variable de instancia

def self.pruebolaclase                    #Método de clase

puts "Estoy en self.pruebolaclase"
puts @@animal
puts @ser_vivo
puts "Salgo de self"

end

def pruebolaclase
puts "Estoy en el método de instancia"
puts @@animal
puts @ser_vivo
puts "Salgo del método de instancia"

end
end

class Perros < Animal
@@animal = "Soy un animal"
def tipo
@tipo = "Cánidos"
end
end

Animal.pruebolaclase
Perros.pruebolaclase
puts Perros.new.tipo

arroja la salida:
Estoy en self.pruebolaclase
Soy un animal
soy un ser vivo
Salgo de self
Estoy en self.pruebolaclase
Soy un animal

Salgo de self
Cánidos

Ya hemos visto que las clases tienen métodos propios y se invocan anteponiendo la instrucción self al método, pero ésto es sólo un método de clase, pues, en realidad, se pueden invocar declarando que ese método es de esa clase, en vez de poner self.nombremetodo se pondría nombreclase.nombremetodo.

Definimos una clase que calcule el área de un rectángulo y de el resultado en cm:

class Area
def Area.rectangulo(base, altura, unidades="cm")
area=base*altura
puts "El área del rectángulo es #{area} " + unidades
end
end

Area.rectangulo(3,5)

da la salida:
El área del rectángulo es 15 cm

y el programa:
class Area
def self.rectangulo(base, altura, unidades="cm")
area=base*altura
puts "El área del rectángulo es #{area} " + unidades
end
end

Area.rectangulo(3,5)

también da la salida:
El área del rectángulo es 15 cm

Cuando tenemos una clase y alguna subclase de ella, quizá nos interese nos interese poner el mismo método en ambas, solo que con contenidos distintos, así que cuando invoquemos el método de la subclase, podemos decirle que invoque al método con el mismo nombre de la clase madre y se complete con los parámetros de ambos. Esto se hace con el procedimiento super dentro de un método.

Veamos este ejemplo:

class Persona

def comer
puts 'comiendo'
end

end

class Trabajador < Persona
def comer
super
puts 'muy rápido'
end
end

Pedro = Trabajador.new

puts Pedro.comer

Que da la salida:

comiendo
muy rápido

Como se ve, super hace que acuda primero al método comer de Persona, coloca lo que esté y después sigue con el método comer de Trabajador.

Ahora bien, puede ser que una instancia concreta tenga que poseer un método concreto, y sólo ella, pues no se va a repetir, a esto se le llama método singleton, lo cual le otorga a esa instancia concreta los métodos propios de su clase y este nuevo,

Un ejemplo, imaginemos que en la clase Perro, todos labran pero habrá uno en concreto que hablará.

class Perro

def initialize(nombre)
@nombre = nombre
end

def ladrar
puts "Guau Guau"
end

end

perro1 = Perro.new('Chachi')
perro2 = Perro.new('Chulo')

def perro2.hablar          #este es el método singleton
puts 'Yo si que se hablar'
end

perro1.ladrar
perro2.ladrar

perro2.hablar
perro1.hablar

y da la salida:
Guau Guau
Guau Guau
Yo si que se hablar
prueba1.rb:26:in `<main>': undefined method `hablar' for #<Perro:0x02d64a40 @nombre="Chachi"> (NoMethodError)

El perro1 no puede acceder al método singleton de de perro2.

A su vez, también hay clase singleton, que se ejecuta sólo una vez en una sola instancia.

En este ejemplo:
class Perro

def initialize(nombre)
@nombre = nombre
end

def ladrar
puts "Guau Guau"
end

end

perro1 = Perro.new('Chachi')     #tiene que estar instanciado antes de la clase singleton
perro2 = Perro.new('Chulo')

class << perro1                           #estos << indica que es clase singleton
def hablar
return "Yo soy el que sabe hablar"
end

end

puts perro1.hablar

da la salida:
Yo soy el que sabe hablar

Esto quiere decir que al objeto perro1 se le agrega el método singleton hablar
https://www.youtube.com/watch?v=TcLYT7qCqwM&list=PLEFC2D43C36013A70&index=29



23 ene. 2020

Las Clases y los Objetos creados por ellas PARTE 1

Referencias:
--------------------------------------------------------------------------------------------------------------------
https://carlossanchezperez.wordpress.com/2013/05/19/mi-guia-de-ruby-las-clases-y-curiosidades-oop/
https://medium.com/@gsanchezd/entendiendo-los-objetos-en-ruby-18bcce934d66
https://ru.coursera.org/lecture/aplicaciones-web/video-3-programacion-rubi-i-objetos-clases-y-herencias-7E4Wa
https://www.youtube.com/watch?v=ArnePVp68F0
---------------------------------------------------------------------------------------------------------------------
Los Objetos, tanto en Ruby como en la vida real, están definidos por sus cualidades y variables, que le han sido otorgados desde algún proceso o mecanismo que los generó.

En Ruby, este proceso o mecanismo es la Clase.

Una Clase es una manera de hacer objetos, en ella se definen, por un lado las cualidades de ella misma (variables de clase) necesarias para llegar a hacer las cualidades que van a tener los objetos que de ella salgan (variables de objeto).

Las variables en Ruby pueden ser:

nombre = variable local
@nombre = variable de instancia
@@nombre = variable de clase
$Nombre =variable universal

A.- Los Objetos que salen de las clases:

Todos los objetos que salgan de esa clase tendrán las mismas característica básicas, éstas características son; las variables, los métodos (que son las acciones que pueden hacer) y los procedimientos.

En Ruby a los objetos se les llaman también Instancias y las variables que se definan dentro de la clase se conocen como variables de instancia si están referidas a ella, inicialmente apuntan a nil, se identifican porque empiezan con @ y gracias a ello pueden ser accesibles desde fuera de la clase mediante el procedimiento adecuado según interese, se definen mediante el método de instancia def... end y esto es así siempre.

Para crear un objeto de esa clase, fuera de la misma clase, hay que utilizar el método .new asociado a una variable según la sintaxis:

variable = nombredelaclase.new() --> cuando se definen constructores
ó
variable = nombredelaclase.new --> cuando no se definen constructores

Los objetos se crean con una clase y tiene que:
  • Sus variables empiezan con @
  • Los métodos que los llaman son def... end
  • Se accede a ello mediante nombredelaclase.métododeinstancia
y este objeto creado contendrá los parámetros que en la clase se le asignen.

Así que podemos ver a los objetos como una compilación de variables, métodos y procedimientos emanados desde una clase concreta, pues de esta manera tenemos la visión desde el objeto hacia la clase.

Para crear una Clase, se empieza con la siguiente sintaxis:

class (nombre de la clase con la primera en mayúscula)
.
.
.
end


Un ejemplo, vamos a crear una clase que englobe a todos los animales, que normalmente quedan definidos por un nombre, un género y una especie;

class Animal
end

En esta clase llamada Animal que no hace nada por ahora, pero podemos comprobar que es una clase, que es una clase del tipo Class y que tiene los métodos heredados de Class más los propios de la clase mediante las siguientes líneas:

puts "Está creada la clase #{Animal}"
puts "Es de la clase #{Animal.class}"
puts "Y tiene los métodos siguientes: #{Animal.methods}"

Esto nos dará como salida:

Está creada la clase Animal
Es de la clase Class
Y tiene los métodos siguientes: ........ (Pondrá varias líneas heredadas de la clase Class pues no hay ninguno definido para Animal, por ahora.)

Ahora bien, como la necesidad de crear la clase Animal es porque entre los métodos de Class no existen los de los animales, es que se necesita establecer unos criterios que serán una base común para todos los objetos que de ella salgan, pero como los valores de sus parámetros han de partir de los mismos criterios, habrá de inicializar las variables de instancia cada vez que se quiera crear un objeto para que no interfiera en los demás, y esto se consigue mediante algún procedimiento que lo consiga.

El procedimiento es variado; con los métodos con los accesores.

1.-  Métodos: por un lado podemos ponerlo explícito, con constructor def initialize()... end

class Animal
def initialize (nombre, genero, especie)
@nombre = nombre
@genero = genero
@especie = especie
end
end

animal = Animal.new('Perro', 'Cánido', 'Canis Lupus Familiaris')
puts animal
animal = Animal.new('Gato', 'Felino', 'Felis Silvestris Catus')
puts animal

La salida de esto da:

<Animal:0x0142e57c>
<Animal:0x0142e464>


En este caso, lo que hace es utilizar la orden constructora initialize(nombre, genero, especie) para decir que lo que esté dentro de ese def serán los valores que van a tener las variables de cada instancia que se creen y son obligatorios en la creación del objeto, de lo contrario dará un error por faltar datos para contruir el objeto, por ejemplo

@nombre = nombre
@genero = genero
@especie = especie

Con esto le decimos que la variable nombre, genero y especie se identificarán (porque serán asignadas) con las variables de instancia @nombre, @genero y @especie respectivamente.

La salida que nos da este código, son dos objetos distintos de la clase Animal con un número alfanumérico identificativo distinto 0x0142e57c y 0142e464, respectivamente.

Al ejecutar este código en otro momento nos dará otros números, como es lógico pensar, pero no queda duda de que hace objetos distintos.

Ahora, ¿qué parámetros, características o peculiaridades tienen esos objetos?

Para acceder a esa respuesta habrá que proceder de tal manera que podamos acudir a ellos según nos interese, bien para simplemente poder leerlos, para modificarlos sobrescribiéndolos o para ambas cosas.

Hay varios procedimientos

Como vemos,  a través de un método: podemos acceder a ellos en un método definido para esa clase,
de esta manera los podremos llamar desde cualquier momento del código, fuera de la clase, que nos interese de la siguiente manera, por ejemplo:

puts animal.nombre ó puts animal.genero ó puts animal.especie

El código quedaría así:

class Animal
def initialize (nombre, genero, especie)
@nombre = nombre
@genero = genero
@especie = especie
end

def nombre
@nombre
end

def genero
@genero
end

def especie
@especie
end

end

animal = Animal.new('Perro', 'Cánido', 'Canis Lupus Familiaris')
puts animal
puts animal.nombre
puts animal.genero
puts animal.especie

animal = Animal.new('Gato', 'Felino', 'Felis Silvestris Catus')
puts animal
puts animal.nombre
puts animal.genero
puts animal.especie

Al ejecutar este código nos dará la siguiente salida:

<Animal:0x0142e57c>
Perro
Cánido
Canis Lupus Familiaris
<Animal:0x0142e464>
Gato
Felino
Felis Silvestris Catus

Como se ve, esto es una manera de acceder a estos parámetros de cada objeto y vemos que tienen la misma estructura.

A este procedimiento se le conoce como método getter, que viene a decir... coger el dato, tan sólo lo lee y te lo muestra al llamar al método.

Si quisiéramos modificar el dato, entonces tendríamos que llamar a la variable que lo identifica con un método setter, que viene a significar... pon esto...

Imaginemos que queremos preguntar al usuario cada dato, entonces este se metería como variable de instancia concreto del objeto.

Vamos a crear el objeto animal pidiendo los datos nombre, género y especie.

class Animal

def initialize (nombre, genero, especie)
@nombre = nombre
@genero = genero
@especie = especie
end

def nombre
@nombre
end

def nombre=(value)
@nombre=value
end

def genero
@genero
end

def genero=(value)
@genero=value
end

def especie
@especie
end

def especie=(value)
@especie=value
end

end

puts "¿Qué animal es?"
STDOUT.flush
n=gets.chomp

puts "¿Qué género es?"
STDOUT.flush
g=gets.chomp

puts "¿Qué especie es?"
STDOUT.flush
e=gets.chomp

animal = Animal.new(n, g, e)
puts "El animal es #{animal.nombre} del género #{animal.genero} y de la especie #{animal.especie}"

Lo cual nos preguntará :
nombre: Perro
género: Cánido
Especie: Lupus

y contestará con la frase:
"El animal es Perro del género Cánido y de la especie Lupus"

2.- Con los accesores: Hay otra forma de acceder a las variables es con los accesores a través de un método concreto denominado attr_ que pueden ser de tres tipos, el attr_accessor, el attr_writer y el attr_reader.

attr_accessor: esta es una instrucción que permite leer y modificar la variable de instancia y simplifica mucho ya que sólo tenemos que hacer la sintaxis:

attr_xxxxx :nombre de la variable1, :nombre de la variable2 ...

en el caso de atr_accessor sustituiría a los dos métodos tanto el setter como el getter, es decir...

def especie #Este es el getter
@especie
end

def especie=(value) #Este es el setter
@especie=value
end


por

attr_accessor :nombre, :genero, :especie

de manera que el código haría lo mismo y quedaría como sigue:
class Animal

def initialize (nombre, genero, especie)
@nombre = nombre
@genero = genero
@especie = especie
end

attr_accessor :nombre, :genero, :especie

end

puts "¿Qué animal es?"
STDOUT.flush
n=gets.chomp

puts "¿Qué género es?"
STDOUT.flush
g=gets.chomp

puts "¿Qué especie es?"
STDOUT.flush
e=gets.chomp

animal = Animal.new(n, g, e)
puts "El animal es #{animal.nombre} del género #{animal.genero} y de la especie #{animal.especie}"

Con attr_writer sólo será accesible a escribir la variable pero no para ser leída.
Con attr_reader sólo estará accesible a ser leída pero no podrá ser modificada.

El método 1 se usa cuando hay constructor con el fin de dejar bien atados los valores de las variables de instancia, en el caso de que no sean variables fijas, puede simplificarse con los accesores.

****************

2.- Las clases  mismas:

Todo lo escrito hasta aquí es válido para la clase misma, que es en sí mismo un objeto desde el que salen otros objetos según sean definidos en ella.

La clase misma también es un objeto, y se aplican las mismas premisas que para los objetos pero con las salvedades que:

  • Sus variables empiezan con @@
  • Los métodos que los llaman son self.def... end
  • Se accede a ello mediante nombredelaclase.métododelaclase
Pero las variables de instancia pueden ponerse en la clase o en los métodos de los objetos, en ambos casos hacen caso a alcances distintos, de manera que una misma variable de instancia se puede poner para la clase y no a los objetos que se creen.

En el caso de los animales, podemos decir que por defecto sean de color rojo, pero sin es perro que sea negro para ese nombre de animal.

Veamos el código:

class Animal
@color="Rojo verdoso"          # variable de clase
def self.color_animal
puts @color
end
def color_animal
puts @color
end
end

Animal.color_animal              # accede a la clase
Animal.new.color_animal       # accede al objeto

Al ejecutar este código sólo dará la salida "Rojo verdoso" pues la variable de instancia está en el nivel de la clase y no del objeto y se accede con self.

Sin embargo, si tendrá valores distintos si se le asigna un valor distinto dentro de un método de objeto con el mismo nombre que la instancia de la clase:

class Animal
@color="Rojo verdoso"          # variable de clase
def self.color_animal
puts @color
end
def color_animal
@color="Rojo azulado"
puts @color
end
end

Animal.color_animal              # accede a la clase
Animal.new.color_animal       # accede al objeto

La salida que da es:
Rojo verdoso
Rojo azulado

Ahora bien, la variable de clase es accesible tanto desde la clase misma como desde los métodos de instancia:

class Animal
@@color="Rojo verdoso"          # variable de clase
def self.color_animal                  # método de clase por llevar self
puts @@color
end
def color_animal                         #método de instancia por no llevar self
puts @@color
end
end

Animal.color_animal              # accede a la clase
Animal.new.color_animal       # accede al objeto

Este código da dos líneas con el contenido "Rojo verdoso" aunque se acceda desde la instancia.

Vallamos con más cuestiones de las clases en la PARTE 2