Orientação a objetos em Ruby, o básico

ruby_oop

Acreditava já ter escrito isto mas procurei e descobri justamente o contrário. Então, vamos lá. Diferente da versão 5 do Perl e do PHP a linguagem Ruby já foi criada com o paradigma da orientação a objetos implementado o que afeta diretamente a estrutura da linguagem, o modo como trabalhamos com variáveis etc.

E assim, tal qual fiz com Perl (versão 5, ainda não brinquei com rakudo) e PHP, o mínimo necessário para se trabalhar com classes de objetos em Ruby é mais ou menos isto…

Criando uma classe

Você pode definir a classe dentro do mesmo arquivo do resto do programa ou em um arquivo separado (o que é recomendável por conta da praticidade). A sintaxe é assim:

class «Nome da classe»
    ...
end

Pegando o mesmo exemplo usado nos guias anteriores (fica bom para ajudar na comparação) criarei a classe Nome¹:

class Nome
    
end

E por pura praticidade ficará em um arquivo separado que chamarei de “Nome.rb”.

(¹) Em Ruby as classes são definidas como se fossem constantes, constantes são criadas colocando a primeira letra do nome em caixa alta; logo não ele resultaria em um erro caso a chamasse “nome”.

Construtor

O construtor das classe se chama #initialize:

class «Nome da classe»
    ...
    def initialize(«parâmetros»)
        ...
    end
end

No exemplo da classe Nome ele ficaria assim:

class Nome
    attr_accessor :nome, :sobrenome
    def initialize(nome,sobrenome)
        @nome=nome
        @sobrenome=sobrenome
    end
end

A confusão foi proposital, as variáveis nome e sobrenome são locais dentro do escopo de #initialize — sim, poderia ter usado outros nomes — e não devem ser confundidos com @nome e @sobrenome que são os atributos da classe Nome e com escopo global dentro dela.

Até poderia ter grafado como self.nome e self.sobrenome, respectivamente, para ficar fácil mas a sintaxe usando o @ é a mais utilizada.

Quanto a cláusula  attr_accessor, um jeito simples de explicá-la é dizer que torna os atributos assinalados, no caso nome e sobrenome, acessíveis para leitura e escrita do lado de fora da classe. Abaixo há uma explicação mais detalhada sobre este assunto mas é importante dizer que isto não relação alguma com métodos ou atributos públicos e privados.

Criando outros métodos

Os demais métodos da classe são:

class Nome
    ...
    def escreve_nome
        @nome
    end

    def escreve_nome_completo
        @nome+" "+@sobrenome
    end
end

Métodos e funções em Ruby não precisam necessariamente terminar com o comando return para indicar o valor a ser passado e a linguagem sempre enviará o resultado do último comando executado em caso de omissão.

Usando a Interactive Ruby Shell — ou irb — é possível demonstrar esta característica:

$ irb
irb(main):001:0> def soma1(a,b)
irb(main):002:1>   return a+b
irb(main):003:1> end
=> :soma1
irb(main):004:0> soma1(1,2)
=> 3
irb(main):005:0> def soma2(a,b)
irb(main):006:1>   a+b
irb(main):007:1> end
=> :soma2
irb(main):008:0> soma2(1,2)
=> 3

Mas muita atenção! Se de um lado torna o código mais limpo, por outro te exige um pouco mais de atenção ao escrever e (ao você) interpretar código.

É recomendação que os objetos² em Ruby tenham implementados alguns métodos específicos, um deles é #to_s cuja função é retornar o conteúdo/conteúdos do objeto em forma de string,  ou seja, ele serve para criar uma forma de “escrever” seu objeto da forma que você acha a melhor.

Especificamente neste caso o método #escreve_nome pode ser renomeado para #to_s.

    ...
    def to_s
        @nome
    end
    ...

A vantagem, além de se estar seguindo o padrão, é também poder usar print «objeto» e saber que automaticamente ele chamará o método #to_s correspondente para exibir o conteúdo mais legível ao ao invés de algo como “<Object:0x00000000903e18>.

(²) Em Ruby as variáveis são também objetos, não se esqueça. 🙂

Testando

Escrevi algo bastante simples, apenas para testar a classe, o arquivo “nome.rb”:

#!/usr/bin/env ruby

require './Nome.rb'

# define o novo objeto
giovanni=Nome.new('Giovanni','Nunes')

# usará automaticamente o .to_s
puts "O primeiro nome é %s." % giovanni

# usará explicitamente um dos métodos
puts "E %s o nome completo." % giovanni.escreve_nome_completo

exit

O resultado será algo mais ou menos assim:

$ ./nome.rb 
O primeiro nome é Giovanni.
E Giovanni Nunes o nome completo.

Sim, é um teste simples.

Herança

Para herdar os métodos e atributos de uma classe já existente você apenas assinala com o sinal de < (o sinal de menor quê) juntamente com o nome da classe pai.

class Supernome < Nome

    # método que escreve o nome completo de outra forma
    def escreve_nome_completo
        @sobrenome.upcase+", "+@nome
    end

end

Neste caso estou aproveitando toda a estrutura existente da classe Nome e mudando apenas um dos métodos para retornar um valor de um jeito diferente. Ah sim, o método #upcase serve para colocar o conteúdo da string em caixa alta.

Acesso aos atributos

Explicando um pouco melhor o uso da cláusula attr_accessor. Digamos que você tem uma classe qualquer, a classe C, e que precise que o atributo qualquer dela, digamos valor.

A forma certa de fazê-lo seria criando um método próprio para isto dentro da classe:

class C
    ...
    def valor
        @valor
    end
    ...

Então é possível fazer o=C.new() e  depois o.valor para recuperar seu conteúdo. E seguindo na mesma lógica também é necessário um método que permita fazer a alteração deste valor:

    ...
    def valor=(numero)
        @valor=numero
    end
    ...
end

E então usar o.valor=10 para inserir/modificar seu conteúdo. Bom, ao menos é assim que deveria ser feito mas não foi assim que fiz, correto?

Sendo uma tarefa bastante repetitiva é possível usar algumas cláusulas na definição da classe para simplificar o trabalho — acho que neste ponto do texto é seguro dizer que em Ruby há diversos recursos disponíveis para que você escreva menos.

Usando attr_reader você indica quais atributos terão métodos de leitura dentro da classe, com attr_writer você assinala em quais haverá método de escrita e, para não precisar usar os dois, com attr_accessor você indica quais terão ambos os métodos.

Finalizando

E para encerrar fica a mesma sugestão do exemplo em PHP, isto é, construir os métodos que alteram nome e também sobrenome em Ruby — retire o attr_accessor que senão fica muito fácil — e para não ser repetitivo há uma publicação anterior sobre o method_missing() que vale a pena a leitura.

Anúncios

2 comentários sobre “Orientação a objetos em Ruby, o básico

  1. Pingback: Orientação a objetos em Ruby, o básico - Linux em Ação XYZ

  2. Pingback: Orientação a objetos em Python, o básico | giovannireisnunes

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s