37 - Objetos: variables de clase

Hemos visto como definimos atributos en una clase anteponiendo el caracter @:

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

Los atributos son independientes por cada objeto o instancia de la clase (también se las llama variables de instancia), es decir si definimos tres objetos de la clase Persona, todas las personas tienen un atributo @nombre pero cada uno tiene un valor independiente:

class Persona
  
  attr_accessor :nombre

  def initialize(nombre)
    @nombre = nombre
  end

end

# bloque principal

persona1 = Persona.new "Juan"
persona2 = Persona.new "Ana"
persona3 = Persona.new "Luis"

puts persona1.nombre # Juan
puts persona2.nombre # Ana
puts persona3.nombre # Luis

En algunas situaciones necesitamos almacenar datos que sean compartidos por todos los objetos de dicha clase, en esas situaciones debemos emplear variables de clase.

Para definir una variable de clase lo hacemos dentro de la clase pero fuera de sus métodos antecediendo a la variable los caracteres @@:

Programa: ejercicio150.rb

class Persona
  @@variable = 20

  attr_accessor :nombre

  def initialize(nombre)
    @nombre = nombre
  end
  
  def variable
    @@variable
  end
 
  def variable=(valor)
    @@variable = valor
  end
end

# bloque principal

persona1 = Persona.new "Juan"
persona2 = Persona.new "Ana"
persona3 = Persona.new "Luis"

puts persona1.nombre # Juan
puts persona2.nombre # Ana
puts persona3.nombre # Luis
puts persona1.variable # 20
persona2.variable = 50
puts persona1.variable # 50

Se reserva solo un espacio para la variable @@variable, independientemente que se definan muchos objetos de la clase Persona. La variable @@variable es compartida por todos los objetos persona1, persona2 y persona3.

Si mostramos el contenido de @@variable a partir del objeto persona1:

puts persona1.variable # 20

Luego modificamos la variable de clase @@variable a partir del objeto persona2:

persona2.variable = 50

Si mostramos ahora el contenido de la variable de la clase accediendo a partir de cualquiera de los tres objetos el valor es el mismo:

puts persona1.variable # 50

Problema 1:

Definir una clase Cliente que almacene un código de cliente y un nombre.
En la clase Cliente definir una variable de clase de tipo Array que almacene todos los clientes que tienen suspendidas sus cuentas corrientes.
Imprimir por pantalla todos los datos de clientes y el estado que se encuentra su cuenta corriente.

Programa: ejercicio151.rb

class Cliente
  @@suspendidos = []

  def initialize(codigo, nombre)
    @codigo = codigo
    @nombre = nombre
  end

  def imprimir
    puts "Codigo: #{@codigo}"
    puts "Nombre: #{@nombre}"
    esta_suspendido
  end

  def esta_suspendido
    if @@suspendidos.include? @codigo
      puts "Esta suspendido"
    else
      puts "No esta suspendido"
    end
    puts "*" * 50
  end

  def suspender
    @@suspendidos << @codigo
  end

end

# bloque principal

cliente1 = Cliente.new 1, "Juan"
cliente2 = Cliente.new 2, "Ana"
cliente3 = Cliente.new 3, "Diego"
cliente4 = Cliente.new 4, "Pedro"

cliente3.suspender
cliente4.suspender

cliente1.imprimir
cliente2.imprimir
cliente3.imprimir
cliente4.imprimir

La clase Cliente define una variable de clase llamada @@suspendidos que es de tipo Array y por ser variable de clase es compartida por todos los objetos que definamos de dicha clase:

class Cliente
  @@suspendidos = []

En el método imprimir mostramos el código, nombre del cliente y si se encuentra suspendida su cuenta corriente:

  def imprimir
    puts "Codigo: #{@codigo}"
    puts "Nombre: #{@nombre}"
    esta_suspendido
  end

El método suspender lo que hace es agregar el código de dicho cliente al Array de clientes suspendidos:

  def suspender
    @@suspendidos << @codigo
  end

El método que analiza si está suspendido el cliente verifica si su codigo se encuentra almacenado en la variable de clase suspendidos:

  def esta_suspendido
    if @@suspendidos.include? @codigo
      puts "Esta suspendido"
    else
      puts "No esta suspendido"
    end
    puts "*" * 50
  end

Para probar esta clase en el bloque principal creamos cuatro objetos de la clase Cliente:

# bloque principal

cliente1 = Cliente.new 1, "Juan"
cliente2 = Cliente.new 2, "Ana"
cliente3 = Cliente.new 3, "Diego"
cliente4 = Cliente.new 4, "Pedro"

Suspendemos dos clientes:

cliente3.suspender
cliente4.suspender

Y luego imprimimos los datos de cada cliente:

cliente1.imprimir
cliente2.imprimir
cliente3.imprimir
cliente4.imprimir

Es importante remarcar que todos los objetos acceden a una único Array llamado @@suspendidos gracias a que se definió como variable de clase.

Problema propuesto

  • Plantear una clase llamada Jugador.
    Definir en la clase Jugador los atributos nombre y puntaje, y los métodos initialize, imprimir y pasar_tiempo (que debe reducir en uno la variable de clase).
    Declarar dentro de la clase Jugador una variable de clase que indique cuantos minutos falta para el fin de juego (iniciarla con el valor 30)
    Definir en el bloque principal dos objetos de la clase Jugador.
    Reducir dicha variable hasta llegar a cero.
Solución
ejercicio152.rb

class Jugador
  @@tiempo = 30

  def initialize(nombre, puntaje)
    @nombre = nombre
    @puntaje = puntaje
  end

  def imprimir
    puts "Nombre: #{@nombre}"
    puts "Puntaje: #{@puntaje}"
    puts "Fin de juego en #{@@tiempo} minutos"
  end

  def pasar_minuto
    @@tiempo = @@tiempo - 1
  end
 
  def tiempo
    @@tiempo
  end
  
end

# bloque principal

jugador1 = Jugador.new "Juan", 100
jugador2 = Jugador.new "Ana", 50
while jugador1.tiempo > 0
  jugador1.imprimir
  jugador2.imprimir
  jugador1.pasar_minuto
end