Para criar uma classe, basicamente fazemos assim:
class Telefone
end
PS: As classes em Ruby nunca são fecchadas, mesmo as padrões, e podem ser adicionadas novos atributos e métodos, mas não é recomendado fazer isso.
Para criar um objeto dessa classe, basta isso:
tel = Telefone.new # Pode ser com ou sem parênteses
Podemos adicionar atributos e métodos na classe, assim:
class Telefone
attr_writer :numero, :operadora
def dados
puts "O número é #{@numero} e a operadora é a #{@operadora}."
end
end
O @
é como o "this" de outras linguagens, ou seja, auto-referência ao objeto criado. O attr_writer
permite a gente escrever no atributo.
PS: O arroba só funciona em atributos. Para métodos use self.
.
Dessa forma, podemos fazer as modificações no objeto assim:
tel = Telefone.new
tel.numero = "123-4567"
tel.operadora = "Vivo"
tel.dados
Quando criamos uma classe, temos mais trabalho para fazê-la, mas depois que ela está pronta, podemos criar quantos objetos precisarmos vindos dessa mesma classe, e o programa principal fica mais simples, menor e mais natural. Os objetos criados dessa mesma classe são independentes entre si e o status de um não interfere no outro. Essa é uma das vantagens da orientação a objetos.
PS: Caso precise declarar um objeto como nulo, use nil
. E para pegar o nome da classe, use self.class
.
Para marcar atributos como privados, fazemos assim, usando o modificador private
:
class Telefone
attr_writer :numero, :operadora
private :numero, :operadora
def dados
puts "O número é #{@numero} e a operadora é a #{@operadora}."
end
end
Dessa forma, só poderemos mexer nele através de métodos getters e setters. Os métodos getters são definidos colocando nos atributos attr_reader
, os setters com attr_writer
e ambos com attr_accessor
, por exemplo:
class Telefone
attr_accessor :numero, :operadora
def dados
puts "O número é #{@numero} e a operadora é a #{@operadora}."
end
end
E chamamos normalmente assim:
tel = Telefone.new
tel.numero = "123-4567"
tel.operadora = "Vivo"
tel.dados
Para explicitar o encapsulamento, podemos colocar os atributos e métodos como public
(padrão), protected
ou private
, assim:
class Telefone
attr_accessor :numero, :operadora
private :numero, :operadora
public
def dados
puts "O número é #{@numero} e a operadora é a #{@operadora}."
end
end
Para definirmos um construtor, usamos o método initialize, assim:
class Telefone
attr_accessor :numero, :operadora
private :numero, :operadora
public
def dados
puts "O número é #{@numero} e a operadora é a #{@operadora}."
end
def initialize(numero, operadora)
@numero = numero
@operadora = operadora
end
end
E na criação do objeto:
tel = Telefone.new("123-4567", "Vivo")
tel.dados
Os atributos estáticos são definidos com dois arrobas e um getter e setter com o mesmo nome. Os métodos estáticos são os que obrigatoriamente terem self.
na frente. Veja um exemplo:
class Numeros
attr_accessor :num
@@num = 50
def self.apresentacao
puts "O número é #{@@num}."
end
def self.num
@@num
end
def self.num=(n)
@@num = n
end
end
E para exibir:
Numeros.num = 100
Numeros.apresentacao
PS: Métodos estáticos só podem trabalhar outros métodos e atributos quando estes também forem estáticos, e não podem ser sobrepostos. E atributos estáticos é recomendável eles serem inicializados.
Vamos considerar a seguinte classe:
class Veiculo
attr_accessor :modelo, :potencia
private :modelo, :potencia
def apresentacao
puts "O #{self.class.to_s.downcase} é do modelo #{@modelo}, potência #{@potencia}."
end
def initialize(modelo, potencia)
@modelo = modelo
@potencia = potencia
end
end
Para fazer uma herança, basta colocar o operador <
seguido do nome da classe Pai. Caso tenha sobreposição de construtor ou outro método, use o super
. Veja um exemplo:
class Carro < Veiculo
def initialize(modelo, potencia)
super(modelo, potencia)
end
end
Instanciação da classe:
fusca = Carro.new("Fusca", 1.6)
fusca.apresentacao
A mesma classe Veiculo pode gerar outras classes, como por exemplo Moto e Onibus.
PS: O Ruby não suporta herança múltipla, mas podemos simular usando um include, dessa forma:
class Carro < Veiculo
include Transporte
end
Podemos simular classes e métodos abstratos, assim:
class Energia
def ligar
raise NotImplementedError, "Implemente o método \"ligar\"!"
end
def desligar
raise NotImplementedError, "Implemente o método \"desligar\"!"
end
end
E as classes herdeiras:
class Lampada < Energia
# Métodos sobrepostos
def ligar
puts "A Lâmpada está acesa!"
end
def desligar
puts "A Lâmpada apagou!"
end
end
A outra classe:
class Televisao < Energia
# Métodos sobrepostos
def ligar
puts "A TV está ligada!"
end
def desligar
puts "A TV desligou!"
end
end
A instanciação:
lamp = Lampada.new
lamp.ligar
lamp.desligar
tv = Televisao.new
tv.ligar
tv.desligar
No entanto, o melhor é simular uma interface com a gem, assim:
require "interface"
Energia = interface {
required_methods :ligar, :desligar
}
E as classes implementadoras:
class Lampada
def ligar
puts "A Lâmpada está acesa!"
end
def desligar
puts "A Lâmpada apagou!"
end
implements Energia
end
A outra classe:
class Televisao
def ligar
puts "A TV está ligada!"
end
def desligar
puts "A TV desligou!"
end
implements Energia
end
PS: Qualquer método pode ser sobreposto, caso chame ele na classe filha, use o método super.nomeDoMetodo()
para isso. As classes com métodos abstratos e interfaces não podem ser instanciadas.
Vamos supor essa classe:
class Pilha
attr_accessor :marca, :carga
public
def initialize(marca)
@marca = marca
@carga = 100
end
public
def apresentacao
puts "A marca da pilha é #{@marca}."
puts "A carga da pilha é #{@carga}%."
end
end
Nós podemos fazer relacionamentos entre classes diferentes, veja por exemplo a classe abaixo, que tem um atributo do "tipo" da classe acima:
class Aparelho
attr_accessor :pl
public
def initialize(pl)
@pl = pl
end
public
def ligado
if @pl.carga > 0 then # Getter do objeto Pilha
puts "O aparelho está ligado e a carga da pilha é de #{@pl.carga)}%!"
else
puts "A pilha do aparelho está sem carga!"
end
end
end
Aí podemos chamar os objetos assim:
ray = Pilha.new("Rayovac")
ray.apresentacao
controle = Aparelho.new(ray)
controle.ligado
puts "A carga da pilha é de #{controle.pl.carga}%!"