Cap. 2 - Classes e Objetos

Anterior Índice Próximo

Classes e Objetos

Até o momento nós temos usado objetos padrão do Ruby como números e strings, exceto um tira-gosto que foi experimentado na seção anterior. Vamos agora ver como criar novos tipos de objetos. Como na maioria das outras linguagens POO, um objeto Ruby é definido por uma classe. A classe é como um "modelo" a partir do qual objetos individuais são construídos. Esta é uma classe bem simples:

class MinhaClass

end

E assim é como eu criaria um objeto utilizável a partir dela:

obj = MinhaClasse.new

Note que eu não posso fazer muita coisa com o meu objeto - pela simples razão que eu não programei nada na minha classe MinhaClasse, da qual o objeto foi criado.

Na verdade, se você criar uma classe vazia como MinhaClasse, os objetos criados a partir dela não são totalmente inúteis. Isso se deve pelo fato já explicado anteriormente que todas as classes Ruby automaticamente herdam as características da classe Object. Logo o objeto obj criado pode fazer uso dos métodos de Object como no exemplo abaixo:

classe.rb

class MinhaClasse

end

obj = MinhaClasse.new

puts obj.class

Saída:

MinhaClasse

Para tornar MinhaClasse um pouco mais útil, é preciso dar-­lhe alguns métodos. Neste exemplo (o qual foi mencionado brevemente no capítulo anterior), eu adicionei um método chamado diga_algo:

class MinhaClasse
       def diga_algo
              puts( "Olá Mundo" )
       end
end

Agora, quando eu crio um objeto da classe MinhaClasse, eu posso chamar este método para que o objeto diga "Olá Mundo":

objeto.rb


class MinhaClasse

       def diga_algo
               puts( "Olá Mundo" )
       end
end

obj = MinhaClass.new

obj.diga_algo

Saída:

Olá Mundo

Instâncias e Variáveis de Instância

Vamos criar mais alguns objetos úteis. Nenhuma casa deveria ficar sem um cachorro :). Então vamos criar este cachorro:

class Cachorro
        def set_nome( nome )
               @meu_nome = nome
        end
end

Note que a definição da classe começa com a palavra- ­chave class (toda em minúsculas) e é seguida do nome da classe propriamente dito, o qual deve iniciar com uma letra maiúscula. Minha classe Cachorro contém um único método, set_nome. Este recebe um argumento de entrada, nome. O corpo do método atribue o valor de nome para uma variável chamada @meu_nome.

Variáveis iniciadas com o caracter @ são 'variáveis de instância' que significa que elas se ligam a objetos individuais - ou 'instâncias' da classe.

Não é necessário declarar previamente estas variáveis.

Eu posso criar instâncias da classe Cachorro ( isto é, 'objetos cachorro' ) chamando o método new. Aqui eu estou criando dois objetos cachorro (lembre que o nome da classe inicia com letra maiúscula e nomes de objetos iniciam com letra minúscula):

meu_cachorro = Cachorro.new

seu_cachorro = Cachorro.new

Neste momento, estes dois cachorros estão sem nome. Então a próxima coisa a fazer é chamar o método set_nome para dar nomes a eles:
meu_cachorro.set_nome( 'Mel' )

seu_cachorro.set_nome( 'Break' )

Tendo dado nomes aos cachorros, eu preciso de alguma forma para achar os nomes mais tarde.

Cada cachorro precisa saber o seu próprio nome, então vamos criar um método get_nome:

def get_nome
  return @meu_nome
end

A palavra-­chave return é opcional. Métodos Ruby sempre retornam a última expressão avaliada.

Para maior clareza ( e também para evitar resultados inesperados de métodos mais complexos que este!) devemos adquirir o hábito de retornar explicitamente o valor para uso posterior. Finalmente, vamos dar algum comportamento ao cachorro pedindo para ele falar, isto é, latir. Aqui está a definição final da classe:

class Cachorro
  def set_nome( nome )
    @meu_nome = nome
  end

  def get_nome
    return @meu_nome
  end

  def fale
    return 'woof!'
  end
end

Agora, nós podemos criar um cachorro, dar-­lhe um nome, mostrar o nome e pedir para ele "falar":

meu_cachorro = Cachorro.new

meu_cachorro.set_nome( 'Mel' )

puts(meu_cachorro.get_nome)

puts(meu_cachorro.fale)

Para maior clareza - e para não dizerem que somos contra a nossos amigos felinos, vamos adicionar uma classe Gato no nosso programa, cachorro_e_gato.rb. A classe Gato é semelhante à classe Cachorro, exceto pelo fato que o método latir, naturalmente, retorna um "miau" em vez de "woof".

O programa abaixo contém um erro. O objeto de nome outro_cachorro nunca tem um valor atribuido para a sua variável @nome. Por sorte, o Ruby não explode quando nós tentamos mostrar o nome do cachorro. Em vez disso o Ruby, simplesmente, imprime 'nil'.

Nós veremos em breve uma forma simples de assegurar que erros como esse não ocorram novamente…

cachorro_e_gato.rb


# Criacao de classes, metodos e variaveis.

class Cachorro

       def set_nome( nome )
              @meu_nome = nome
       end

       def get_nome
               return @meu_nome
       end

       def fale
              return 'woof!'
       end

end

class Gato

       def set_nome( nome )
              @meu_nome = nome
       end

       def get_nome
               return @meu_nome
        end

        def fale
                  return 'miau!'
         end

end

# Criando instancias (objetos) das classes Cachorro e Gato

meu_cachorro = Cachorro.new
seu_cachorro = Cachorro.new
meu_gato = Gato.new
seu_gato = Gato.new
outro_cachorro = Cachorro.new

#Nomeando-os

meu_cachorro.set_nome( 'Mel' )
seu_dog.set_nome( 'Break' )
meu_gato.set_name( 'Fifi' )
seu_gato.set_name( 'Garfield' )

# Pegando os nomes deles e mostrando
# Classe Cachorro

puts(meu_cachorro.get_nome)
puts(seu_cachorro.get_nome)

#O que acontece se um cachorro nao tiver nenhum nome?

puts(outro_cachorro.get_nome)

# Classe Gato
puts(meu_gato.get_nome)
puts(seu_gato.get_nome)

# Olha a Bicharada (derivado de bichos :)). 

puts(meu_cachorro.fale)
puts(seu_cachorro.fale)
puts(meu_gato.fale)
puts(seu_gato.fale)

Saída:

Mel
Break
nil
Fifi
Garfield
woof!
woof!
miau!
miau!

Construtores - new e initialize

Agora, vamos dá uma olhada num outro exemplo de uma classe definida pelo usuário.

tesouro.rb

# definindo uma classe e criando alguns objetos 

class Coisa

   def set_nome( nome )
      @nome = nome
   end

   def get_nome
      return @nome
   end

end

class Tesouro

   def initialize( nome, descricao )
      @nome         = nome
      @descricao  = descricao
   end
      
   def to_s # sobrescrevendo o método padrão to_s 
      "O(a) #{@nome} é um tesouro que é #{@descricao}\n"
   end

end

coisa1 = Coisa.new

coisa1.set_nome( "Uma coisa Amável" )

puts coisa1.get_nome

t1 = Tesouro.new("Espada de Arwen", "uma arma élfica.")

t2 = Tesouro.new("O Anel", " um anel para todos governar um anel para encontrá-los .")

puts t1.to_s

puts t2.to_s

# O metodo inspect fara você ver dentro de um objeto
puts "Inspecionando  o primeiro tesouro ( t1 ): #{t1.inspect}"

print("t.inspect: ")
puts(t.inspect)

Este é um jogo de estratégia em construção, vocês podem ter percebido que não pude esconder a minha fascinação por Senhor dos Anéis ;). Contém duas classes, Coisa e Tesouro. A classe Coisa é bem similar à classe Cachorro vista anteriormente, exceto pelo fato de que ela não late ( 'woof' ) ;). Já a classe Tesouro tem alguns elementos extras interessantes. Primeiramente, ela não tem os métodos get_nome e set_nome. No lugar, ela contém um método chamado initialize que recebe dois argumentos cujos valores são atribuidos para as variáveis @nome e @descrição:

def initialize( nome, descricao )
  @nome              = nome
  @descricao       = descricao
end

Quando uma classe contém um método de nome initialize, este método é automaticamente chamado quando um objeto é criado usando o método new. É uma boa idéia usar um método initialize para definir os valores das variáveis de instância de um objeto, como se faz normalmente nas linguagens de programação orientadas a objeto. Qual o benefício disso? Imagine que você define todos os valores das variáveis do objeto com set_nome e este objeto possui 30 variáveis, vai ser no mínimo chato ter que inicializar todas estas variáveis com o método set_nome, não? ;). Um outro ponto é que ao instanciar um objeto você sempre inicializará as variáveis do mesmo, evitando que o objeto possua variáveis com valor nulo como mostrado no programa cachorro_e_gato.rb. E além de tudo isso é mais bonito dessa forma :).

Depois, foi criado um método chamado to_s o qual serve para retornar uma string representativa de um objeto da classe Tesouro. O nome do método, to_s, não é arbitrário. O mesmo nome de método é muito utilizado extensivamente pela hierarquia de classes padrão do Ruby. De fato, o método to_s é definido para a própria classe Object a qual é a última ancestral de todas as outras classes em Ruby. O método to_s foi redefinido, adicionando um novo comportamento que é mais apropriado para a classe Tesouro. Assim podemos dizer que o método to_s foi sobrescrito.

O método new cria um objeto, logo você pode vê-lo como um construtor de objetos. Contudo, você não deveria normalmente implementar a sua própria versão do método new (isto é possível mas não é, geralmente, recomendado). Em vez disso, quando você quer executar quaisquer ações de configuração - como definir valores iniciais para variáveis internas de um objeto - você deveria fazer isso em um método chamado initialize. O Ruby executa o método initialize imediatamente após um novo obejto ser criado.

Inspecionando Objetos

Se olharmos com cuidado o programa, poderá notar que também foi tirada uma radioagrafia do objeto Tesouro, t1, usando o método inspect:

t1.inspect

Este método inspect é definido para todos os objetos Ruby. Ele retorna uma string contendo uma representação do objeto legível por nós humanos. No caso em questão, a saída da chamada deste método é:

   #<Tesouro:0xa7c98018 @descricao="uma arma élfica.", @nome="Espada de Arwen">

Primeiro temos o nome da classe Tesouro, seguido de um número, o qual pode variar - este é o código de identificação interna de um objeto particular do Ruby, em seguida vem os nomes e valores das variáveis do objeto.

O Ruby também fornece o método p que é um atalho para inspecionar e mostrar objetos, então se fizermos:

p( t1 )

Teremos a mesma saída indicada anteriormente:

   #<Tesouro:0xa7c98018 @descricao="uma arma élfica.", @nome="Espada de Arwen">

Para ver como to_s pode ser usado com uma variedade de objetos e para testar como um objeto Tesouro seria convertido para uma string na ausência de uma sobrescrição do método to_s, teste o programa to_s.rb.

to_s.rb

# Mostra a representação de string de vários objetos usando o método to_s
class Tesouro
      def initialize( nome, descricao )
        @nome         = nome
        @descricao  = descricao
      end

# Desta vez não sobrescreveremos o método __to_s__
# Então o objeto Tesouro utilizará o método __to_s__ default
end

t = Tesouro.new("Espada de Arwen", "uma arma élfica.")
print("Class.to_s: ")
puts(Class.to_s)
print("Object.to_s: ")
puts(Object.to_s)
print("String.to_s: ")
puts(String.to_s)
print("100.to_s: ")
puts(100.to_s)
print("Tesouro.to_s: ")
puts(Tesouro.to_s)
print("t.to_s: ")
puts(t.to_s) 
print("t.inspect: ")
puts(t.inspect)

Como você verá, classes como Class, Object, String e Tesouro, simplesmente retornam seus nomes 
quando o método to_s é chamado. Um objeto como o objeto Tesouro, t, retorna seu identificador #<Treasure:0xb7d0d3a8> que é o mesmo identificador retornado pelo método inspect.

Olhando para o programa tesouro.rb verificamos que o código é um pouco repetitivo. Além do 
que, por que ter uma classe Coisa que contém um nome (nome) e uma classe *Tesouro* que também 
contém um nome (a variável de instância @nome), cada qual codificada independentemente? Faria 
mais sentido considerar *Tesouro* com um "tipo de *Coisa*". Se formos desenvolver este programa dentro de um completo jogo de estratégia, outros objetos como Locais, Armas podem ser outros "tipos de *Coisa*". É chegada a hora de começar a trabalhar em uma hierarquia de classes apropriada, para as coisas ficarem mais bonitinhas ;).

Mas isso são cenas do próximo capítulo!!!

<center>
[[TutorialRubyCap1][Anterior]]  [[TutorialRubyIndice][Índice]]  [[TutorialRubyCap3][Próximo]]
</center>

-- Main.LeandroNunes - 18 Oct 2006
   * [[%ATTACHURL%/capitulo_2.tar.gz][capitulo_2.tar.gz]]: capitulo_2.tar.gz
Topic revision: r1 - 05 Jul 2008, UnknownUser
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki-Colivre? Send feedback